refactor: Refactor git state module to use module config (#605)

This commit is contained in:
Zhenhui Xie 2019-11-06 21:00:31 +08:00 committed by Matan Kushner
parent 48726fdd2a
commit a3d5ea3e43
3 changed files with 107 additions and 79 deletions

35
src/configs/git_state.rs Normal file
View File

@ -0,0 +1,35 @@
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
use ansi_term::{Color, Style};
use starship_module_config_derive::ModuleConfig;
#[derive(Clone, ModuleConfig)]
pub struct GitStateConfig<'a> {
pub rebase: SegmentConfig<'a>,
pub merge: SegmentConfig<'a>,
pub revert: SegmentConfig<'a>,
pub cherry_pick: SegmentConfig<'a>,
pub bisect: SegmentConfig<'a>,
pub am: SegmentConfig<'a>,
pub am_or_rebase: SegmentConfig<'a>,
pub progress_divider: SegmentConfig<'a>,
pub style: Style,
pub disabled: bool,
}
impl<'a> RootModuleConfig<'a> for GitStateConfig<'a> {
fn new() -> Self {
GitStateConfig {
rebase: SegmentConfig::new("REBASING"),
merge: SegmentConfig::new("MERGING"),
revert: SegmentConfig::new("REVERTING"),
cherry_pick: SegmentConfig::new("CHERRY-PICKING"),
bisect: SegmentConfig::new("BISECTING"),
am: SegmentConfig::new("AM"),
am_or_rebase: SegmentConfig::new("AM/REBASE"),
progress_divider: SegmentConfig::new("/"),
style: Color::Yellow.bold(),
disabled: false,
}
}
}

View File

@ -7,6 +7,7 @@ pub mod directory;
pub mod dotnet; pub mod dotnet;
pub mod env_var; pub mod env_var;
pub mod git_branch; pub mod git_branch;
pub mod git_state;
pub mod git_status; pub mod git_status;
pub mod go; pub mod go;
pub mod hostname; pub mod hostname;

View File

