diff options
-rw-r--r-- | src/ansi.rs | 68 | ||||
-rw-r--r-- | src/cli.rs | 18 | ||||
-rw-r--r-- | src/config.rs | 70 | ||||
-rw-r--r-- | src/delta.rs | 34 | ||||
-rw-r--r-- | src/features/color_moved.rs | 24 | ||||
-rw-r--r-- | src/features/mod.rs | 5 | ||||
-rw-r--r-- | src/features/side_by_side.rs | 34 | ||||
-rw-r--r-- | src/options/set.rs | 10 | ||||
-rw-r--r-- | src/paint.rs | 39 | ||||
-rw-r--r-- | src/parse_style.rs | 4 | ||||
-rw-r--r-- | src/style.rs | 90 |
11 files changed, 254 insertions, 142 deletions
diff --git a/src/ansi.rs b/src/ansi.rs index 01b2e0e6..c885c304 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -1,3 +1,71 @@ +use std::cmp::min; + +use console; +use itertools::Itertools; + pub const ANSI_CSI_CLEAR_TO_EOL: &str = "\x1b[0K"; pub const ANSI_CSI_CLEAR_TO_BOL: &str = "\x1b[1K"; pub const ANSI_SGR_RESET: &str = "\x1b[0m"; + +pub fn string_starts_with_ansi_escape_sequence(s: &str) -> bool { + console::AnsiCodeIterator::new(s) + .nth(0) + .map(|(_, is_ansi)| is_ansi) + .unwrap_or(false) +} + +/// Return string formed from a byte slice starting at byte position `start`, where the index +/// counts bytes in non-ANSI-escape-sequence content only. All ANSI escape sequences in the +/// original string are preserved. +pub fn ansi_preserving_slice(s: &str, start: usize) -> String { + console::AnsiCodeIterator::new(s) + .scan(0, |i, (substring, is_ansi)| { + // i is the index in non-ANSI-escape-sequence content. + let substring_slice = if is_ansi || *i > start { + substring + } else { + &substring[min(substring.len(), start - *i)..] + }; + if !is_ansi { + *i += substring.len(); + } + Some(substring_slice) + }) + .join("") +} + +#[cfg(test)] +mod tests { + + use crate::ansi::ansi_preserving_slice; + use crate::ansi::string_starts_with_ansi_escape_sequence; + + #[test] + fn test_string_starts_with_ansi_escape_sequence() { + assert!(!string_starts_with_ansi_escape_sequence("")); + assert!(!string_starts_with_ansi_escape_sequence("-")); + assert!(string_starts_with_ansi_escape_sequence( + "\x1b[31m-XXX\x1b[m\n" + )); + assert!(string_starts_with_ansi_escape_sequence("\x1b[32m+XXX")); + } + + #[test] + fn test_ansi_preserving_slice() { + assert_eq!(ansi_preserving_slice("", 0), ""); + assert_eq!(ansi_preserving_slice("a", 0), "a"); + assert_eq!(ansi_preserving_slice("a", 1), ""); + assert_eq!( + ansi_preserving_slice("\x1b[1;35m-2222.2222.2222.2222\x1b[0m", 1), + "\x1b[1;35m2222.2222.2222.2222\x1b[0m" + ); + assert_eq!( + ansi_preserving_slice("\x1b[1;35m-2222.2222.2222.2222\x1b[0m", 15), + "\x1b[1;35m.2222\x1b[0m" + ); + assert_eq!( + ansi_preserving_slice("\x1b[1;36m-\x1b[m\x1b[1;36m2222·2222·2222·2222\x1b[m\n", 1), + "\x1b[1;36m\x1b[m\x1b[1;36m2222·2222·2222·2222\x1b[m\n" + ) + } +} @@ -234,14 +234,6 @@ pub struct Opt { /// --file-renamed-label. pub navigate: bool, - #[structopt(long = "color-moved")] - /// Detect moved code lines and apply the styles color-moved-minus-style and - /// color-moved-plus-style. This option requires the git config setting `diff.colorMoved = - /// true` (or that you pass --color-moved to git on the command line). It can only work if - /// delta receives colored input from git. So it works with `core.pager = delta` in git config, - /// but if you pipe git's output to delta, you must pass --color=always to git. - pub color_moved: bool, - #[structopt(long = "hyperlinks")] /// Render commit hashes, file names, and line numbers as hyperlinks, according to the /// hyperlink spec for terminal emulators: @@ -435,16 +427,6 @@ pub struct Opt { #[structopt(long = "line-numbers-right-style", default_value = "auto")] pub line_numbers_right_style: String, - #[structopt(long = "color-moved-minus-style", default_value = "auto")] - /// Style (foreground, background, attributes) for moved lines in their old location. See - /// STYLES section. - pub color_moved_minus_style: String, - - #[structopt(long = "color-moved-plus-style", default_value = "auto")] - /// Style (foreground, background, attributes) for moved lines in their new location. See - /// STYLES section. - pub color_moved_plus_style: String, - #[structopt(long = "file-modified-label", default_value = "")] /// Text to display in front of a modified file path. pub file_modified_label: String, diff --git a/src/config.rs b/src/config.rs index 4703e38e..9b39c350 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,7 +15,7 @@ use crate::delta::State; use crate::env; use crate::features::side_by_side; use crate::git_config_entry::GitConfigEntry; -use crate::style::Style; +use crate::style::{self, Style}; pub struct Config { pub available_terminal_width: usize, @@ -46,7 +46,6 @@ pub struct Config { pub minus_emph_style: Style, pub minus_empty_line_marker_style: Style, pub minus_file: Option<PathBuf>, - pub minus_moved_style: Style, pub minus_non_emph_style: Style, pub minus_style: Style, pub navigate: bool, @@ -56,11 +55,10 @@ pub struct Config { pub plus_emph_style: Style, pub plus_empty_line_marker_style: Style, pub plus_file: Option<PathBuf>, - pub plus_moved_style: Style, pub plus_non_emph_style: Style, pub plus_style: Style, - pub raw_expected_minus_style: Style, - pub raw_expected_plus_style: Style, + pub git_minus_style: Style, + pub git_plus_style: Style, pub side_by_side: bool, pub side_by_side_data: side_by_side::SideBySideData, pub syntax_dummy_theme: SyntaxTheme, @@ -77,10 +75,8 @@ pub struct Config { impl Config { pub fn get_style(&self, state: &State) -> &Style { match state { - State::HunkMinus(false) => &self.minus_style, - State::HunkMinus(true) => &self.minus_moved_style, - State::HunkPlus(false) => &self.plus_style, - State::HunkPlus(true) => &self.plus_moved_style, + State::HunkMinus(_) => &self.minus_style, + State::HunkPlus(_) => &self.plus_style, State::CommitMeta => &self.commit_style, State::FileMeta => &self.file_style, State::HunkHeader => &self.hunk_header_style, @@ -95,13 +91,11 @@ impl From<cli::Opt> for Config { minus_style, minus_emph_style, minus_non_emph_style, - minus_moved_style, minus_empty_line_marker_style, zero_style, plus_style, plus_emph_style, plus_non_emph_style, - plus_moved_style, plus_empty_line_marker_style, whitespace_error_style, ) = make_hunk_styles(&opt); @@ -137,26 +131,14 @@ impl From<cli::Opt> for Config { &opt.computed.available_terminal_width, ); - let raw_expected_minus_style = Style::from_str( - match opt.git_config_entries.get("color.diff.old") { - Some(GitConfigEntry::Style(s)) => s, - _ => "red", - }, - None, - None, - opt.computed.true_color, - false, - ); - let raw_expected_plus_style = Style::from_str( - match opt.git_config_entries.get("color.diff.new") { - Some(GitConfigEntry::Style(s)) => s, - _ => "green", - }, - None, - None, - opt.computed.true_color, - false, - ); + let git_minus_style = match opt.git_config_entries.get("color.diff.old") { + Some(GitConfigEntry::Style(s)) => Style::from_git_str(s), + _ => *style::GIT_DEFAULT_MINUS_STYLE, + }; + let git_plus_style = match opt.git_config_entries.get("color.diff.new") { + Some(GitConfigEntry::Style(s)) => Style::from_git_str(s), + _ => *style::GIT_DEFAULT_PLUS_STYLE, + }; Self { available_terminal_width: opt.computed.available_terminal_width, @@ -189,7 +171,6 @@ impl From<cli::Opt> for Config { minus_emph_style, minus_empty_line_marker_style, minus_file: opt.minus_file.map(|s| s.clone()), - minus_moved_style, minus_non_emph_style, minus_style, navigate: opt.navigate, @@ -199,11 +180,10 @@ impl From<cli::Opt> for Config { plus_emph_style, plus_empty_line_marker_style, plus_file: opt.plus_file.map(|s| s.clone()), - plus_moved_style, plus_non_emph_style, plus_style, - raw_expected_minus_style, - raw_expected_plus_style, + git_minus_style, + git_plus_style, side_by_side: opt.side_by_side, side_by_side_data, syntax_dummy_theme: SyntaxTheme::default(), @@ -232,8 +212,6 @@ fn make_hunk_styles<'a>( Style, Style, Style, - Style, - Style, ) { let is_light_mode = opt.computed.is_light_mode; let true_color = opt.computed.true_color; @@ -273,14 +251,6 @@ fn make_hunk_styles<'a>( false, ); - let minus_moved_style = Style::from_str( - &opt.color_moved_minus_style, - Some(minus_style), - None, - true_color, - false, - ); - // The style used to highlight a removed empty line when otherwise it would be invisible due to // lack of background color in minus-style. let minus_empty_line_marker_style = Style::from_str( @@ -335,14 +305,6 @@ fn make_hunk_styles<'a>( false, ); - let plus_moved_style = Style::from_str( - &opt.color_moved_plus_style, - Some(plus_style), - None, - true_color, - false, - ); - // The style used to highlight an added empty line when otherwise it would be invisible due to // lack of background color in plus-style. let plus_empty_line_marker_style = Style::from_str( @@ -366,13 +328,11 @@ fn make_hunk_styles<'a>( minus_style, minus_emph_style, minus_non_emph_style, - minus_moved_style, minus_empty_line_marker_style, zero_style, plus_style, plus_emph_style, plus_non_emph_style, - plus_moved_style, plus_empty_line_marker_style, whitespace_error_style, ) diff --git a/src/delta.rs b/src/delta.rs index 5b3de991..409c1df6 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -12,16 +12,16 @@ use crate::features; use crate::format; use crate::paint::Painter; use crate::parse; -use crate::style::DecorationStyle; +use crate::style::{self, DecorationStyle}; #[derive(Clone, Debug, PartialEq)] pub enum State { - CommitMeta, // In commit metadata section - FileMeta, // In diff metadata section, between (possible) commit metadata and first hunk - HunkHeader, // In hunk metadata line - HunkZero, // In hunk; unchanged line - HunkMinus(bool), // In hunk; removed line (is_moved) - HunkPlus(bool), // In hunk; added line (is_moved) + CommitMeta, // In commit metadata section + FileMeta, // In diff metadata section, between (possible) commit metadata and first hunk + HunkHeader, // In hunk metadata line + HunkZero, // In hunk; unchanged line + HunkMinus(Option<String>), // In hunk; removed line (raw_line) + HunkPlus(Option<String>), // In hunk; added line (raw_line) Unknown, } @@ -502,16 +502,28 @@ fn handle_hunk_line( if let State::HunkPlus(_) = state { painter.paint_buffered_minus_and_plus_lines(); } - let is_moved = !config.raw_expected_minus_style.is_applied_to(raw_line); - let state = State::HunkMinus(is_moved); + let state = if style::line_has_style_other_than( + raw_line, + [*style::GIT_DEFAULT_MINUS_STYLE, config.git_minus_style].iter(), + ) { + State::HunkMinus(Some(painter.prepare_raw_line(raw_line))) + } else { + State::HunkMinus(None) + }; painter .minus_lines .push((painter.prepare(&line, true), state.clone())); state } Some('+') => { - let is_moved = !config.raw_expected_plus_style.is_applied_to(raw_line); - let state = State::HunkPlus(is_moved); + let state = if style::line_has_style_other_than( + raw_line, + [*style::GIT_DEFAULT_PLUS_STYLE, config.git_plus_style].iter(), + ) { + State::HunkPlus(Some(painter.prepare_raw_line(raw_line))) + } else { + State::HunkPlus(None) + }; painter .plus_lines .push((painter.prepare(&line, true), state.clone())); diff --git a/src/features/color_moved.rs b/src/features/color_moved.rs deleted file mode 100644 index 2879fb8d..00000000 --- a/src/features/color_moved.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::features::OptionValueFunction; - -pub fn make_feature() -> Vec<(String, OptionValueFunction)> { - builtin_feature!([ - ( - "color-moved", - bool, - None, - _opt => true - ), - ( - "color-moved-minus-style", - bool, - Some("color.diff.oldMoved"), - _opt => "red black" - ), - ( - "color-moved-plus-style", - bool, - Some("color.diff.newMoved"), - _opt => "green black" - ) - ]) -} diff --git a/src/features/mod.rs b/src/features/mod.rs index 89081e4f..67dbd6ba 100644 --- a/src/features/mod.rs +++ b/src/features/mod.rs @@ -27,10 +27,6 @@ type OptionValueFunction = Box<dyn Fn(&cli::Opt, &Option<GitConfig>) -> Provenan pub fn make_builtin_features() -> HashMap<String, BuiltinFeature> { vec![ ( - "color-moved".to_string(), - color_moved::make_feature().into_iter().collect(), - ), - ( "color-only".to_string(), color_only::make_feature().into_iter().collect(), ), @@ -86,7 +82,6 @@ macro_rules! builtin_feature { } } -pub mod color_moved; pub mod color_only; pub mod diff_highlight; pub mod diff_so_fancy; diff --git a/src/features/side_by_side.rs b/src/features/side_by_side.rs index 3feb6e90..5226c97a 100644 --- a/src/features/side_by_side.rs +++ b/src/features/side_by_side.rs @@ -60,11 +60,13 @@ impl SideBySideData { } /// Emit a sequence of minus and plus lines in side-by-side mode. -pub fn paint_minus_and_plus_lines_side_by_side( +pub fn paint_minus_and_plus_lines_side_by_side<'a>( minus_syntax_style_sections: Vec<Vec<(SyntectStyle, &str)>>, minus_diff_style_sections: Vec<Vec<(Style, &str)>>, + minus_states: Vec<&'a State>, plus_syntax_style_sections: Vec<Vec<(SyntectStyle, &str)>>, plus_diff_style_sections: Vec<Vec<(Style, &str)>>, + plus_states: Vec<&'a State>, line_alignment: Vec<(Option<usize>, Option<usize>)>, output_buffer: &mut String, config: &Config, @@ -76,6 +78,10 @@ pub fn paint_minus_and_plus_lines_side_by_side( minus_line_index, &minus_syntax_style_sections, &minus_diff_style_sections, + match minus_line_index { + Some(i) => minus_states[i], + None => &State::HunkMinus(None), + }, line_numbers_data, if config.keep_plus_minus_markers { "-" @@ -89,6 +95,10 @@ pub fn paint_minus_and_plus_lines_side_by_side( plus_line_index, &plus_syntax_style_sections, &plus_diff_style_sections, + match plus_line_index { + Some(i) => plus_states[i], + None => &State::HunkPlus(None), + }, line_numbers_data, if config.keep_plus_minus_markers { "+" @@ -166,10 +176,11 @@ pub fn paint_zero_lines_side_by_side( } } -fn paint_left_panel_minus_line( +fn paint_left_panel_minus_line<'a>( line_index: Option<usize>, syntax_style_sections: &Vec<Vec<(SyntectStyle, &str)>>, diff_style_sections: &Vec<Vec<(Style, &str)>>, + state: &'a State, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, prefix: &str, background_color_extends_to_terminal_width: Option<bool>, @@ -179,7 +190,7 @@ fn paint_left_panel_minus_line( line_index, &syntax_style_sections, &diff_style_sections, - &State::HunkMinus(false), + state, line_numbers_data, PanelSide::Left, prefix, @@ -190,7 +201,7 @@ fn paint_left_panel_minus_line( panel_line_is_empty, line_index, diff_style_sections, - &State::HunkMinus(false), + state, background_color_extends_to_terminal_width, config, ); @@ -198,10 +209,11 @@ fn paint_left_panel_minus_line( panel_line } -fn paint_right_panel_plus_line( +fn paint_right_panel_plus_line<'a>( line_index: Option<usize>, syntax_style_sections: &Vec<Vec<(SyntectStyle, &str)>>, diff_style_sections: &Vec<Vec<(Style, &str)>>, + state: &'a State, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, prefix: &str, background_color_extends_to_terminal_width: Option<bool>, @@ -211,7 +223,7 @@ fn paint_right_panel_plus_line( line_index, &syntax_style_sections, &diff_style_sections, - &State::HunkPlus(false), + state, line_numbers_data, PanelSide::Right, prefix, @@ -222,7 +234,7 @@ fn paint_right_panel_plus_line( panel_line_is_empty, line_index, diff_style_sections, - &State::HunkPlus(false), + state, background_color_extends_to_terminal_width, config, ); @@ -278,7 +290,7 @@ fn get_right_fill_style_for_left_panel( // what this will do is set the line number pair in that function to `(Some(minus_number), None)`, // and then only emit the right field (which has a None number, i.e. blank). However, it will also // increment the minus line number, so we need to knock that back down. -fn paint_minus_or_plus_panel_line( +fn paint_minus_or_plus_panel_line<'a>( line_index: Option<usize>, syntax_style_sections: &Vec<Vec<(SyntectStyle, &str)>>, diff_style_sections: &Vec<Vec<(Style, &str)>>, @@ -298,9 +310,9 @@ fn paint_minus_or_plus_panel_line( state.clone(), ) } else { - let opposite_state = match *state { - State::HunkMinus(x) => State::HunkPlus(x), - State::HunkPlus(x) => State::HunkMinus(x), + let opposite_state = match state { + State::HunkMinus(x) => State::HunkPlus(x.clone()), + State::HunkPlus(x) => State::HunkMinus(x.clone()), _ => unreachable!(), }; ( diff --git a/src/options/set.rs b/src/options/set.rs index e7932e4b..02bd1ea3 100644 --- a/src/options/set.rs +++ b/src/options/set.rs @@ -115,9 +115,6 @@ pub fn set_options( set_options!( [ - color_moved, - color_moved_minus_style, - color_moved_plus_style, color_only, commit_decoration_style, commit_style, @@ -525,12 +522,7 @@ fn set_widths( fn set_git_config_entries(opt: &mut cli::Opt, git_config: &mut git_config::GitConfig) { // Styles - for key in &[ - "color.diff.old", - "color.diff.new", - "color.diff.oldMoved", - "color.diff.newMoved", - ] { + for key in &["color.diff.old", "color.diff.new"] { if let Some(style_string) = git_config.get::<String>(key) { opt.git_config_entries .insert(key.to_string(), GitConfigEntry::Style(style_string)); diff --git a/src/paint.rs b/src/paint.rs index e10b4840..3aed7c21 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -101,6 +101,12 @@ impl<'a> Painter<'a> { } } + /// Remove the initial +/- character of a line that will be emitted unchanged, including any + /// ANSI escape sequences. + pub fn prepare_raw_line(&self, line: &str) -> String { + ansi::ansi_preserving_slice(line, 1) + } + /// Expand tabs as spaces. /// tab_width = 0 is documented to mean do not replace tabs. pub fn expand_tabs<'b, I>(&self, line: I) -> String @@ -117,16 +123,15 @@ impl<'a> Painter<'a> { } pub fn paint_buffered_minus_and_plus_lines(&mut self) { - let __ = false; let minus_line_syntax_style_sections = Self::get_syntax_style_sections_for_lines( &self.minus_lines, - &State::HunkMinus(__), + &State::HunkMinus(None), &mut self.highlighter, self.config, ); let plus_line_syntax_style_sections = Self::get_syntax_style_sections_for_lines( &self.plus_lines, - &State::HunkPlus(__), + &State::HunkPlus(None), &mut self.highlighter, self.config, ); @@ -137,8 +142,10 @@ impl<'a> Painter<'a> { side_by_side::paint_minus_and_plus_lines_side_by_side( minus_line_syntax_style_sections, minus_line_diff_style_sections, + self.minus_lines.iter().map(|(_, state)| state).collect(), plus_line_syntax_style_sections, plus_line_diff_style_sections, + self.plus_lines.iter().map(|(_, state)| state).collect(), line_alignment, &mut self.output_buffer, self.config, @@ -296,11 +303,9 @@ impl<'a> Painter<'a> { // style: for right fill if line contains no emph sections // non_emph_style: for right fill if line contains emph sections let (style, non_emph_style) = match state { - State::HunkMinus(false) => (config.minus_style, config.minus_non_emph_style), + State::HunkMinus(None) => (config.minus_style, config.minus_non_emph_style), State::HunkZero => (config.zero_style, config.zero_style), - State::HunkPlus(false) => (config.plus_style, config.plus_non_emph_style), - State::HunkMinus(true) => (config.minus_moved_style, config.minus_moved_style), - State::HunkPlus(true) => (config.plus_moved_style, config.plus_moved_style), + State::HunkPlus(None) => (config.plus_style, config.plus_non_emph_style), _ => (config.null_style, config.null_style), }; let fill_style = if style_sections_contain_more_than_one_style(diff_sections) { @@ -362,6 +367,21 @@ impl<'a> Painter<'a> { config, )) } + match state { + State::HunkMinus(Some(raw_line)) | State::HunkPlus(Some(raw_line)) => { + // This line has been identified as one which should be emitted unchanged, + // including any ANSI escape sequences that it has. + return ( + format!( + "{}{}", + ansi_term::ANSIStrings(&ansi_strings).to_string(), + raw_line + ), + false, + ); + } + _ => {} + } let mut is_empty = true; for (section_style, mut text) in superimpose_style_sections( syntax_sections, @@ -398,16 +418,17 @@ impl<'a> Painter<'a> { return false; } match state { - State::HunkMinus(_) => { + State::HunkMinus(None) => { config.minus_style.is_syntax_highlighted || config.minus_emph_style.is_syntax_highlighted } State::HunkZero => config.zero_style.is_syntax_highlighted, - State::HunkPlus(_) => { + State::HunkPlus(None) => { config.plus_style.is_syntax_highlighted || config.plus_emph_style.is_syntax_highlighted } State::HunkHeader => true, + State::HunkMinus(Some(_)) | State::HunkPlus(Some(_)) => false, _ => panic!( "should_compute_syntax_highlighting is undefined for state {:?}", state diff --git a/src/parse_style.rs b/src/parse_style.rs index 73a42c65..565defa8 100644 --- a/src/parse_style.rs +++ b/src/parse_style.rs @@ -32,6 +32,10 @@ impl Style { } } + pub fn from_git_str(git_style_string: &str) -> Self { + Self::from_str(git_style_string, None, None, true, false) + } + /// Construct Style but interpreting 'ul', 'box', etc as applying to the decoration style. fn from_str_with_handling_of_special_decoration_attributes( style_string: &str, diff --git a/src/style.rs b/src/style.rs index 51403130..0188922c 100644 --- a/src/style.rs +++ b/src/style.rs @@ -2,7 +2,9 @@ use std::borrow::Cow; use std::fmt; use ansi_term; +use lazy_static::lazy_static; +use crate::ansi; use crate::color; #[derive(Clone, Copy, Debug, PartialEq)] @@ -137,3 +139,91 @@ impl Style { words.join(" ") } } + +lazy_static! { + pub static ref GIT_DEFAULT_MINUS_STYLE: Style = Style { + ansi_term_style: ansi_term::Color::Red.normal(), + ..Style::new() + }; + pub static ref GIT_DEFAULT_PLUS_STYLE: Style = Style { + ansi_term_style: ansi_term::Color::Green.normal(), + ..Style::new() + }; +} + +pub fn line_has_style_other_than<'a>(line: &str, styles: impl Iterator<Item = &'a Style>) -> bool { + if !ansi::string_starts_with_ansi_escape_sequence(line) { + return false; + } + for style in styles { + if style.is_applied_to(line) { + return false; + } + } + return true; +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_is_applied_to() { + assert!(Style::from_git_str(r##"black "#ddeeff""##) + .is_applied_to( + "\x1b[30;48;2;221;238;255m+\x1b[m\x1b[30;48;2;221;238;255m .map(|(_, is_ansi)| is_ansi)\x1b[m\n")) + } + + #[test] + fn test_git_default_styles() { + let minus_line_from_unconfigured_git = "\x1b[31m-____\x1b[m\n"; + let plus_line_from_unconfigured_git = "\x1b[32m+\x1b[m\x1b[32m____\x1b[m\n"; + assert!(GIT_DEFAULT_MINUS_STYLE.is_applied_to(minus_line_from_unconfigured_git)); + assert!(!GIT_DEFAULT_MINUS_STYLE.is_applied_to(plus_line_from_unconfigured_git)); + + assert!(GIT_DEFAULT_PLUS_STYLE.is_applied_to(plus_line_from_unconfigured_git)); + assert!(!GIT_DEFAULT_PLUS_STYLE.is_applied_to(minus_line_from_unconfigured_git)); + } + + #[test] + fn test_line_has_style_other_than() { + let minus_line_from_unconfigured_git = "\x1b[31m-____\x1b[m\n"; + let plus_line_from_unconfigured_git = "\x1b[32m+\x1b[m\x1b[32m____\x1b[m\n"; + + // Unstyled lines should test negative, regardless of supplied styles. + assert!(!line_has_style_other_than("", [].iter())); + assert!(!line_has_style_other_than( + "", + [*GIT_DEFAULT_MINUS_STYLE].iter() + )); + + // Lines from git should test negative when corresponding default is supplied + assert!(!line_has_style_other_than( + minus_line_from_unconfigured_git, + [*GIT_DEFAULT_MINUS_STYLE].iter() + )); + assert!(!line_has_style_other_than( + plus_line_from_unconfigured_git, + [*GIT_DEFAULT_PLUS_STYLE].iter() + )); + + // Styled lines should test positive when unless their style is supplied. + assert!(line_has_style_other_than( + minus_line_from_unconfigured_git, + [*GIT_DEFAULT_PLUS_STYLE].iter() + )); + assert!(line_has_style_other_than( + minus_line_from_unconfigured_git, + [].iter() + )); + assert!(line_has_style_other_than( + plus_line_from_unconfigured_git, + [*GIT_DEFAULT_MINUS_STYLE].iter() + )); + assert!(line_has_style_other_than( + plus_line_from_unconfigured_git, + [].iter() + )); + } +} |