summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2020-05-17 18:19:57 -0400
committerDan Davison <dandavison7@gmail.com>2020-05-22 13:57:52 -0400
commitf08effc46de52655c8431498c6bc214c0eda30e5 (patch)
tree4fd806d5199b42f3ce77c87050795f59408b1a24 /src
parent54da480bd0465daf698f2566c454e0921110d69c (diff)
Introduce style strings to replace color options
https://git-scm.com/docs/git-config#Documentation/git-config.txt-color - Support "syntax" pseudo foreground color - Delete the --syntax-highlight CLI option This was never released.
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs202
-rw-r--r--src/config.rs109
-rw-r--r--src/paint.rs11
-rw-r--r--src/style.rs12
-rw-r--r--src/tests/integration_test_utils.rs17
-rw-r--r--src/tests/test_hunk_highlighting.rs60
6 files changed, 231 insertions, 180 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 0d915ad5..ea105a62 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -2,7 +2,6 @@ use std::process;
use std::str::FromStr;
use std::string::ToString;
-use bit_set::BitSet;
use console::Term;
use structopt::clap::AppSettings::{ColorAlways, ColoredHelp, DeriveDisplayOrder};
use structopt::StructOpt;
@@ -10,7 +9,6 @@ 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;
@@ -73,37 +71,37 @@ pub struct Opt {
#[structopt(long = "dark")]
pub dark: bool,
+ #[structopt(long = "minus-style")]
+ /// The style for removed lines.
+ pub minus_style: Option<String>,
+
+ #[structopt(long = "minus-emph-style")]
+ /// The style for emphasized sections of removed lines.
+ pub minus_emph_style: Option<String>,
+
+ #[structopt(long = "plus-style")]
+ /// The style for removed lines.
+ pub plus_style: Option<String>,
+
+ #[structopt(long = "plus-emph-style")]
+ /// The style for emphasized sections of removed lines.
+ pub plus_emph_style: Option<String>,
+
#[structopt(long = "minus-color")]
/// The background color for removed lines.
- pub minus_color: Option<String>,
+ pub _deprecated_minus_color: Option<String>,
#[structopt(long = "minus-emph-color")]
/// 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>,
+ pub _deprecated_minus_emph_color: Option<String>,
#[structopt(long = "plus-color")]
/// The background color for added lines.
- pub plus_color: Option<String>,
+ pub _deprecated_plus_color: Option<String>,
#[structopt(long = "plus-emph-color")]
/// 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>,
+ pub _deprecated_plus_emph_color: Option<String>,
#[structopt(long = "theme", env = "BAT_THEME")]
/// The code syntax highlighting theme to use. Use --theme=none to disable syntax highlighting.
@@ -113,14 +111,8 @@ 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")]
- /// DEPRECATED: use --syntax-highlight.
+ /// DEPRECATED: supply 'syntax' as the foreground color in --minus-style.
pub highlight_minus_lines: bool,
#[structopt(long = "color-only")]
@@ -249,41 +241,14 @@ impl ToString for Error {
}
}
-pub fn process_command_line_arguments<'a>(opt: Opt) -> config::Config<'a> {
+pub fn process_command_line_arguments<'a>(mut opt: Opt) -> config::Config<'a> {
let assets = HighlightingAssets::new();
- if opt.light && opt.dark {
- eprintln!("--light and --dark cannot be used together.");
- process::exit(1);
- }
- match &opt.theme {
- Some(theme) if !style::is_no_syntax_highlighting_theme_name(&theme) => {
- if !assets.theme_set.themes.contains_key(theme.as_str()) {
- eprintln!("Invalid theme: '{}'", theme);
- process::exit(1);
- }
- let is_light_theme = style::is_light_theme(&theme);
- if is_light_theme && opt.dark {
- eprintln!(
- "{} is a light theme, but you supplied --dark. \
- If you use --theme, you do not need to supply --light or --dark.",
- theme
- );
- process::exit(1);
- } else if !is_light_theme && opt.light {
- eprintln!(
- "{} is a dark theme, but you supplied --light. \
- If you use --theme, you do not need to supply --light or --dark.",
- theme
- );
- process::exit(1);
- }
- }
- _ => (),
- };
+ _check_validity(&opt, &assets);
- // We do not use the full width, in case `less --status-column` is in effect. See #41 and #10.
+ _apply_rewrite_rules(&mut opt);
+ // We do not use the full width, in case `less --status-column` is in effect. See #41 and #10.
// TODO: There seems to be some confusion in the accounting: we are actually leaving 2
// characters unused for less at the right edge of the terminal, despite the subtraction of 1
// here.
@@ -295,7 +260,7 @@ pub fn process_command_line_arguments<'a>(opt: Opt) -> config::Config<'a> {
"auto" => PagingMode::QuitIfOneScreen,
_ => {
eprintln!(
- "Invalid paging value: {} (valid values are \"always\", \"never\", and \"auto\")",
+ "Invalid value for --paging option: {} (valid values are \"always\", \"never\", and \"auto\")",
opt.paging_mode
);
process::exit(1);
@@ -315,8 +280,6 @@ pub fn process_command_line_arguments<'a>(opt: Opt) -> config::Config<'a> {
}
};
- let lines_to_be_syntax_highlighted = get_lines_to_be_syntax_highlighted(&opt);
-
config::get_config(
opt,
assets.syntax_set,
@@ -324,52 +287,89 @@ pub fn process_command_line_arguments<'a>(opt: Opt) -> config::Config<'a> {
true_color,
available_terminal_width,
paging_mode,
- lines_to_be_syntax_highlighted,
)
}
-fn is_truecolor_terminal() -> bool {
- env::get_env_var("COLORTERM")
- .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,
+fn _check_validity(opt: &Opt, assets: &HighlightingAssets) {
+ if opt.light && opt.dark {
+ eprintln!("--light and --dark cannot be used together.");
+ process::exit(1);
}
- .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);
+ if let Some(ref theme) = opt.theme {
+ if !style::is_no_syntax_highlighting_theme_name(&theme) {
+ if !assets.theme_set.themes.contains_key(theme.as_str()) {
+ eprintln!("Invalid theme: '{}'", theme);
process::exit(1);
}
- });
- }
- if opt.minus_foreground_color.is_some() || opt.minus_emph_foreground_color.is_some() {
- lines_to_be_syntax_highlighted.remove(State::HunkMinus as usize);
+ let is_light_theme = style::is_light_theme(&theme);
+ if is_light_theme && opt.dark {
+ eprintln!(
+ "{} is a light theme, but you supplied --dark. \
+ If you use --theme, you do not need to supply --light or --dark.",
+ theme
+ );
+ process::exit(1);
+ } else if !is_light_theme && opt.light {
+ eprintln!(
+ "{} is a dark theme, but you supplied --light. \
+ If you use --theme, you do not need to supply --light or --dark.",
+ theme
+ );
+ process::exit(1);
+ }
+ }
}
- if opt.plus_foreground_color.is_some() || opt.plus_emph_foreground_color.is_some() {
- lines_to_be_syntax_highlighted.remove(State::HunkPlus as usize);
+}
+
+fn _apply_rewrite_rules(opt: &mut Opt) {
+ opt.minus_style = _make_style_string(
+ opt.minus_style.as_deref(),
+ opt._deprecated_minus_color.as_deref(),
+ "minus",
+ );
+ opt.minus_emph_style = _make_style_string(
+ opt.minus_emph_style.as_deref(),
+ opt._deprecated_minus_emph_color.as_deref(),
+ "minus-emph",
+ );
+ opt.plus_style = _make_style_string(
+ opt.plus_style.as_deref(),
+ opt._deprecated_plus_color.as_deref(),
+ "plus",
+ );
+ opt.plus_emph_style = _make_style_string(
+ opt.plus_emph_style.as_deref(),
+ opt._deprecated_plus_emph_color.as_deref(),
+ "plus-emph",
+ );
+}
+
+pub fn _make_style_string(
+ style: Option<&str>,
+ background_color: Option<&str>,
+ element_name: &str,
+) -> Option<String> {
+ match (style, background_color) {
+ (_, None) => style.map(str::to_string),
+ (None, Some(background_color)) => Some(format!("syntax {}", background_color)),
+ (Some(_), Some(_)) => {
+ eprintln!(
+ "--{name}-color cannot be used with --{name}-style. \
+ Use --{name}-style=\"fg bg attr1 attr2 ...\" to set \
+ foreground color, background color, and style attributes. \
+ --{name}-color can only be used to set the background color. \
+ (It is still available for backwards-compatibility.)",
+ name = element_name,
+ );
+ process::exit(1);
+ }
}
- lines_to_be_syntax_highlighted
+}
+
+fn is_truecolor_terminal() -> bool {
+ env::get_env_var("COLORTERM")
+ .map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
+ .unwrap_or(false)
}
#[cfg(test)]
diff --git a/src/config.rs b/src/config.rs
index a98ee11c..773d9a61 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,8 +1,7 @@
use std::process;
use std::str::FromStr;
-use bit_set::BitSet;
-use syntect::highlighting::{Color, Style, StyleModifier, Theme, ThemeSet};
+use syntect::highlighting::{Color, FontStyle, Style, StyleModifier, Theme, ThemeSet};
use syntect::parsing::SyntaxSet;
use crate::bat::output::PagingMode;
@@ -24,7 +23,6 @@ pub struct Config<'a> {
pub plus_emph_style_modifier: StyleModifier,
pub minus_line_marker: &'a str,
pub plus_line_marker: &'a str,
- pub lines_to_be_syntax_highlighted: BitSet,
pub commit_style: cli::SectionStyle,
pub commit_color: Color,
pub file_style: cli::SectionStyle,
@@ -50,25 +48,29 @@ 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 {
+ pub fn get_style(&self, state: &State) -> Option<StyleModifier> {
+ 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 get_color(&self, state: &State, layer: ColorLayer) -> Option<Color> {
+ match (self.get_style(state), layer) {
+ (Some(style), Background) => style.background,
+ (Some(style), Foreground) => style.foreground,
+ (None, _) => None,
+ }
+ }
+
pub fn should_syntax_highlight(&self, state: &State) -> bool {
- self.lines_to_be_syntax_highlighted
- .contains((*state).clone() as usize)
+ match self.get_style(state) {
+ Some(style) => style.foreground == Some(style::SYNTAX_HIGHLIGHTING_COLOR),
+ None => false,
+ }
}
}
@@ -79,7 +81,6 @@ 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 {
@@ -139,7 +140,6 @@ pub fn get_config<'a>(
minus_emph_style_modifier,
plus_style_modifier,
plus_emph_style_modifier,
- lines_to_be_syntax_highlighted,
minus_line_marker,
plus_line_marker,
commit_style,
@@ -220,52 +220,98 @@ fn make_styles<'a>(
true_color: bool,
) -> (StyleModifier, StyleModifier, StyleModifier, StyleModifier) {
let minus_style = make_style(
- opt.minus_color.as_deref(),
+ opt.minus_style.as_deref(),
Some(style::get_minus_color_default(is_light_mode, true_color)),
- opt.minus_foreground_color.as_deref(),
None,
);
let minus_emph_style = make_style(
- opt.minus_emph_color.as_deref(),
+ opt.minus_emph_style.as_deref(),
Some(style::get_minus_emph_color_default(
is_light_mode,
true_color,
)),
- opt.minus_emph_foreground_color.as_deref(),
minus_style.foreground,
);
let plus_style = make_style(
- opt.plus_color.as_deref(),
+ opt.plus_style.as_deref(),
Some(style::get_plus_color_default(is_light_mode, true_color)),
- opt.plus_foreground_color.as_deref(),
None,
);
let plus_emph_style = make_style(
- opt.plus_emph_color.as_deref(),
+ opt.plus_emph_style.as_deref(),
Some(style::get_plus_emph_color_default(
is_light_mode,
true_color,
)),
- opt.plus_emph_foreground_color.as_deref(),
plus_style.foreground,
);
(minus_style, minus_emph_style, plus_style, plus_emph_style)
}
+/// Construct syntect StyleModifier from background and foreground strings,
+/// together with their defaults. The background string is handled specially in
+/// that it may be a single color, or it may be a space-separated "style string".
fn make_style(
- background: Option<&str>,
+ style_string: Option<&str>,
background_default: Option<Color>,
- foreground: Option<&str>,
foreground_default: Option<Color>,
) -> StyleModifier {
+ if let Some(s) = style_string {
+ parse_style_string(s, background_default, foreground_default)
+ } else {
+ StyleModifier {
+ background: background_default,
+ foreground: foreground_default,
+ font_style: None,
+ }
+ }
+}
+
+fn parse_style_string(
+ style_string: &str,
+ background_default: Option<Color>,
+ foreground_default: Option<Color>,
+) -> StyleModifier {
+ let mut foreground = foreground_default;
+ let mut background = background_default;
+ let mut font_style = FontStyle::empty();
+ let mut seen_foreground = false;
+ let mut seen_background = false;
+ for s in style_string.to_lowercase().split_whitespace() {
+ if s == "bold" {
+ font_style.set(FontStyle::BOLD, true)
+ } else if s == "italic" {
+ font_style.set(FontStyle::ITALIC, true)
+ } else if s == "underline" {
+ font_style.set(FontStyle::UNDERLINE, true)
+ } else if !seen_foreground {
+ foreground = color_from_rgb_or_ansi_code_with_default(Some(s), None);
+ seen_foreground = true;
+ } else if !seen_background {
+ background = color_from_rgb_or_ansi_code_with_default(Some(s), None);
+ seen_background = true;
+ } else {
+ eprintln!(
+ "Invalid style string: {}.\n\
+ A style string may contain a foreground color string. \
+ If it contains a foreground color string it may subsequently \
+ contain a background color string. Font style attributes \
+ 'bold', 'italic', and 'underline' may occur in any position. \
+ All strings must be separated by spaces. \
+ See delta --help for how to specify colors.",
+ s
+ );
+ process::exit(1);
+ }
+ }
StyleModifier {
- background: color_from_rgb_or_ansi_code_with_default(background, background_default),
- foreground: color_from_rgb_or_ansi_code_with_default(foreground, foreground_default),
- font_style: None,
+ background,
+ foreground,
+ font_style: Some(font_style),
}
}
@@ -289,9 +335,10 @@ fn color_from_rgb_or_ansi_code_with_default(
arg: Option<&str>,
default: Option<Color>,
) -> Option<Color> {
- match arg {
- Some(string) if string.to_lowercase() == "none" => None,
- Some(string) => Some(color_from_rgb_or_ansi_code(&string)),
+ match arg.map(str::to_lowercase) {
+ Some(s) if s == "none" => None,
+ Some(s) if s == "syntax" => Some(style::SYNTAX_HIGHLIGHTING_COLOR),
+ Some(s) => Some(color_from_rgb_or_ansi_code(&s)),
None => default,
}
}
diff --git a/src/paint.rs b/src/paint.rs
index 2b9969eb..f3ebf9de 100644
--- a/src/paint.rs
+++ b/src/paint.rs
@@ -3,7 +3,7 @@ use std::str::FromStr;
use ansi_term;
use syntect::easy::HighlightLines;
-use syntect::highlighting::{Color, Style, StyleModifier};
+use syntect::highlighting::{Color, FontStyle, Style, StyleModifier};
use syntect::parsing::{SyntaxReference, SyntaxSet};
use crate::bat::terminal::to_ansi_color;
@@ -223,6 +223,15 @@ pub fn to_ansi_style(style: Style, true_color: bool) -> ansi_term::Style {
if style.foreground != style::NO_COLOR {
ansi_style = ansi_style.fg(to_ansi_color(style.foreground, true_color));
}
+ if style.font_style.contains(FontStyle::BOLD) {
+ ansi_style.is_bold = true;
+ }
+ if style.font_style.contains(FontStyle::ITALIC) {
+ ansi_style.is_italic = true;
+ }
+ if style.font_style.contains(FontStyle::UNDERLINE) {
+ ansi_style.is_underline = true;
+ }
ansi_style
}
diff --git a/src/style.rs b/src/style.rs
index 80256805..339e3f02 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -170,6 +170,18 @@ const DARK_THEME_PLUS_EMPH_COLOR_256: Color = Color {
/// A special color to specify that no color escape codes should be emitted.
pub const NO_COLOR: Color = Color::BLACK;
+/// A special color value to signify that the foreground color of a style should be derived from
+/// syntax highlighting.
+// alpha is 0, which is how the 256-palette colors are encoded (see bat::terminal::to_ansi_color).
+// So if painted, this would be black. However, the presence of a non-zero bit in the blue channel
+// distinguishes this from any 256-palette color.
+pub const SYNTAX_HIGHLIGHTING_COLOR: Color = Color {
+ r: 0x00,
+ g: 0x00,
+ b: 0x01,
+ a: 0x00,
+};
+
pub fn get_no_style() -> Style {
Style {
foreground: NO_COLOR,
diff --git a/src/tests/integration_test_utils.rs b/src/tests/integration_test_utils.rs
index 6836d85d..cc4dc22b 100644
--- a/src/tests/integration_test_utils.rs
+++ b/src/tests/integration_test_utils.rs
@@ -12,18 +12,17 @@ pub mod integration_test_utils {
cli::Opt {
light: false,
dark: false,
- minus_color: None,
- minus_emph_color: None,
- minus_foreground_color: None,
- minus_emph_foreground_color: None,
- plus_color: None,
- plus_emph_color: None,
- plus_foreground_color: None,
- plus_emph_foreground_color: None,
+ minus_style: None,
+ minus_emph_style: None,
+ plus_style: None,
+ plus_emph_style: None,
+ _deprecated_minus_color: None,
+ _deprecated_minus_emph_color: None,
+ _deprecated_plus_color: None,
+ _deprecated_plus_emph_color: None,
color_only: false,
keep_plus_minus_markers: false,
theme: None,
- lines_to_be_syntax_highlighted: "0+".to_string(),
highlight_minus_lines: false,
commit_style: cli::SectionStyle::Plain,
commit_color: "Yellow".to_string(),
diff --git a/src/tests/test_hunk_highlighting.rs b/src/tests/test_hunk_highlighting.rs
index 730218c0..8c2bbe35 100644
--- a/src/tests/test_hunk_highlighting.rs
+++ b/src/tests/test_hunk_highlighting.rs
@@ -16,45 +16,29 @@ mod tests {
let mut options = integration_test_utils::get_command_line_options();
options.theme = Some("GitHub".to_string());
options.max_line_distance = 1.0;
- options.minus_emph_color = Some("#ffa0a0".to_string());
- options.plus_emph_color = Some("#80ef80".to_string());
- for minus_foreground_color in vec![None, Some("green".to_string())] {
- options.minus_foreground_color = minus_foreground_color;
- for minus_emph_foreground_color in vec![None, Some("#80ef80".to_string())] {
- options.minus_emph_foreground_color = minus_emph_foreground_color;
- for plus_foreground_color in vec![None, Some("red".to_string())] {
- options.plus_foreground_color = plus_foreground_color;
- for plus_emph_foreground_color in vec![None, Some("#ffa0a0".to_string())] {
- options.plus_emph_foreground_color = plus_emph_foreground_color;
- for lines_to_be_syntax_highlighted in vec!["none", "all"] {
- options.lines_to_be_syntax_highlighted =
- lines_to_be_syntax_highlighted.to_string();
- if VERBOSE {
- println!();
- print!(
- " --syntax-highlight {:?}",
- options.lines_to_be_syntax_highlighted
- );
- print!(
- " --minus-foreground-color {:?}",
- options.minus_foreground_color
- );
- print!(
- " --minus-emph-foreground-color {:?}",
- options.minus_emph_foreground_color
- );
- print!(
- " --plus-foreground-color {:?}",
- options.plus_foreground_color
- );
- print!(
- " --plus-emph-foreground-color {:?}",
- options.plus_emph_foreground_color
- );
- println!();
- }
- _do_hunk_color_test(options.clone());
+ let minus_emph_background = "#ffa0a0";
+ let plus_emph_background = "#80ef80";
+ for minus_foreground in vec!["none", "green"] {
+ for minus_emph_foreground in vec!["none", "#80ef80"] {
+ for plus_foreground in vec!["none", "red"] {
+ for plus_emph_foreground in vec!["none", "#ffa0a0"] {
+ options.minus_style = Some(minus_foreground.to_string());
+ options.minus_emph_style = Some(format!(
+ "{} {}",
+ minus_emph_foreground, minus_emph_background
+ ));
+ options.plus_style = Some(plus_foreground.to_string());
+ options.plus_emph_style =
+ Some(format!("{} {}", plus_emph_foreground, plus_emph_background));
+ if VERBOSE {
+ println!();
+ print!(" --minus-style {:?}", options.minus_style);
+ print!(" --minus-emph-style {:?}", options.minus_emph_style);
+ print!(" --plus-style {:?}", options.plus_style);
+ print!(" --plus-emph-style {:?}", options.plus_emph_style);
+ println!();
}
+ _do_hunk_color_test(options.clone());
}
}
}