From b1133963bdf19f895645f59b1075837ef175cb15 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Sat, 20 Sep 2025 05:33:25 +0100 Subject: [PATCH 01/11] Use theme styles for git signs (#3404) --- src/printer.rs | 124 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 3 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index a28cb1905f..a56ddf6e20 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,4 +1,6 @@ +use std::str::FromStr; use std::vec::Vec; +use syntect::highlighting::ScopeSelectors; use nu_ansi_term::Color::{Fixed, Green, Red, Yellow}; use nu_ansi_term::Style; @@ -892,14 +894,130 @@ impl Colors { ..Style::default() }; + // Get scope style from theme using given `scope_name` or use `default_style`. + let get_scope_style = |scope_name: &str, default_style: Style| { + let scope_selectors = ScopeSelectors::from_str(scope_name).unwrap(); + + theme + .scopes + .iter() + .find(|item| item.scope.eq(&scope_selectors)) + .and_then(|item| item.style.foreground) + .map(|c| Style { + foreground: to_ansi_color(c, true_color), + ..Style::default() + }) + .unwrap_or(default_style) + }; + Colors { grid: gutter_style, rule: gutter_style, header_value: Style::new().bold(), - git_added: Green.normal(), - git_removed: Red.normal(), - git_modified: Yellow.normal(), + git_added: get_scope_style("markup.inserted", Green.normal()), + git_removed: get_scope_style("markup.deleted", Red.normal()), + git_modified: get_scope_style("markup.changed", Yellow.normal()), line_number: gutter_style, } } } + +#[cfg(test)] +mod tests { + use super::*; + use syntect::highlighting::{Color, StyleModifier, Theme, ThemeItem}; + + fn make_theme_with_scope(scope_name: &str, color: Color) -> Theme { + let mut theme = Theme::default(); + + let style = StyleModifier { + foreground: Some(color), + ..StyleModifier::default() + }; + + theme.scopes.push(ThemeItem { + scope: ScopeSelectors::from_str(scope_name).unwrap(), + style, + }); + + theme + } + + #[test] + fn git_added_default_style() { + let theme = Theme::default(); + let colors = Colors::colored(&theme, true); + + assert_eq!( + colors.git_added.foreground, + Some(nu_ansi_term::Color::Green) + ) + } + + #[test] + fn git_added_theme_style() { + // #afdc7c + let r = 175; + let g = 220; + let b = 124; + let theme = make_theme_with_scope("markup.inserted", Color { r, g, b, a: 255 }); + let colors = Colors::colored(&theme, true); + + assert_eq!( + colors.git_added.foreground, + Some(nu_ansi_term::Color::Rgb(r, g, b)) + ) + } + + #[test] + fn git_deleted_default_style() { + let theme = Theme::default(); + let colors = Colors::colored(&theme, true); + + assert_eq!( + colors.git_removed.foreground, + Some(nu_ansi_term::Color::Red) + ) + } + + #[test] + fn git_deleted_theme_style() { + // #f36072 + let r = 243; + let g = 96; + let b = 114; + let theme = make_theme_with_scope("markup.deleted", Color { r, g, b, a: 255 }); + let colors = Colors::colored(&theme, true); + + assert_eq!( + colors.git_removed.foreground, + Some(nu_ansi_term::Color::Rgb(r, g, b)) + ) + } + + #[test] + fn git_modified_default_style() { + let theme = Theme::default(); + let colors = Colors::colored(&theme, true); + + assert_eq!( + colors.git_modified.foreground, + Some(nu_ansi_term::Color::Yellow) + ) + } + + #[test] + fn git_modified_theme_style() { + // #72a2f8 + let r = 114; + let g = 162; + let b = 248; + let theme = make_theme_with_scope("markup.changed", Color { r, g, b, a: 255 }); + let colors = Colors::colored(&theme, true); + + assert_eq!( + colors.git_modified.foreground, + Some(nu_ansi_term::Color::Rgb(r, g, b)) + ) + } +} From 02dbe8762973496ce8e24d1924a0a4af7cbaba8f Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 04:12:24 +0100 Subject: [PATCH 02/11] Add `--gitsigns` option (#3404) --- doc/long-help.txt | 10 ++ doc/short-help.txt | 2 + src/bin/bat/app.rs | 15 +++ src/bin/bat/clap_app.rs | 15 +++ src/bin/bat/config.rs | 1 + src/bin/bat/main.rs | 1 + src/config.rs | 6 + src/decorations.rs | 20 ++- src/gitsigns.rs | 75 +++++++++++ src/lib.rs | 2 + src/printer.rs | 5 +- tests/.DS_Store | Bin 0 -> 6148 bytes tests/gitsigns/after/test.txt | 8 ++ tests/gitsigns/bat.conf | 2 + tests/gitsigns/before/test.txt | 8 ++ tests/gitsigns/output/classic | 12 ++ tests/gitsigns/output/custom | 12 ++ tests/gitsigns/output/default | 12 ++ tests/gitsigns/output/modern | 12 ++ tests/integration_tests.rs | 240 +++++++++++++++++++++++++++++++++ 20 files changed, 450 insertions(+), 8 deletions(-) create mode 100644 src/gitsigns.rs create mode 100644 tests/.DS_Store create mode 100644 tests/gitsigns/after/test.txt create mode 100644 tests/gitsigns/bat.conf create mode 100644 tests/gitsigns/before/test.txt create mode 100644 tests/gitsigns/output/classic create mode 100644 tests/gitsigns/output/custom create mode 100644 tests/gitsigns/output/default create mode 100644 tests/gitsigns/output/modern diff --git a/doc/long-help.txt b/doc/long-help.txt index ac5ded9e0d..1082256870 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -57,6 +57,16 @@ Options: --diff-context Include N lines of context around added/removed/modified lines when using '--diff'. + --gitsigns + Set git `added`, `modified`, `removed-above`, `removed-below` signs. (default: classic) + To set a default gitsigns, add the '--gitsigns="..."' option to the configuration file + or export the BAT_GITSIGNS environment variable (e.g.: export BAT_GITSIGNS="..."). + + Possible values: + * classic: preset `~,+,‾,_` + * modern: preset `▎,▎,‾,_` + * 4 comma-separated signs, e.g. `~,+,-,-` + --tabs Set the tab width to T spaces. Use a width of 0 to pass tabs through directly diff --git a/doc/short-help.txt b/doc/short-help.txt index d67a51d03c..daf48aabf7 100644 --- a/doc/short-help.txt +++ b/doc/short-help.txt @@ -23,6 +23,8 @@ Options: Specify the name to display for a file. -d, --diff Only show lines that have been added/removed/modified. + --gitsigns + Set git `added`, `modified`, `removed-above`, `removed-below` signs. (default: classic) --tabs Set the tab width to T spaces. --wrap diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index b3d48cba0f..7514b47732 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -22,6 +22,7 @@ use bat::{ bat_warning, config::{Config, VisibleLines}, error::*, + gitsigns::Gitsigns, input::Input, line_range::{HighlightedLineRanges, LineRange, LineRanges}, style::{StyleComponent, StyleComponents}, @@ -335,6 +336,20 @@ impl App { } else { None }, + #[cfg(feature = "git")] + gitsigns: match self + .matches + .get_one::("gitsigns") + .map(|s| s.as_str()) + { + Some("classic") => Gitsigns::classic(), + Some("modern") => Gitsigns::modern(), + Some(s) => Gitsigns::parse(s).unwrap_or_else(|e| { + eprintln!("Error parsing `--gitsigns={s}` \n{e}"); + std::process::exit(1); + }), + None => Gitsigns::default(), + }, }) } diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index f7e86dbcab..fb69a75507 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -185,6 +185,21 @@ pub fn build_app(interactive_output: bool) -> Command { "Include N lines of context around added/removed/modified lines when using '--diff'.", ), ) + .arg( + Arg::new("gitsigns") + .long("gitsigns") + .overrides_with("gitsigns") + .help("Set git `added`, `modified`, `removed-above`, `removed-below` signs. (default: classic)") + .long_help( + "Set git `added`, `modified`, `removed-above`, `removed-below` signs. (default: classic)\n\ + To set a default gitsigns, add the '--gitsigns=\"...\"' option to the configuration file\n\ + or export the BAT_GITSIGNS environment variable (e.g.: export BAT_GITSIGNS=\"...\").\n\n\ + Possible values:\n \ + * classic: preset `~,+,‾,_`\n \ + * modern: preset `▎,▎,‾,_`\n \ + * 4 comma-separated signs, e.g. `~,+,-,-`", + ), + ) } app = app.arg( diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index 7972d808e7..272e9e029b 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -145,6 +145,7 @@ pub fn get_args_from_env_vars() -> Vec { ("--pager", "BAT_PAGER"), ("--paging", "BAT_PAGING"), ("--style", "BAT_STYLE"), + ("--gitsigns", bat::gitsigns::env::BAT_GITSIGNS), ] .iter() .filter_map(|(flag, key)| { diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index d489992c5a..d4079a2d58 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -304,6 +304,7 @@ fn invoke_bugreport(app: &App, cache_dir: &Path) { "BAT_STYLE", "BAT_TABS", "BAT_THEME", + "BAT_GITSIGNS", "COLORTERM", "LANG", "LC_ALL", diff --git a/src/config.rs b/src/config.rs index 622a6ca618..3643ed8919 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "git")] +use crate::gitsigns::Gitsigns; use crate::line_range::{HighlightedLineRanges, LineRanges}; use crate::nonprintable_notation::{BinaryBehavior, NonprintableNotation}; #[cfg(feature = "paging")] @@ -107,6 +109,10 @@ pub struct Config<'a> { // Weather or not to set terminal title when using a pager pub strip_ansi: StripAnsiMode, + + /// Set git `added`, `modified`, `removed-above`, `removed-below` signs + #[cfg(feature = "git")] + pub gitsigns: Gitsigns, } #[cfg(all(feature = "minimal-application", feature = "paging"))] diff --git a/src/decorations.rs b/src/decorations.rs index 5b7846c30d..27103289da 100644 --- a/src/decorations.rs +++ b/src/decorations.rs @@ -1,6 +1,6 @@ -#[cfg(feature = "git")] -use crate::diff::LineChange; use crate::printer::{Colors, InteractivePrinter}; +#[cfg(feature = "git")] +use crate::{diff::LineChange, gitsigns::Gitsigns}; use nu_ansi_term::Style; #[derive(Debug, Clone)] @@ -88,13 +88,19 @@ impl LineChangesDecoration { } } - pub(crate) fn new(colors: &Colors) -> Self { + pub(crate) fn new(colors: &Colors, gitsigns: &Gitsigns) -> Self { LineChangesDecoration { cached_none: Self::generate_cached(Style::default(), " "), - cached_added: Self::generate_cached(colors.git_added, "+"), - cached_removed_above: Self::generate_cached(colors.git_removed, "‾"), - cached_removed_below: Self::generate_cached(colors.git_removed, "_"), - cached_modified: Self::generate_cached(colors.git_modified, "~"), + cached_added: Self::generate_cached(colors.git_added, &gitsigns.added), + cached_removed_above: Self::generate_cached( + colors.git_removed, + &gitsigns.removed_above, + ), + cached_removed_below: Self::generate_cached( + colors.git_removed, + &gitsigns.removed_below, + ), + cached_modified: Self::generate_cached(colors.git_modified, &gitsigns.modified), } } } diff --git a/src/gitsigns.rs b/src/gitsigns.rs new file mode 100644 index 0000000000..1fc74bf614 --- /dev/null +++ b/src/gitsigns.rs @@ -0,0 +1,75 @@ +pub mod env { + pub const BAT_GITSIGNS: &str = "BAT_GITSIGNS"; +} + +#[cfg(feature = "git")] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Gitsigns { + pub modified: String, + pub added: String, + pub removed_above: String, + pub removed_below: String, +} + +#[cfg(feature = "git")] +impl Default for Gitsigns { + fn default() -> Self { + Gitsigns::classic() + } +} + +#[cfg(feature = "git")] +impl Gitsigns { + pub fn classic() -> Self { + Self { + modified: "~".into(), + added: "+".into(), + removed_above: "‾".into(), + removed_below: "_".into(), + } + } + + pub fn modern() -> Self { + Self { + modified: "▎".to_string(), + added: "▎".to_string(), + removed_above: "‾".to_string(), + removed_below: "_".to_string(), + } + } + + pub fn parse(s: &str) -> Result { + let parts: Vec<&str> = s + .split(',') + .map(|p| { + // allow single space char as gitsign + if p == " " { + return p; + } + + p.trim() + }) + .collect(); + + if parts.len() != 4 { + return Err("Expected 4 comma-separated signs: `modified,added,removed-above,removed-below`, e.g. `~,+,‾,_`".into()); + } + + for (i, part) in parts.iter().enumerate() { + if part.chars().count() != 1 { + return Err(format!( + "Invalid sign at position {}: `{}`. Each sign must be a single character.", + i + 1, + part + )); + } + } + + Ok(Self { + modified: parts[0].to_string(), + added: parts[1].to_string(), + removed_above: parts[2].to_string(), + removed_below: parts[3].to_string(), + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index 4c60f10ec7..208a1c3d09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,8 @@ pub mod controller; mod decorations; mod diff; pub mod error; +#[cfg(feature = "git")] +pub mod gitsigns; pub mod input; mod less; #[cfg(feature = "lessopen")] diff --git a/src/printer.rs b/src/printer.rs index a56ddf6e20..42bd4cb034 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -232,7 +232,10 @@ impl<'a> InteractivePrinter<'a> { if config.style_components.changes() && line_changes.as_ref().is_some_and(|c| !c.is_empty()) { - decorations.push(Box::new(LineChangesDecoration::new(&colors))); + decorations.push(Box::new(LineChangesDecoration::new( + &colors, + &config.gitsigns, + ))); } } diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4876999c6b85b00a2d22770975ab72863908e98e GIT binary patch literal 6148 zcmeHKJ8Hu~5S>X>2-3JrxmU;y7Gs~l7f2B>0;512r&g77`DlLg!C0)^7}A6{F!Oe2 z=dI8yG#U}n_37hHWF;a!+)#ckv}Wh#jXh;XfpFY0$X-S{{_MWSrg}MH+#ak!zU6q! zKYWh+xObVzEES*vRDcRl0V?p20@izB{YxMt6`%rC;HrRq9}3*CCQgC==|J!i0N5ez zhPBTUz+wqtO`HOefoV{ILDd{FH0a2etgDGrV9-T#_|SZ^=7ggDblhLOT(kx PathBuf { + let tmp = env::temp_dir(); + let bat_tempdir = tmp.join("bat_test").join(namespace); + + // remove `/path_to_os_tmp_dir/bar_test` dir if it was already created in a previous test + let _ = fs::remove_dir_all(&bat_tempdir); + + // create a new empty `/path_to_os_tmp_dir/bat_test` dir + fs::create_dir_all(&bat_tempdir).unwrap(); + + bat_tempdir +} + +fn make_git_repo_with_file_changes(repo_path: &Path, file_path: &Path) { + let _ = fs::create_dir_all(repo_path); + + // 1. git init + StdCommand::new("git") + .args(["init"]) + .current_dir(repo_path) + .output() + .unwrap(); + + // 2. create test.txt + fs::write( + file_path, + fs::read_to_string("tests/gitsigns/before/test.txt").unwrap(), + ) + .unwrap(); + + // 3. git add . + StdCommand::new("git") + .args(["add", "."]) + .current_dir(repo_path) + .output() + .unwrap(); + + // 4. git commit -m "Initial commit" + StdCommand::new("git") + .args(["commit", "-m", "Initial commit"]) + .current_dir(repo_path) + .output() + .unwrap(); + + // 5. change test.txt content + fs::write( + file_path, + fs::read_to_string("tests/gitsigns/after/test.txt").unwrap(), + ) + .unwrap(); +} + +#[test] +fn gitsigns_default() { + let bat_tempdir = bat_tempdir("gitsigns_default"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/default").unwrap(); + + assert_eq!(output, expected); +} + +#[test] +fn gitsigns_classic() { + let bat_tempdir = bat_tempdir("gitsigns_classic"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=classic tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=classic", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/classic").unwrap(); + + assert_eq!(output, expected); +} + +#[test] +fn gitsigns_modern() { + let bat_tempdir = bat_tempdir("gitsigns_modern"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=modern tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=modern", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/modern").unwrap(); + + assert_eq!(output, expected); +} + +#[test] +fn gitsigns_custom() { + let bat_tempdir = bat_tempdir("gitsigns_custom"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns="M,A,‾,_" tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=M,A,‾,_", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/custom").unwrap(); + + assert_eq!(output, expected); +} + +#[test] +fn gitsigns_using_config_file() { + let bat_tempdir = bat_tempdir("gitsigns_using_config_file"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + let bat_config_path = bat_tempdir.join("bat.conf"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + fs::write( + &bat_config_path, + fs::read_to_string("tests/gitsigns/bat.conf").unwrap(), + ) + .unwrap(); + + // BAT_CONFIG_PATH=tests/gitsigns/bat.conf bat tests/gitsigns/after/test.txt + let cmd = bat_with_config() + .env("BAT_CONFIG_PATH", bat_config_path.to_str().unwrap()) + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/modern").unwrap(); + + assert_eq!(output, expected); +} + +#[test] +fn gitsigns_using_env_var() { + let bat_tempdir = bat_tempdir("gitsigns_using_env_var"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // BAT_GITSIGNS=modern bat tests/gitsigns/after/test.txt + let cmd = bat() + .env("BAT_GITSIGNS", "modern") + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/modern").unwrap(); + + assert_eq!(output, expected); +} + +#[test] +fn gitsigns_parse_error_expected_4_comma_separated_signs() { + // bat --gitsigns="1,2,3" test.txt + bat() + .args(["--gitsigns=1,2,3", "test.txt"]) + .assert() + .failure() + .code(1) + .stderr(contains("Expected 4 comma-separated signs")); +} + +#[test] +fn gitsigns_parse_error_each_sign_must_be_a_single_char() { + // bat --gitsigns="M,A,RA,RB" test.txt + bat() + .args(["--gitsigns=M,A,RA,RB", "test.txt"]) + .assert() + .failure() + .code(1) + .stderr(contains("Each sign must be a single character")); +} From 410b9be2cffc88965a9b9db945501bf29648e211 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 06:07:05 +0100 Subject: [PATCH 03/11] Fix `Gitsigns` imports --- src/bin/bat/app.rs | 4 +++- src/bin/bat/config.rs | 27 ++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index 7514b47732..139e1d8b3f 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -22,13 +22,15 @@ use bat::{ bat_warning, config::{Config, VisibleLines}, error::*, - gitsigns::Gitsigns, input::Input, line_range::{HighlightedLineRanges, LineRange, LineRanges}, style::{StyleComponent, StyleComponents}, MappingTarget, NonprintableNotation, PagingMode, SyntaxMapping, WrappingMode, }; +#[cfg(feature = "git")] +use bat::gitsigns::Gitsigns; + fn is_truecolor_terminal() -> bool { env::var("COLORTERM") .map(|colorterm| colorterm == "truecolor" || colorterm == "24bit") diff --git a/src/bin/bat/config.rs b/src/bin/bat/config.rs index 272e9e029b..4f676de1b3 100644 --- a/src/bin/bat/config.rs +++ b/src/bin/bat/config.rs @@ -137,7 +137,7 @@ fn get_args_from_str(content: &str) -> Result, shell_words::ParseE } pub fn get_args_from_env_vars() -> Vec { - [ + let mut args = vec![ ("--tabs", "BAT_TABS"), ("--theme", bat::theme::env::BAT_THEME), ("--theme-dark", bat::theme::env::BAT_THEME_DARK), @@ -145,16 +145,21 @@ pub fn get_args_from_env_vars() -> Vec { ("--pager", "BAT_PAGER"), ("--paging", "BAT_PAGING"), ("--style", "BAT_STYLE"), - ("--gitsigns", bat::gitsigns::env::BAT_GITSIGNS), - ] - .iter() - .filter_map(|(flag, key)| { - env::var(key) - .ok() - .map(|var| [flag.to_string(), var].join("=")) - }) - .map(|a| a.into()) - .collect() + ]; + + #[cfg(feature = "git")] + { + args.push(("--gitsigns", bat::gitsigns::env::BAT_GITSIGNS)); + } + + args.iter() + .filter_map(|(flag, key)| { + env::var(key) + .ok() + .map(|var| [flag.to_string(), var].join("=")) + }) + .map(|a| a.into()) + .collect() } #[test] From 02daedfc0d7f316d09bfb44da3732a4afa5a16c9 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 06:08:57 +0100 Subject: [PATCH 04/11] Add `#[cfg(feature = "git")]` to gitsigns related tests --- tests/integration_tests.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 62a3041410..fc5a3e2217 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -3242,6 +3242,7 @@ fn style_components_will_merge_with_env_var() { .stderr(""); } +#[cfg(feature = "git")] fn bat_tempdir(namespace: &str) -> PathBuf { let tmp = env::temp_dir(); let bat_tempdir = tmp.join("bat_test").join(namespace); @@ -3255,6 +3256,7 @@ fn bat_tempdir(namespace: &str) -> PathBuf { bat_tempdir } +#[cfg(feature = "git")] fn make_git_repo_with_file_changes(repo_path: &Path, file_path: &Path) { let _ = fs::create_dir_all(repo_path); @@ -3295,6 +3297,7 @@ fn make_git_repo_with_file_changes(repo_path: &Path, file_path: &Path) { } #[test] +#[cfg(feature = "git")] fn gitsigns_default() { let bat_tempdir = bat_tempdir("gitsigns_default"); let repo_path = bat_tempdir.join("bat"); @@ -3320,6 +3323,7 @@ fn gitsigns_default() { } #[test] +#[cfg(feature = "git")] fn gitsigns_classic() { let bat_tempdir = bat_tempdir("gitsigns_classic"); let repo_path = bat_tempdir.join("bat"); @@ -3346,6 +3350,7 @@ fn gitsigns_classic() { } #[test] +#[cfg(feature = "git")] fn gitsigns_modern() { let bat_tempdir = bat_tempdir("gitsigns_modern"); let repo_path = bat_tempdir.join("bat"); @@ -3372,6 +3377,7 @@ fn gitsigns_modern() { } #[test] +#[cfg(feature = "git")] fn gitsigns_custom() { let bat_tempdir = bat_tempdir("gitsigns_custom"); let repo_path = bat_tempdir.join("bat"); @@ -3398,6 +3404,7 @@ fn gitsigns_custom() { } #[test] +#[cfg(feature = "git")] fn gitsigns_using_config_file() { let bat_tempdir = bat_tempdir("gitsigns_using_config_file"); let repo_path = bat_tempdir.join("bat"); @@ -3431,6 +3438,7 @@ fn gitsigns_using_config_file() { } #[test] +#[cfg(feature = "git")] fn gitsigns_using_env_var() { let bat_tempdir = bat_tempdir("gitsigns_using_env_var"); let repo_path = bat_tempdir.join("bat"); @@ -3457,6 +3465,7 @@ fn gitsigns_using_env_var() { } #[test] +#[cfg(feature = "git")] fn gitsigns_parse_error_expected_4_comma_separated_signs() { // bat --gitsigns="1,2,3" test.txt bat() @@ -3468,6 +3477,7 @@ fn gitsigns_parse_error_expected_4_comma_separated_signs() { } #[test] +#[cfg(feature = "git")] fn gitsigns_parse_error_each_sign_must_be_a_single_char() { // bat --gitsigns="M,A,RA,RB" test.txt bat() From 97cc9c37ef83332d6e7fb14787ccca98ce43e4dd Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 06:38:41 +0100 Subject: [PATCH 05/11] Fix `PathBuf` import for *windows* --- tests/integration_tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index fc5a3e2217..cd3efab4eb 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -4,7 +4,7 @@ use predicates::{prelude::predicate, str::PredicateStrExt}; use serial_test::serial; use std::env; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command as StdCommand; use std::str::from_utf8; use tempfile::tempdir; @@ -13,7 +13,6 @@ use tempfile::tempdir; mod unix { pub use std::fs::File; pub use std::io::{self, Write}; - pub use std::path::PathBuf; pub use std::process::Stdio; pub use std::thread; pub use std::time::Duration; From 727a7fb85f1e672bb5e1aab4766c717a3490d6a0 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 07:05:37 +0100 Subject: [PATCH 06/11] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5419c6fb9c..5d703458c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # unreleased ## Features +- Use theme styles for git signs, see PR #3407 (@flashios09) +- Add `--gitsigns` option, see PR #3407 (@flashios09) - Add build for windows/ARM64 platform. #3190 (@alcroito) - Add paging to `--list-themes`, see PR #3239 (@einfachIrgendwer0815) From 3712fb99fc9c026fad880e66731dcb0bccddbcbb Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 07:53:10 +0100 Subject: [PATCH 07/11] Fix gitsigns tests on windows: normalize output line endings --- tests/integration_tests.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index cd3efab4eb..4d5fc56f0d 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -34,6 +34,12 @@ use utils::command::{bat, bat_with_config}; use utils::command::bat_raw_command; use utils::mocked_pagers; +#[cfg(windows)] +const LINE_ENDING: &str = "\r\n"; + +#[cfg(unix)] +const LINE_ENDING: &str = "\n"; + const EXAMPLES_DIR: &str = "tests/examples"; fn get_config() -> &'static str { @@ -3241,6 +3247,11 @@ fn style_components_will_merge_with_env_var() { .stderr(""); } +/// Normalize line endings depending on OS(windows or unix) +fn normalize_line_endings(s: &str) -> String { + s.replace("\n", LINE_ENDING) +} + #[cfg(feature = "git")] fn bat_tempdir(namespace: &str) -> PathBuf { let tmp = env::temp_dir(); @@ -3316,7 +3327,8 @@ fn gitsigns_default() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/default").unwrap(); + let expected = + normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/default").unwrap()); assert_eq!(output, expected); } @@ -3343,7 +3355,8 @@ fn gitsigns_classic() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/classic").unwrap(); + let expected = + normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/classic").unwrap()); assert_eq!(output, expected); } @@ -3370,7 +3383,8 @@ fn gitsigns_modern() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/modern").unwrap(); + let expected = + normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/modern").unwrap()); assert_eq!(output, expected); } @@ -3397,7 +3411,8 @@ fn gitsigns_custom() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/custom").unwrap(); + let expected = + normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/custom").unwrap()); assert_eq!(output, expected); } @@ -3431,7 +3446,8 @@ fn gitsigns_using_config_file() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/modern").unwrap(); + let expected = + normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/modern").unwrap()); assert_eq!(output, expected); } @@ -3458,7 +3474,8 @@ fn gitsigns_using_env_var() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/modern").unwrap(); + let expected = + normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/modern").unwrap()); assert_eq!(output, expected); } From 992884a5be5cc8187feacb038d88e6e4f0f85918 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 22:17:09 +0100 Subject: [PATCH 08/11] Fix test on **x86_64-pc-windows-msvc (windows-2025)** --- tests/gitsigns/output/{ => unix}/classic | 0 tests/gitsigns/output/{ => unix}/custom | 0 tests/gitsigns/output/{ => unix}/default | 0 tests/gitsigns/output/{ => unix}/modern | 0 tests/gitsigns/output/windows/classic | 12 +++ tests/gitsigns/output/windows/custom | 12 +++ tests/gitsigns/output/windows/default | 12 +++ tests/gitsigns/output/windows/modern | 12 +++ tests/integration_tests.rs | 99 ++++++++++++++---------- 9 files changed, 108 insertions(+), 39 deletions(-) rename tests/gitsigns/output/{ => unix}/classic (100%) rename tests/gitsigns/output/{ => unix}/custom (100%) rename tests/gitsigns/output/{ => unix}/default (100%) rename tests/gitsigns/output/{ => unix}/modern (100%) create mode 100644 tests/gitsigns/output/windows/classic create mode 100644 tests/gitsigns/output/windows/custom create mode 100644 tests/gitsigns/output/windows/default create mode 100644 tests/gitsigns/output/windows/modern diff --git a/tests/gitsigns/output/classic b/tests/gitsigns/output/unix/classic similarity index 100% rename from tests/gitsigns/output/classic rename to tests/gitsigns/output/unix/classic diff --git a/tests/gitsigns/output/custom b/tests/gitsigns/output/unix/custom similarity index 100% rename from tests/gitsigns/output/custom rename to tests/gitsigns/output/unix/custom diff --git a/tests/gitsigns/output/default b/tests/gitsigns/output/unix/default similarity index 100% rename from tests/gitsigns/output/default rename to tests/gitsigns/output/unix/default diff --git a/tests/gitsigns/output/modern b/tests/gitsigns/output/unix/modern similarity index 100% rename from tests/gitsigns/output/modern rename to tests/gitsigns/output/unix/modern diff --git a/tests/gitsigns/output/windows/classic b/tests/gitsigns/output/windows/classic new file mode 100644 index 0000000000..53dbf7076c --- /dev/null +++ b/tests/gitsigns/output/windows/classic @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 ‾ │ line 2 + 2 ~ │ line 3 changed + 3 │ line 4 + 4 _ │ line 5 + 5 │ line 7 + 6 │ line 8 + 7 + │ new line + 8 + │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/gitsigns/output/windows/custom b/tests/gitsigns/output/windows/custom new file mode 100644 index 0000000000..86c4ccebe3 --- /dev/null +++ b/tests/gitsigns/output/windows/custom @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 ‾ │ line 2 + 2 M │ line 3 changed + 3 │ line 4 + 4 _ │ line 5 + 5 │ line 7 + 6 │ line 8 + 7 A │ new line + 8 A │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/gitsigns/output/windows/default b/tests/gitsigns/output/windows/default new file mode 100644 index 0000000000..53dbf7076c --- /dev/null +++ b/tests/gitsigns/output/windows/default @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 ‾ │ line 2 + 2 ~ │ line 3 changed + 3 │ line 4 + 4 _ │ line 5 + 5 │ line 7 + 6 │ line 8 + 7 + │ new line + 8 + │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/gitsigns/output/windows/modern b/tests/gitsigns/output/windows/modern new file mode 100644 index 0000000000..80ec991ef5 --- /dev/null +++ b/tests/gitsigns/output/windows/modern @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 ‾ │ line 2 + 2 ▎ │ line 3 changed + 3 │ line 4 + 4 _ │ line 5 + 5 │ line 7 + 6 │ line 8 + 7 ▎ │ new line + 8 ▎ │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 4d5fc56f0d..ef53cd6cfa 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -34,12 +34,6 @@ use utils::command::{bat, bat_with_config}; use utils::command::bat_raw_command; use utils::mocked_pagers; -#[cfg(windows)] -const LINE_ENDING: &str = "\r\n"; - -#[cfg(unix)] -const LINE_ENDING: &str = "\n"; - const EXAMPLES_DIR: &str = "tests/examples"; fn get_config() -> &'static str { @@ -3247,9 +3241,9 @@ fn style_components_will_merge_with_env_var() { .stderr(""); } -/// Normalize line endings depending on OS(windows or unix) -fn normalize_line_endings(s: &str) -> String { - s.replace("\n", LINE_ENDING) +/// Used to fix bat ouput on windows: mixed line endings `\n` and `\r\n` !! +fn normalize_output_line_endings(s: &str) -> String { + s.replace("\r\n", "\n") } #[cfg(feature = "git")] @@ -3307,9 +3301,9 @@ fn make_git_repo_with_file_changes(repo_path: &Path, file_path: &Path) { } #[test] -#[cfg(feature = "git")] -fn gitsigns_default() { - let bat_tempdir = bat_tempdir("gitsigns_default"); +#[cfg(all(feature = "git", unix))] +fn gitsigns_default_unix() { + let bat_tempdir = bat_tempdir("gitsigns_default_unix"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3327,16 +3321,15 @@ fn gitsigns_default() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = - normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/default").unwrap()); + let expected = fs::read_to_string("tests/gitsigns/output/unix/default").unwrap(); assert_eq!(output, expected); } #[test] -#[cfg(feature = "git")] -fn gitsigns_classic() { - let bat_tempdir = bat_tempdir("gitsigns_classic"); +#[cfg(all(feature = "git", unix))] +fn gitsigns_classic_unix() { + let bat_tempdir = bat_tempdir("gitsigns_classic_unix"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3355,16 +3348,15 @@ fn gitsigns_classic() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = - normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/classic").unwrap()); + let expected = fs::read_to_string("tests/gitsigns/output/unix/classic").unwrap(); assert_eq!(output, expected); } #[test] -#[cfg(feature = "git")] -fn gitsigns_modern() { - let bat_tempdir = bat_tempdir("gitsigns_modern"); +#[cfg(all(feature = "git", unix))] +fn gitsigns_modern_unix() { + let bat_tempdir = bat_tempdir("gitsigns_modern_unix"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3383,16 +3375,15 @@ fn gitsigns_modern() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = - normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/modern").unwrap()); + let expected = fs::read_to_string("tests/gitsigns/output/unix/modern").unwrap(); assert_eq!(output, expected); } #[test] -#[cfg(feature = "git")] -fn gitsigns_custom() { - let bat_tempdir = bat_tempdir("gitsigns_custom"); +#[cfg(all(feature = "git", unix))] +fn gitsigns_custom_unix() { + let bat_tempdir = bat_tempdir("gitsigns_custom_unix"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3411,16 +3402,15 @@ fn gitsigns_custom() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = - normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/custom").unwrap()); + let expected = fs::read_to_string("tests/gitsigns/output/unix/custom").unwrap(); assert_eq!(output, expected); } #[test] -#[cfg(feature = "git")] -fn gitsigns_using_config_file() { - let bat_tempdir = bat_tempdir("gitsigns_using_config_file"); +#[cfg(all(feature = "git", unix))] +fn gitsigns_using_config_file_unix() { + let bat_tempdir = bat_tempdir("gitsigns_using_config_file_unix"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); let bat_config_path = bat_tempdir.join("bat.conf"); @@ -3446,16 +3436,15 @@ fn gitsigns_using_config_file() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = - normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/modern").unwrap()); + let expected = fs::read_to_string("tests/gitsigns/output/unix/modern").unwrap(); assert_eq!(output, expected); } #[test] -#[cfg(feature = "git")] -fn gitsigns_using_env_var() { - let bat_tempdir = bat_tempdir("gitsigns_using_env_var"); +#[cfg(all(feature = "git", unix))] +fn gitsigns_using_env_var_unix() { + let bat_tempdir = bat_tempdir("gitsigns_using_env_var_unix"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3474,8 +3463,7 @@ fn gitsigns_using_env_var() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = - normalize_line_endings(&fs::read_to_string("tests/gitsigns/output/modern").unwrap()); + let expected = fs::read_to_string("tests/gitsigns/output/unix/modern").unwrap(); assert_eq!(output, expected); } @@ -3503,3 +3491,36 @@ fn gitsigns_parse_error_each_sign_must_be_a_single_char() { .code(1) .stderr(contains("Each sign must be a single character")); } + +#[test] +#[cfg(all(feature = "git", windows))] +fn gitsigns_default_windows() { + let bat_tempdir = bat_tempdir("gitsigns_default_windows"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows/default").unwrap(); + + // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) + // TODO: fix test on i686-pc-windows-msvc (windows-2025) + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +// TODO: add windows gitsigns `classic`, `modern`, `custom`, `using_config_file` and `using_env_var` tests From e13dfa558bf98032be1aa00461428f264e5f3476 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 22:33:41 +0100 Subject: [PATCH 09/11] Add `#[cfg(windows)]` to `normalize_output_line_endings` function --- tests/integration_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index ef53cd6cfa..95dd41f41f 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -3242,6 +3242,7 @@ fn style_components_will_merge_with_env_var() { } /// Used to fix bat ouput on windows: mixed line endings `\n` and `\r\n` !! +#[cfg(windows)] fn normalize_output_line_endings(s: &str) -> String { s.replace("\r\n", "\n") } From c8340fb6f883b503d9ff9cbacd81251637f12953 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Tue, 23 Sep 2025 22:47:32 +0100 Subject: [PATCH 10/11] Finish gitsigns tests for x86_64-pc-windows-msvc (windows-2025) --- tests/integration_tests.rs | 167 ++++++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 95dd41f41f..7e5c69e189 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -3524,4 +3524,169 @@ fn gitsigns_default_windows() { ); } -// TODO: add windows gitsigns `classic`, `modern`, `custom`, `using_config_file` and `using_env_var` tests +#[test] +#[cfg(all(feature = "git", windows))] +fn gitsigns_classic_windows() { + let bat_tempdir = bat_tempdir("gitsigns_classic_windows"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=classic tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=classic", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows/classic").unwrap(); + + // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) + // TODO: fix test on i686-pc-windows-msvc (windows-2025) + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", windows))] +fn gitsigns_modern_windows() { + let bat_tempdir = bat_tempdir("gitsigns_modern_windows"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=modern tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=modern", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows/modern").unwrap(); + + // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) + // TODO: fix test on i686-pc-windows-msvc (windows-2025) + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", windows))] +fn gitsigns_custom_windows() { + let bat_tempdir = bat_tempdir("gitsigns_custom_windows"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=M,A,‾,_ tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=M,A,‾,_", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows/custom").unwrap(); + + // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) + // TODO: fix test on i686-pc-windows-msvc (windows-2025) + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", windows))] +fn gitsigns_using_config_file_windows() { + let bat_tempdir = bat_tempdir("gitsigns_using_config_file_windows"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + let bat_config_path = bat_tempdir.join("bat.conf"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + fs::write( + &bat_config_path, + fs::read_to_string("tests/gitsigns/bat.conf").unwrap(), + ) + .unwrap(); + + // BAT_CONFIG_PATH=tests/gitsigns/bat.conf bat tests/gitsigns/after/test.txt + let cmd = bat_with_config() + .env("BAT_CONFIG_PATH", bat_config_path.to_str().unwrap()) + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows/modern").unwrap(); + + // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) + // TODO: fix test on i686-pc-windows-msvc (windows-2025) + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", windows))] +fn gitsigns_using_env_var_windows() { + let bat_tempdir = bat_tempdir("gitsigns_using_env_var_windows"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // BAT_GITSIGNS=modern bat tests/gitsigns/after/test.txt + let cmd = bat() + .env("BAT_GITSIGNS", "modern") + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows/modern").unwrap(); + + // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) + // TODO: fix test on i686-pc-windows-msvc (windows-2025) + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} From b56ed6e0e2084dd5f145d99a7c7d279b10824626 Mon Sep 17 00:00:00 2001 From: SAM GEORGE Date: Wed, 24 Sep 2025 00:20:53 +0100 Subject: [PATCH 11/11] Fix windows x86 tests --- tests/gitsigns/output/windows_x86/classic | 12 + tests/gitsigns/output/windows_x86/custom | 12 + tests/gitsigns/output/windows_x86/default | 12 + tests/gitsigns/output/windows_x86/modern | 12 + .../{windows => windows_x86_64}/classic | 0 .../output/{windows => windows_x86_64}/custom | 0 .../{windows => windows_x86_64}/default | 0 .../output/{windows => windows_x86_64}/modern | 0 tests/integration_tests.rs | 246 +++++++++++++++--- 9 files changed, 258 insertions(+), 36 deletions(-) create mode 100644 tests/gitsigns/output/windows_x86/classic create mode 100644 tests/gitsigns/output/windows_x86/custom create mode 100644 tests/gitsigns/output/windows_x86/default create mode 100644 tests/gitsigns/output/windows_x86/modern rename tests/gitsigns/output/{windows => windows_x86_64}/classic (100%) rename tests/gitsigns/output/{windows => windows_x86_64}/custom (100%) rename tests/gitsigns/output/{windows => windows_x86_64}/default (100%) rename tests/gitsigns/output/{windows => windows_x86_64}/modern (100%) diff --git a/tests/gitsigns/output/windows_x86/classic b/tests/gitsigns/output/windows_x86/classic new file mode 100644 index 0000000000..05dc14503c --- /dev/null +++ b/tests/gitsigns/output/windows_x86/classic @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 ~ │ line 2 + 2 ~ │ line 3 changed + 3 ~ │ line 4 + 4 ~ │ line 5 + 5 ~ │ line 7 + 6 ~ │ line 8 + 7 ~ │ new line + 8 ~ │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/gitsigns/output/windows_x86/custom b/tests/gitsigns/output/windows_x86/custom new file mode 100644 index 0000000000..081fd45164 --- /dev/null +++ b/tests/gitsigns/output/windows_x86/custom @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 M │ line 2 + 2 M │ line 3 changed + 3 M │ line 4 + 4 M │ line 5 + 5 M │ line 7 + 6 M │ line 8 + 7 M │ new line + 8 M │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/gitsigns/output/windows_x86/default b/tests/gitsigns/output/windows_x86/default new file mode 100644 index 0000000000..05dc14503c --- /dev/null +++ b/tests/gitsigns/output/windows_x86/default @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 ~ │ line 2 + 2 ~ │ line 3 changed + 3 ~ │ line 4 + 4 ~ │ line 5 + 5 ~ │ line 7 + 6 ~ │ line 8 + 7 ~ │ new line + 8 ~ │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/gitsigns/output/windows_x86/modern b/tests/gitsigns/output/windows_x86/modern new file mode 100644 index 0000000000..eca7fd2f14 --- /dev/null +++ b/tests/gitsigns/output/windows_x86/modern @@ -0,0 +1,12 @@ +───────┬─────────────────────────────────────────────────────────────────────── + │ File: tests/gitsigns/after/test.txt +───────┼─────────────────────────────────────────────────────────────────────── + 1 ▎ │ line 2 + 2 ▎ │ line 3 changed + 3 ▎ │ line 4 + 4 ▎ │ line 5 + 5 ▎ │ line 7 + 6 ▎ │ line 8 + 7 ▎ │ new line + 8 ▎ │ another new line +───────┴─────────────────────────────────────────────────────────────────────── diff --git a/tests/gitsigns/output/windows/classic b/tests/gitsigns/output/windows_x86_64/classic similarity index 100% rename from tests/gitsigns/output/windows/classic rename to tests/gitsigns/output/windows_x86_64/classic diff --git a/tests/gitsigns/output/windows/custom b/tests/gitsigns/output/windows_x86_64/custom similarity index 100% rename from tests/gitsigns/output/windows/custom rename to tests/gitsigns/output/windows_x86_64/custom diff --git a/tests/gitsigns/output/windows/default b/tests/gitsigns/output/windows_x86_64/default similarity index 100% rename from tests/gitsigns/output/windows/default rename to tests/gitsigns/output/windows_x86_64/default diff --git a/tests/gitsigns/output/windows/modern b/tests/gitsigns/output/windows_x86_64/modern similarity index 100% rename from tests/gitsigns/output/windows/modern rename to tests/gitsigns/output/windows_x86_64/modern diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 7e5c69e189..728905fb9f 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -3494,9 +3494,9 @@ fn gitsigns_parse_error_each_sign_must_be_a_single_char() { } #[test] -#[cfg(all(feature = "git", windows))] -fn gitsigns_default_windows() { - let bat_tempdir = bat_tempdir("gitsigns_default_windows"); +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86_64"))] +fn gitsigns_default_windows_x86_64() { + let bat_tempdir = bat_tempdir("gitsigns_default_windows_x86_64"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3514,10 +3514,8 @@ fn gitsigns_default_windows() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/windows/default").unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86_64/default").unwrap(); - // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) - // TODO: fix test on i686-pc-windows-msvc (windows-2025) assert_eq!( normalize_output_line_endings(output), normalize_output_line_endings(&expected) @@ -3525,9 +3523,9 @@ fn gitsigns_default_windows() { } #[test] -#[cfg(all(feature = "git", windows))] -fn gitsigns_classic_windows() { - let bat_tempdir = bat_tempdir("gitsigns_classic_windows"); +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86_64"))] +fn gitsigns_classic_windows_x86_64() { + let bat_tempdir = bat_tempdir("gitsigns_classic_windows_x86_64"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3546,10 +3544,8 @@ fn gitsigns_classic_windows() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/windows/classic").unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86_64/classic").unwrap(); - // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) - // TODO: fix test on i686-pc-windows-msvc (windows-2025) assert_eq!( normalize_output_line_endings(output), normalize_output_line_endings(&expected) @@ -3557,9 +3553,9 @@ fn gitsigns_classic_windows() { } #[test] -#[cfg(all(feature = "git", windows))] -fn gitsigns_modern_windows() { - let bat_tempdir = bat_tempdir("gitsigns_modern_windows"); +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86_64"))] +fn gitsigns_modern_windows_x86_64() { + let bat_tempdir = bat_tempdir("gitsigns_modern_windows_x86_64"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3578,10 +3574,8 @@ fn gitsigns_modern_windows() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/windows/modern").unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86_64/modern").unwrap(); - // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) - // TODO: fix test on i686-pc-windows-msvc (windows-2025) assert_eq!( normalize_output_line_endings(output), normalize_output_line_endings(&expected) @@ -3589,9 +3583,9 @@ fn gitsigns_modern_windows() { } #[test] -#[cfg(all(feature = "git", windows))] -fn gitsigns_custom_windows() { - let bat_tempdir = bat_tempdir("gitsigns_custom_windows"); +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86_64"))] +fn gitsigns_custom_windows_x86_64() { + let bat_tempdir = bat_tempdir("gitsigns_custom_windows_x86_64"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3610,10 +3604,8 @@ fn gitsigns_custom_windows() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/windows/custom").unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86_64/custom").unwrap(); - // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) - // TODO: fix test on i686-pc-windows-msvc (windows-2025) assert_eq!( normalize_output_line_endings(output), normalize_output_line_endings(&expected) @@ -3621,9 +3613,9 @@ fn gitsigns_custom_windows() { } #[test] -#[cfg(all(feature = "git", windows))] -fn gitsigns_using_config_file_windows() { - let bat_tempdir = bat_tempdir("gitsigns_using_config_file_windows"); +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86_64"))] +fn gitsigns_using_config_file_windows_x86_64() { + let bat_tempdir = bat_tempdir("gitsigns_using_config_file_windows_x86_64"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); let bat_config_path = bat_tempdir.join("bat.conf"); @@ -3649,10 +3641,8 @@ fn gitsigns_using_config_file_windows() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/windows/modern").unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86_64/modern").unwrap(); - // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) - // TODO: fix test on i686-pc-windows-msvc (windows-2025) assert_eq!( normalize_output_line_endings(output), normalize_output_line_endings(&expected) @@ -3660,9 +3650,9 @@ fn gitsigns_using_config_file_windows() { } #[test] -#[cfg(all(feature = "git", windows))] -fn gitsigns_using_env_var_windows() { - let bat_tempdir = bat_tempdir("gitsigns_using_env_var_windows"); +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86_64"))] +fn gitsigns_using_env_var_windows_x86_64() { + let bat_tempdir = bat_tempdir("gitsigns_using_env_var_windows_x86_64"); let repo_path = bat_tempdir.join("bat"); let file_path = repo_path.join("test.txt"); @@ -3681,10 +3671,194 @@ fn gitsigns_using_env_var_windows() { .success(); let output = from_utf8(&cmd.get_output().stdout).unwrap(); - let expected = fs::read_to_string("tests/gitsigns/output/windows/modern").unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86_64/modern").unwrap(); + + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86"))] +fn gitsigns_default_windows_x86() { + let bat_tempdir = bat_tempdir("gitsigns_default_windows_x86"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86/default").unwrap(); + + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86"))] +fn gitsigns_classic_windows_x86() { + let bat_tempdir = bat_tempdir("gitsigns_classic_windows_x86"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=classic tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=classic", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86/classic").unwrap(); + + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86"))] +fn gitsigns_modern_windows_x86() { + let bat_tempdir = bat_tempdir("gitsigns_modern_windows_x86"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=modern tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=modern", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86/modern").unwrap(); + + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86"))] +fn gitsigns_custom_windows_x86() { + let bat_tempdir = bat_tempdir("gitsigns_custom_windows_x86"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // bat --gitsigns=M,A,‾,_ tests/gitsigns/after/test.txt + let cmd = bat() + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + "--gitsigns=M,A,‾,_", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86/custom").unwrap(); + + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86"))] +fn gitsigns_using_config_file_windows_x86() { + let bat_tempdir = bat_tempdir("gitsigns_using_config_file_windows_x86"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + let bat_config_path = bat_tempdir.join("bat.conf"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + fs::write( + &bat_config_path, + fs::read_to_string("tests/gitsigns/bat.conf").unwrap(), + ) + .unwrap(); + + // BAT_CONFIG_PATH=tests/gitsigns/bat.conf bat tests/gitsigns/after/test.txt + let cmd = bat_with_config() + .env("BAT_CONFIG_PATH", bat_config_path.to_str().unwrap()) + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86/modern").unwrap(); + + assert_eq!( + normalize_output_line_endings(output), + normalize_output_line_endings(&expected) + ); +} + +#[test] +#[cfg(all(feature = "git", target_os = "windows", target_arch = "x86"))] +fn gitsigns_using_env_var_windows_x86() { + let bat_tempdir = bat_tempdir("gitsigns_using_env_var_windows_x86"); + let repo_path = bat_tempdir.join("bat"); + let file_path = repo_path.join("test.txt"); + + make_git_repo_with_file_changes(&repo_path, &file_path); + + // BAT_GITSIGNS=modern bat tests/gitsigns/after/test.txt + let cmd = bat() + .env("BAT_GITSIGNS", "modern") + .current_dir(repo_path) + .args([ + "--decorations=always", + "--file-name=tests/gitsigns/after/test.txt", + file_path.to_str().unwrap(), + ]) + .assert() + .success(); + + let output = from_utf8(&cmd.get_output().stdout).unwrap(); + let expected = fs::read_to_string("tests/gitsigns/output/windows_x86/modern").unwrap(); - // NOTE: expected to pass on x86_64-pc-windows-msvc (windows-2025) and fail on i686-pc-windows-msvc (windows-2025) - // TODO: fix test on i686-pc-windows-msvc (windows-2025) assert_eq!( normalize_output_line_endings(output), normalize_output_line_endings(&expected)