diff --git a/docs/config/README.md b/docs/config/README.md index 9b297323..b9a76609 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -91,6 +91,7 @@ prompt_order = [ "kubernetes", "directory", "git_branch", + "git_commit", "git_state", "git_status", "hg_branch", @@ -438,6 +439,38 @@ truncation_length = 4 truncation_symbol = "" ``` + +## Git Commit + +The `git_commit` module shows the active branch of the repo in your current directory. + +::: tip + +This module is disabled by default. +To enable it, set `disabled` to `false` in your configuration file. + +::: + +### Options + +| Variable | Default | Description | +| ------------ | ----------| -------------------------------------------------| +| `commit_hash_length` | `7` | The length of the displayed git commit hash. | +| `style` | `"green"` | The style for the module. | +| `prefix` | `(` | Prefix to display immediately before git commit. | +| `suffix` | `)` | Suffix to display immediately after git commit. | +| `disabled` | `true` | Disables the `git_commit` module. | + +### Example + +```toml +# ~/.config/starship.toml + +[git_commit] +disabled = false +commit_hash_length = 4 +``` + ## Git State The `git_state` module will show in directories which are part of a git diff --git a/src/config.rs b/src/config.rs index 027ca75a..c2c8c0a4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -98,6 +98,22 @@ impl<'a> ModuleConfig<'a> for f64 { } } +impl<'a> ModuleConfig<'a> for usize { + fn from_config(config: &Value) -> Option { + match config { + Value::Integer(value) => { + if *value > 0 { + Some(*value as usize) + } else { + None + } + } + Value::String(value) => value.parse::().ok(), + _ => None, + } + } +} + impl<'a, T> ModuleConfig<'a> for Vec where T: ModuleConfig<'a>, diff --git a/src/configs/git_commit.rs b/src/configs/git_commit.rs new file mode 100644 index 00000000..d6f65fdc --- /dev/null +++ b/src/configs/git_commit.rs @@ -0,0 +1,28 @@ +use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig}; + +use ansi_term::{Color, Style}; +use starship_module_config_derive::ModuleConfig; + +#[derive(Clone, ModuleConfig)] +pub struct GitCommitConfig<'a> { + pub commit_hash_length: usize, + pub hash: SegmentConfig<'a>, + pub prefix: &'a str, + pub suffix: &'a str, + pub style: Style, + pub disabled: bool, +} + +impl<'a> RootModuleConfig<'a> for GitCommitConfig<'a> { + fn new() -> Self { + GitCommitConfig { + // be consistent with git by default, which has DEFAULT_ABBREV set to 7 + commit_hash_length: 7, + hash: SegmentConfig::default(), + prefix: "(", + suffix: ") ", + style: Color::Green.bold(), + disabled: true, + } + } +} diff --git a/src/configs/mod.rs b/src/configs/mod.rs index 55330789..c840f3bb 100644 --- a/src/configs/mod.rs +++ b/src/configs/mod.rs @@ -7,6 +7,7 @@ pub mod directory; pub mod dotnet; pub mod env_var; pub mod git_branch; +pub mod git_commit; pub mod git_state; pub mod git_status; pub mod go; diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs index eb0523a2..293dcb17 100644 --- a/src/configs/starship_root.rs +++ b/src/configs/starship_root.rs @@ -22,6 +22,7 @@ impl<'a> RootModuleConfig<'a> for StarshipRootConfig<'a> { "kubernetes", "directory", "git_branch", + "git_commit", "git_state", "git_status", "hg_branch", diff --git a/src/module.rs b/src/module.rs index 591934f2..71b25627 100644 --- a/src/module.rs +++ b/src/module.rs @@ -18,6 +18,7 @@ pub const ALL_MODULES: &[&str] = &[ "dotnet", "env_var", "git_branch", + "git_commit", "git_state", "git_status", "golang", diff --git a/src/modules/git_commit.rs b/src/modules/git_commit.rs new file mode 100644 index 00000000..cacfa398 --- /dev/null +++ b/src/modules/git_commit.rs @@ -0,0 +1,54 @@ +use super::{Context, Module, RootModuleConfig}; +use git2::Repository; + +use crate::configs::git_commit::GitCommitConfig; + +/// Creates a module with the Git commit in the current directory +/// +/// Will display the commit hash if the current directory is a git repo +pub fn module<'a>(context: &'a Context) -> Option> { + let mut module = context.new_module("git_commit"); + let config = GitCommitConfig::try_load(module.config); + if config.disabled { + return None; + }; + + module + .get_prefix() + .set_value(config.prefix) + .set_style(config.style); + module + .get_suffix() + .set_value(config.suffix) + .set_style(config.style); + module.set_style(config.style); + + let repo = context.get_repo().ok()?; + let repo_root = repo.root.as_ref()?; + let git_repo = Repository::open(repo_root).ok()?; + + let git_head = git_repo.head().ok()?; + let head_commit = git_head.peel_to_commit().ok()?; + let commit_oid = head_commit.id(); + module.create_segment( + "hash", + &config.hash.with_value(&id_to_hex_abbrev( + commit_oid.as_bytes(), + config.commit_hash_length, + )), + ); + + Some(module) +} + +/// len specifies length of hex encoded string +pub fn id_to_hex_abbrev(bytes: &[u8], len: usize) -> String { + bytes + .iter() + .map(|b| format!("{:02x}", b)) + .collect::>() + .join("") + .chars() + .take(len) + .collect() +} diff --git a/src/modules/mod.rs b/src/modules/mod.rs index f69c9465..42951324 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -7,6 +7,7 @@ mod directory; mod dotnet; mod env_var; mod git_branch; +mod git_commit; mod git_state; mod git_status; mod golang; @@ -49,6 +50,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option> { "dotnet" => dotnet::module(context), "env_var" => env_var::module(context), "git_branch" => git_branch::module(context), + "git_commit" => git_commit::module(context), "git_state" => git_state::module(context), "git_status" => git_status::module(context), "golang" => golang::module(context), diff --git a/tests/testsuite/git_commit.rs b/tests/testsuite/git_commit.rs new file mode 100644 index 00000000..c7dd50bb --- /dev/null +++ b/tests/testsuite/git_commit.rs @@ -0,0 +1,68 @@ +use ansi_term::Color; +use std::process::Command; +use std::{io, str}; + +use crate::common::{self, TestCommand}; + +#[test] +fn test_render_commit_hash() -> io::Result<()> { + let repo_dir = common::create_fixture_repo()?; + + let mut git_output = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .current_dir(repo_dir.as_path()) + .output()? + .stdout; + git_output.truncate(7); + let expected_hash = str::from_utf8(&git_output).unwrap(); + + let output = common::render_module("git_commit") + .use_config(toml::toml! { + [git_commit] + disabled = false + }) + .arg("--path") + .arg(repo_dir) + .output()?; + + let actual = String::from_utf8(output.stdout).unwrap(); + let expected = Color::Green + .bold() + .paint(format!("({}) ", expected_hash)) + .to_string(); + + assert_eq!(expected, actual); + Ok(()) +} + +#[test] +fn test_render_commit_hash_len_override() -> io::Result<()> { + let repo_dir = common::create_fixture_repo()?; + + let mut git_output = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .current_dir(repo_dir.as_path()) + .output()? + .stdout; + git_output.truncate(14); + let expected_hash = str::from_utf8(&git_output).unwrap(); + + let output = common::render_module("git_commit") + .use_config(toml::toml! { + [git_commit] + disabled = false + commit_hash_length = 14 + }) + .arg("--path") + .arg(repo_dir) + .output()?; + + let actual = String::from_utf8(output.stdout).unwrap(); + let expected = Color::Green + .bold() + .paint(format!("({}) ", expected_hash)) + .to_string(); + + assert_eq!(expected, actual); + Ok(()) +} diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index c3e20926..214f8b01 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -8,6 +8,7 @@ mod directory; mod dotnet; mod env_var; mod git_branch; +mod git_commit; mod git_state; mod git_status; mod golang;