diff options
-rw-r--r-- | Cargo.lock | 42 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | src/cli.rs | 191 | ||||
-rw-r--r-- | src/config.rs | 214 | ||||
-rw-r--r-- | src/delta.rs | 778 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/paint.rs | 95 | ||||
-rw-r--r-- | src/tests/ansi_test_utils.rs | 111 | ||||
-rw-r--r-- | src/tests/integration_test_utils.rs | 68 | ||||
-rw-r--r-- | src/tests/mod.rs | 4 | ||||
-rw-r--r-- | src/tests/test_example_diffs.rs | 575 | ||||
-rw-r--r-- | src/tests/test_hunk_highlighting.rs | 160 |
12 files changed, 1383 insertions, 859 deletions
@@ -14,6 +14,14 @@ dependencies = [ ] [[package]] +name = "ansi-parser" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "ansi_colours" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -122,6 +130,19 @@ dependencies = [ ] [[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit-vec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "bitflags" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -260,6 +281,11 @@ dependencies = [ ] [[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "encode_unicode" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -337,14 +363,17 @@ dependencies = [ name = "git-delta" version = "0.1.1" dependencies = [ + "ansi-parser 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_colours 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "box_drawing 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytelines 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "console 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -397,6 +426,14 @@ dependencies = [ ] [[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "itoa" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -934,6 +971,7 @@ dependencies = [ [metadata] "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +"checksum ansi-parser 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "761ac675f1638a6a49e26f6ac3a4067ca3fefa8029816ae4ef8d3fa3d06a5194" "checksum ansi_colours 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0f302a81afc6a7f4350c04f0ba7cfab529cc009bca3324b3fb5764e6add8b6" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" @@ -946,6 +984,8 @@ dependencies = [ "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" "checksum bindgen 0.50.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb0e5a5f74b2bafe0b39379f616b5975e08bcaca4e779c078d5c31324147e9ba" +"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +"checksum bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum blake2b_simd 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b83b7baab1e671718d78204225800d6b170e648188ac7dc992e9d6bddf87d0c0" "checksum box_drawing 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea27d8d5fd867b17523bf6788b1175fa9867f34669d057e9adaf76e27bcea44b" @@ -963,6 +1003,7 @@ dependencies = [ "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" @@ -978,6 +1019,7 @@ dependencies = [ "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum humantime 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b6c53306532d3c8e8087b44e6580e10db51a023cf9b433cea2ac38066b92da" "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" @@ -17,12 +17,15 @@ path = "src/main.rs" [dependencies] ansi_colours = "1.0.1" +ansi-parser = "0.6.5" ansi_term = "0.12.1" atty = "0.2.14" +bit-set = "0.5.2" box_drawing = "0.1.2" bytelines = "2.2.2" console = "0.11.2" dirs = "2.0" +itertools = "0.9.0" lazy_static = "1.4" regex = "1.2.1" shell-words = "0.1.0" @@ -2,12 +2,14 @@ use std::process; use std::str::FromStr; use std::string::ToString; +use bit_set::BitSet; use console::Term; use structopt::StructOpt; use crate::bat::assets::HighlightingAssets; use crate::bat::output::PagingMode; use crate::config; +use crate::delta::State; use crate::env; use crate::style; @@ -68,21 +70,37 @@ pub struct Opt { pub dark: bool, #[structopt(long = "minus-color")] - /// The background color to use for removed lines. + /// The background color for removed lines. pub minus_color: Option<String>, #[structopt(long = "minus-emph-color")] - /// The background color to use for emphasized sections of removed lines. + /// The background color for emphasized sections of removed lines. pub minus_emph_color: Option<String>, + #[structopt(long = "minus-foreground-color")] + /// The foreground color for removed lines. + pub minus_foreground_color: Option<String>, + + #[structopt(long = "minus-emph-foreground-color")] + /// The foreground color for emphasized sections of removed lines. + pub minus_emph_foreground_color: Option<String>, + #[structopt(long = "plus-color")] - /// The background color to use for added lines. + /// The background color for added lines. pub plus_color: Option<String>, #[structopt(long = "plus-emph-color")] - /// The background color to use for emphasized sections of added lines. + /// The background color for emphasized sections of added lines. pub plus_emph_color: Option<String>, + #[structopt(long = "plus-foreground-color")] + /// Disable syntax highlighting and instead use this foreground color for added lines. + pub plus_foreground_color: Option<String>, + + #[structopt(long = "plus-emph-foreground-color")] + /// Disable syntax highlighting and instead use this foreground color for emphasized sections of added lines. + pub plus_emph_foreground_color: Option<String>, + #[structopt(long = "theme", env = "BAT_THEME")] /// The code syntax highlighting theme to use. Use --theme=none to disable syntax highlighting. /// If the theme is not set using this option, it will be taken from the BAT_THEME environment @@ -91,10 +109,15 @@ pub struct Opt { /// --file-color, --hunk-color to configure the colors of other parts of the diff output. pub theme: Option<String>, + /// A string consisting only of the characters '-', '0', '+', specifying + /// which of the 3 diff hunk line-types (removed, unchanged, added) should + /// be syntax-highlighted. "all" and "none" are also valid values. + #[structopt(long = "syntax-highlight", default_value = "0+")] + pub lines_to_be_syntax_highlighted: String, + #[structopt(long = "highlight-removed")] - /// Apply syntax highlighting to removed lines. The default is to - /// apply syntax highlighting to unchanged and new lines only. - pub highlight_removed: bool, + /// DEPRECATED: use --syntax-highlight. + pub highlight_minus_lines: bool, #[structopt(long = "color-only")] /// Do not alter the input in any way other than applying colors. Equivalent to @@ -296,6 +319,7 @@ pub fn process_command_line_arguments<'a>( true_color, available_terminal_width, paging_mode, + get_lines_to_be_syntax_highlighted(opt), ) } @@ -304,3 +328,156 @@ fn is_truecolor_terminal() -> bool { .map(|colorterm| colorterm == "truecolor" || colorterm == "24bit") .unwrap_or(false) } + +fn get_lines_to_be_syntax_highlighted(opt: &Opt) -> BitSet { + if opt.highlight_minus_lines { + eprintln!("--highlight-removed is deprecated: use --syntax-highlight."); + } + + let syntax_highlight_lines = match opt.lines_to_be_syntax_highlighted.to_lowercase().as_ref() { + "none" => "", + // This is the default value of the new option: honor the deprecated option if it has been used. + "0+" => match opt.highlight_minus_lines { + true => "-0+", + false => "0+", + }, + "all" => "-0+", + s => s, + } + .to_string(); + + let mut lines_to_be_syntax_highlighted = BitSet::new(); + for line_type in syntax_highlight_lines.chars() { + lines_to_be_syntax_highlighted.insert(match line_type { + '-' => State::HunkMinus as usize, + '0' => State::HunkZero as usize, + '+' => State::HunkPlus as usize, + s => { + eprintln!("Invalid --syntax-highlight value: {}. Valid characters are \"-\", \"0\", \"+\".", s); + process::exit(1); + } + }); + } + lines_to_be_syntax_highlighted +} + +#[cfg(test)] +mod tests { + use std::env; + + use crate::bat::assets::HighlightingAssets; + use crate::cli; + use crate::style; + use crate::tests::integration_test_utils::integration_test_utils; + + #[test] + fn test_theme_selection() { + #[derive(PartialEq)] + enum Mode { + Light, + Dark, + }; + let assets = HighlightingAssets::new(); + for ( + theme_option, + bat_theme_env_var, + mode_option, // (--light, --dark) + expected_theme, + expected_mode, + ) in vec![ + (None, "", None, style::DEFAULT_DARK_THEME, Mode::Dark), + (Some("GitHub".to_string()), "", None, "GitHub", Mode::Light), + ( + Some("GitHub".to_string()), + "1337", + None, + "GitHub", + Mode::Light, + ), + (None, "1337", None, "1337", Mode::Dark), + ( + None, + "<not set>", + None, + style::DEFAULT_DARK_THEME, + Mode::Dark, + ), + ( + None, + "", + Some(Mode::Light), + style::DEFAULT_LIGHT_THEME, + Mode::Light, + ), + ( + None, + "", + Some(Mode::Dark), + style::DEFAULT_DARK_THEME, + Mode::Dark, + ), + ( + None, + "<@@@@@>", + Some(Mode::Light), + style::DEFAULT_LIGHT_THEME, + Mode::Light, + ), + (None, "1337", Some(Mode::Light), "1337", Mode::Light), + (Some("none".to_string()), "", None, "none", Mode::Dark), + ( + Some("None".to_string()), + "", + Some(Mode::Light), + "None", + Mode::Light, + ), + ] { + if bat_theme_env_var == "<not set>" { + env::remove_var("BAT_THEME") + } else { + env::set_var("BAT_THEME", bat_theme_env_var); + } + let is_true_color = true; + let mut options = integration_test_utils::get_command_line_options(); + options.theme = theme_option; + match mode_option { + Some(Mode::Light) => { + options.light = true; + options.dark = false; + } + Some(Mode::Dark) => { + options.light = false; + options.dark = true; + } + None => { + options.light = false; + options.dark = false; + } + } + let config = cli::process_command_line_arguments(&assets, &options); + assert_eq!(config.theme_name, expected_theme); + if style::is_no_syntax_highlighting_theme_name(expected_theme) { + assert!(config.theme.is_none()) + } else { + assert_eq!(config.theme.unwrap().name.as_ref().unwrap(), expected_theme); + } + assert_eq!( + config.minus_style_modifier.background.unwrap(), + style::get_minus_color_default(expected_mode == Mode::Light, is_true_color) + ); + assert_eq!( + config.minus_emph_style_modifier.background.unwrap(), + style::get_minus_emph_color_default(expected_mode == Mode::Light, is_true_color) + ); + assert_eq!( + config.plus_style_modifier.background.unwrap(), + style::get_plus_color_default(expected_mode == Mode::Light, is_true_color) + ); + assert_eq!( + config.plus_emph_style_modifier.background.unwrap(), + style::get_plus_emph_color_default(expected_mode == Mode::Light, is_true_color) + ); + } + } +} diff --git a/src/config.rs b/src/config.rs index 9fc757d3..f9e42196 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,11 +1,13 @@ use std::process; use std::str::FromStr; +use bit_set::BitSet; use syntect::highlighting::{Color, Style, StyleModifier, Theme, ThemeSet}; use syntect::parsing::SyntaxSet; use crate::bat::output::PagingMode; use crate::cli; +use crate::delta::State; use crate::env; use crate::paint; use crate::style; @@ -15,13 +17,15 @@ pub struct Config<'a> { pub theme_name: String, pub max_line_distance: f64, pub max_line_distance_for_naively_paired_lines: f64, + pub minus_foreground_style_modifier: Option<StyleModifier>, pub minus_style_modifier: StyleModifier, pub minus_emph_style_modifier: StyleModifier, + pub plus_foreground_style_modifier: Option<StyleModifier>, pub plus_style_modifier: StyleModifier, pub plus_emph_style_modifier: StyleModifier, pub minus_line_marker: &'a str, pub plus_line_marker: &'a str, - pub highlight_removed: bool, + pub lines_to_be_syntax_highlighted: BitSet, pub commit_style: cli::SectionStyle, pub commit_color: Color, pub file_style: cli::SectionStyle, @@ -38,6 +42,37 @@ pub struct Config<'a> { pub paging_mode: PagingMode, } +#[allow(dead_code)] +pub enum ColorLayer { + Background, + Foreground, +} +use ColorLayer::*; +use State::*; + +impl<'a> Config<'a> { + #[allow(dead_code)] + pub fn get_color(&self, state: &State, layer: ColorLayer) -> Option<Color> { + let modifier = match state { + HunkMinus => Some(self.minus_style_modifier), + HunkZero => None, + HunkPlus => Some(self.plus_style_modifier), + _ => panic!("Invalid"), + }; + match (modifier, layer) { + (Some(modifier), Background) => modifier.background, + (Some(modifier), Foreground) => modifier.foreground, + (None, _) => None, + } + } + + #[allow(dead_code)] + pub fn should_syntax_highlight(&self, state: &State) -> bool { + self.lines_to_be_syntax_highlighted + .contains((*state).clone() as usize) + } +} + pub fn get_config<'a>( opt: &'a cli::Opt, syntax_set: &'a SyntaxSet, @@ -45,6 +80,7 @@ pub fn get_config<'a>( true_color: bool, terminal_width: usize, paging_mode: PagingMode, + lines_to_be_syntax_highlighted: BitSet, ) -> Config<'a> { // Implement --color-only let keep_plus_minus_markers = if opt.color_only { @@ -54,20 +90,14 @@ pub fn get_config<'a>( }; let background_color_extends_to_terminal_width = opt.width != Some("variable".to_string()); let tab_width = if opt.color_only { 0 } else { opt.tab_width }; - let commit_style = if opt.color_only { - cli::SectionStyle::Plain - } else { - opt.commit_style - }; - let file_style = if opt.color_only { - cli::SectionStyle::Plain + let (commit_style, file_style, hunk_style) = if opt.color_only { + ( + cli::SectionStyle::Plain, + cli::SectionStyle::Plain, + cli::SectionStyle::Plain, + ) } else { - opt.file_style - }; - let hunk_style = if opt.color_only { - cli::SectionStyle::Plain - } else { - opt.hunk_style + (opt.commit_style, opt.file_style, opt.hunk_style) }; let theme_name_from_bat_pager = env::get_env_var("BAT_THEME"); @@ -78,56 +108,26 @@ pub fn get_config<'a>( theme_set, ); + let ( + minus_style_modifier, + plus_style_modifier, + minus_emph_style_modifier, + plus_emph_style_modifier, + minus_foreground_style_modifier, + plus_foreground_style_modifier, + ) = make_style_modifiers( + &lines_to_be_syntax_highlighted, + opt, + is_light_mode, + true_color, + ); + let theme = if style::is_no_syntax_highlighting_theme_name(&theme_name) { None } else { Some(&theme_set.themes[&theme_name]) }; - let minus_style_modifier = StyleModifier { - background: Some(color_from_rgb_or_ansi_code_with_default( - opt.minus_color.as_ref(), - style::get_minus_color_default(is_light_mode, true_color), - )), - foreground: if opt.highlight_removed { - None - } else { - Some(style::NO_COLOR) - }, - font_style: None, - }; - - let minus_emph_style_modifier = StyleModifier { - background: Some(color_from_rgb_or_ansi_code_with_default( - opt.minus_emph_color.as_ref(), - style::get_minus_emph_color_default(is_light_mode, true_color), - )), - foreground: if opt.highlight_removed { - None - } else { - Some(style::NO_COLOR) - }, - font_style: None, - }; - - let plus_style_modifier = StyleModifier { - background: Some(color_from_rgb_or_ansi_code_with_default( - opt.plus_color.as_ref(), - style::get_plus_color_default(is_light_mode, true_color), - )), - foreground: None, - font_style: None, - }; - - let plus_emph_style_modifier = StyleModifier { - background: Some(color_from_rgb_or_ansi_code_with_default( - opt.plus_emph_color.as_ref(), - style::get_plus_emph_color_default(is_light_mode, true_color), - )), - foreground: None, - font_style: None, - }; - let minus_line_marker = if keep_plus_minus_markers { "-" } else { " " }; let plus_line_marker = if keep_plus_minus_markers { "+" } else { " " }; @@ -141,11 +141,13 @@ pub fn get_config<'a>( theme_name, max_line_distance: opt.max_line_distance, max_line_distance_for_naively_paired_lines, + minus_foreground_style_modifier, minus_style_modifier, minus_emph_style_modifier, + plus_foreground_style_modifier, plus_style_modifier, plus_emph_style_modifier, - highlight_removed: opt.highlight_removed, + lines_to_be_syntax_highlighted, minus_line_marker, plus_line_marker, commit_style, @@ -220,6 +222,100 @@ fn valid_theme_name_or_none(theme_name: Option<&String>, theme_set: &ThemeSet) - } } +fn make_style_modifiers<'a>( + lines_to_be_syntax_highlighted: &BitSet, + opt: &'a cli::Opt, + is_light_mode: bool, + true_color: bool, +) -> ( + StyleModifier, + StyleModifier, + StyleModifier, + StyleModifier, + Option<StyleModifier>, + Option<StyleModifier>, +) { + // Background styles + let minus_background_style_modifier = StyleModifier { + background: Some(color_from_rgb_or_ansi_code_with_default( + opt.minus_color.as_ref(), + style::get_minus_color_default(is_light_mode, true_color), + )), + foreground: if lines_to_be_syntax_highlighted.contains(State::HunkMinus as usize) { + None + } else { + Some(style::NO_COLOR) + }, + font_style: None, + }; + + let plus_background_style_modifier = StyleModifier { + background: Some(color_from_rgb_or_ansi_code_with_default( + opt.plus_color.as_ref(), + style::get_plus_color_default(is_light_mode, true_color), + )), + foreground: None, + font_style: None, + }; + + // Background emph styles + let minus_background_emph_style_modifier = StyleModifier { + background: Some(color_from_rgb_or_ansi_code_with_default( + opt.minus_emph_color.as_ref(), + style::get_minus_emph_color_default(is_light_mode, true_color), + )), + foreground: if lines_to_be_syntax_highlighted.contains(State::HunkMinus as usize) { + None + } else { + Some(style::NO_COLOR) + }, + font_style: None, + }; + + let plus_background_emph_style_modifier = StyleModifier { + background: Some(color_from_rgb_or_ansi_code_with_default( + opt.plus_emph_color.as_ref(), + style::get_plus_emph_color_default(is_light_mode, true_color), + )), + foreground: None, + font_style: None, + }; + + // Foreground styles (these replace syntax highlighting). + let minus_foreground_style_modifier = match opt.minus_foreground_color.is_some() { + true => Some(StyleModifier { + background: None, + foreground: Some(color_from_rgb_or_ansi_code_with_default( + opt.minus_foreground_color.as_ref(), + style::get_minus_emph_color_default(is_light_mode, true_color), + )), + font_style: None, + }), + false => None, + }; + + let plus_foreground_style_modifier = match opt.plus_foreground_color.is_some() { + true => Some(StyleModifier { + background: None, + foreground: Some(color_from_rgb_or_ansi_code_with_default( + opt.plus_foreground_color.as_ref(), + style::get_plus_emph_color_default(is_light_mode, true_color), + )), + font_style: None, + }), + false => None, + }; + + ( + minus_background_style_modifier, + plus_background_style_modifier, + minus_background_emph_style_modifier, + plus_background_emph_style_modifier, + minus_foreground_style_modifier, + plus_foreground_style_modifier, + ) +} + fn color_from_rgb_or_ansi_code(s: &str) -> Color { let die = || { eprintln!("Invalid color: {}", s); diff --git a/src/delta.rs b/src/delta.rs index 05fc0760..bcbf3e07 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -13,7 +13,7 @@ use crate::paint::{self, Painter}; use crate::parse; use crate::style; -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum State { CommitMeta, // In commit metadata section FileMeta, // In diff metadata section, between (possible) commit metadata and first hunk @@ -262,8 +262,8 @@ fn handle_hunk_meta_line( let syntax_style_sections = Painter::get_line_syntax_style_sections( &code_fragment, &mut painter.highlighter, + None, &painter.config, - true, ); Painter::paint_lines( vec![syntax_style_sections], @@ -330,25 +330,33 @@ fn handle_hunk_line( State::HunkPlus } Some(' ') => { + let state = State::HunkZero; let prefix = if line.is_empty() { "" } else { &line[..1] }; painter.paint_buffered_lines(); let line = prepare(&line, true, config); - let syntax_style_sections = Painter::get_line_syntax_style_sections( - &line, - &mut painter.highlighter, - &painter.config, - true, - ); + let syntax_style_sections = if config.should_syntax_highlight(&state) { + Painter::get_line_syntax_style_sections( + &line, + &mut painter.highlighter, + None, + &painter.config, + ) + } else { + vec![(style::get_no_style(), line.as_str())] + }; + let diff_style_sections = + vec![(style::NO_BACKGROUND_COLOR_STYLE_MODIFIER, line.as_str())]; |