@ -1,8 +1,8 @@
use ansi_term::Color;
use git2::RepositoryState; use git2::RepositoryState;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use super::{Context, Module}; use super::{Context, Module, RootModuleConfig, SegmentConfig};
use crate::configs::git_state::GitStateConfig;
/// Creates a module with the state of the git repository at the current directory /// Creates a module with the state of the git repository at the current directory
/// ///
@ -10,97 +10,83 @@ use super::{Context, Module};
/// If the progress information is available (e.g. rebasing 3/10), it will show that too. /// If the progress information is available (e.g. rebasing 3/10), it will show that too.
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("git_state"); let mut module = context.new_module("git_state");
let config: GitStateConfig = GitStateConfig::try_load(module.config);
module.set_style(config.style);
module.get_prefix().set_value("(");
module.get_suffix().set_value(") ");
let repo = context.get_repo().ok()?; let repo = context.get_repo().ok()?;
let repo_root = repo.root.as_ref()?; let repo_root = repo.root.as_ref()?;
let repo_state = repo.state?; let repo_state = repo.state?;
let state_description = get_state_description(repo_state, repo_root);
if let StateDescription::Clean = state_description { let state_description = get_state_description(repo_state, repo_root, config);
return None;
}
let module_style = module let label = match &state_description {
.config_value_style("style")
.unwrap_or_else(|| Color::Yellow.bold());
module.set_style(module_style);
module.get_prefix().set_value("(");
module.get_suffix().set_value(") ");
let label = match state_description {
StateDescription::Label(label) => label, StateDescription::Label(label) => label,
StateDescription::LabelAndProgress(label, _) => label, StateDescription::LabelAndProgress(label, _) => label,
// Should only be possible if you've added a new variant to StateDescription StateDescription::Clean => {
_ => panic!("Expected to have a label at this point in the control flow."), return None;
}
}; };
module.new_segment(label.segment_name, label.message_default); module.create_segment(label.name, &label.segment);
if let StateDescription::LabelAndProgress(_, progress) = state_description { if let StateDescription::LabelAndProgress(_, progress) = &state_description {
module.new_segment("progress_current", &format!(" {}", progress.current)); module.create_segment(
module.new_segment("progress_divider", "/"); "progress_current",
module.new_segment("progress_total", &format!("{}", progress.total)); &SegmentConfig::new(&format!(" {}", progress.current)),
);
module.create_segment("progress_divider", &SegmentConfig::new("/"));
module.create_segment(
"progress_total",
&SegmentConfig::new(&format!("{}", progress.total)),
);
} }
Some(module) Some(module)
} }
static MERGE_LABEL: StateLabel = StateLabel {
segment_name: "merge",
message_default: "MERGING",
};
static REVERT_LABEL: StateLabel = StateLabel {
segment_name: "revert",
message_default: "REVERTING",
};
static CHERRY_LABEL: StateLabel = StateLabel {
segment_name: "cherry_pick",
message_default: "CHERRY-PICKING",
};
static BISECT_LABEL: StateLabel = StateLabel {
segment_name: "bisect",
message_default: "BISECTING",
};
static AM_LABEL: StateLabel = StateLabel {
segment_name: "am",
message_default: "AM",
};
static REBASE_LABEL: StateLabel = StateLabel {
segment_name: "rebase",
message_default: "REBASING",
};
static AM_OR_REBASE_LABEL: StateLabel = StateLabel {
segment_name: "am_or_rebase",
message_default: "AM/REBASE",
};
/// Returns the state of the current repository /// Returns the state of the current repository
/// ///
/// During a git operation it will show: REBASING, BISECTING, MERGING, etc. /// During a git operation it will show: REBASING, BISECTING, MERGING, etc.
fn get_state_description(state: RepositoryState, root: &PathBuf) -> StateDescription { fn get_state_description<'a>(
state: RepositoryState,
root: &'a std::path::PathBuf,
config: GitStateConfig<'a>,
) -> StateDescription<'a> {
match state { match state {
RepositoryState::Clean => StateDescription::Clean, RepositoryState::Clean => StateDescription::Clean,
RepositoryState::Merge => StateDescription::Label(&MERGE_LABEL), RepositoryState::Merge => StateDescription::Label(StateLabel::new("merge", config.merge)),
RepositoryState::Revert => StateDescription::Label(&REVERT_LABEL), RepositoryState::Revert => {
RepositoryState::RevertSequence => StateDescription::Label(&REVERT_LABEL), StateDescription::Label(StateLabel::new("revert", config.revert))
RepositoryState::CherryPick => StateDescription::Label(&CHERRY_LABEL), }
RepositoryState::CherryPickSequence => StateDescription::Label(&CHERRY_LABEL), RepositoryState::RevertSequence => {
RepositoryState::Bisect => StateDescription::Label(&BISECT_LABEL), StateDescription::Label(StateLabel::new("revert", config.revert))
RepositoryState::ApplyMailbox => StateDescription::Label(&AM_LABEL), }
RepositoryState::ApplyMailboxOrRebase => StateDescription::Label(&AM_OR_REBASE_LABEL), RepositoryState::CherryPick => {
RepositoryState::Rebase => describe_rebase(root), StateDescription::Label(StateLabel::new("cherry_pick", config.cherry_pick))
RepositoryState::RebaseInteractive => describe_rebase(root), }
RepositoryState::RebaseMerge => describe_rebase(root), RepositoryState::CherryPickSequence => {
StateDescription::Label(StateLabel::new("cherry_pick", config.cherry_pick))
}
RepositoryState::Bisect => {
StateDescription::Label(StateLabel::new("bisect", config.bisect))
}
RepositoryState::ApplyMailbox => StateDescription::Label(StateLabel::new("am", config.am)),
RepositoryState::ApplyMailboxOrRebase => {
StateDescription::Label(StateLabel::new("am_or_rebase", config.am_or_rebase))
}
RepositoryState::Rebase => describe_rebase(root, config.rebase),
RepositoryState::RebaseInteractive => describe_rebase(root, config.rebase),
RepositoryState::RebaseMerge => describe_rebase(root, config.rebase),
} }
} }
fn describe_rebase(root: &PathBuf) -> StateDescription { fn describe_rebase<'a>(
root: &'a PathBuf,
rebase_config: SegmentConfig<'a>,
) -> StateDescription<'a> {
/* /*
* Sadly, libgit2 seems to have some issues with reading the state of * Sadly, libgit2 seems to have some issues with reading the state of
* interactive rebases. So, instead, we'll poke a few of the .git files * interactive rebases. So, instead, we'll poke a few of the .git files
@ -109,8 +95,6 @@ fn describe_rebase(root: &PathBuf) -> StateDescription {
* The following is based heavily on: https://github.com/magicmonty/bash-git-prompt * The following is based heavily on: https://github.com/magicmonty/bash-git-prompt
*/ */
let just_label = StateDescription::Label(&REBASE_LABEL);
let dot_git = root.join(".git"); let dot_git = root.join(".git");
let has_path = |relative_path: &str| { let has_path = |relative_path: &str| {
@ -140,23 +124,31 @@ fn describe_rebase(root: &PathBuf) -> StateDescription {
}; };
match progress { match progress {
None => just_label, None => StateDescription::Label(StateLabel::new("rebase", rebase_config)),
Some(progress) => StateDescription::LabelAndProgress(&REBASE_LABEL, progress), Some(progress) => {
StateDescription::LabelAndProgress(StateLabel::new("rebase", rebase_config), progress)
}
} }
} }
enum StateDescription { enum StateDescription<'a> {
Clean, Clean,
Label(&'static StateLabel), Label(StateLabel<'a>),
LabelAndProgress(&'static StateLabel, StateProgress), LabelAndProgress(StateLabel<'a>, StateProgress),
} }
struct StateLabel { struct StateLabel<'a> {
segment_name: &'static str, name: &'static str,
message_default: &'static str, segment: SegmentConfig<'a>,
} }
struct StateProgress { struct StateProgress {
current: usize, current: usize,
total: usize, total: usize,
} }
impl<'a> StateLabel<'a> {
fn new(name: &'static str, segment: SegmentConfig<'a>) -> Self {
Self { name, segment }
}
}