summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2020-02-27 14:26:59 -0600
committerGitHub <noreply@github.com>2020-02-27 14:26:59 -0600
commitcc8ffaa9b4fd1fc91ccfd73b4da258ed4a433ec8 (patch)
treedac68c21e9df071052d30d75e86c30ab01b5d6cb
parent73edbf7bc0d9dc27d24b86d379c920c967a102e5 (diff)
parent3a73fff4341734b58922b1f6d7747ae98a4b28c4 (diff)
Merge pull request #107 from dandavison/99-103-color-options
Provide CLI options for all colors
-rw-r--r--src/cli.rs78
-rw-r--r--src/config.rs38
-rw-r--r--src/delta.rs22
-rw-r--r--src/draw.rs48
-rw-r--r--src/paint.rs57
5 files changed, 184 insertions, 59 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 41c901e2..67635a97 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -10,38 +10,74 @@ use crate::config;
use crate::style;
#[derive(StructOpt, Clone, Debug)]
-#[structopt(name = "delta", about = "A syntax-highlighting pager for git")]
+#[structopt(
+ name = "delta",
+ about = "A syntax-highlighter for git and diff output",
+ after_help = "\
+Colors
+------
+
+All delta color options work the same way. There are two ways to specify a color:
+
+1. RGB hex code
+
+ An example of passing an RGB hex code is:
+ --file-color=\"#0e7c0e\"
+
+2. ANSI color name
+
+ There are 8 ANSI color names:
+ black, red, green, yellow, blue, magenta, cyan, white.
+
+ In addition, all of them have a bright form:
+ bright-black, bright-red, bright-green, bright-yellow, bright-blue, bright-magenta, bright-cyan, bright-white
+
+ An example is:
+ --file-color=\"green\"
+
+ Unlike RGB hex codes, ANSI color names are just names: you can choose the exact color that each
+ name corresponds to in the settings of your terminal application (the application you use to run
+ command line programs). This means that if you use ANSI color names, and you change the color
+ theme used by your terminal, then delta's colors will respond automatically, without needing to
+ change the delta command line.
+
+ \"purple\" is accepted as a synonym for \"magenta\". Color names and codes are case-insensitive.
+"
+)]
pub struct Opt {
- /// Use colors appropriate for a light terminal background. For
- /// more control, see --theme, --plus-color, and --minus-color.
+ /// Use default colors appropriate for a light terminal background. For more control, see the other
+ /// color options.
#[structopt(long = "light")]
pub light: bool,
- /// Use colors appropriate for a dark terminal background. For
- /// more control, see --theme, --plus-color, and --minus-color.
+ /// Use default colors appropriate for a dark terminal background. For more control, see the
+ /// other color options.
#[structopt(long = "dark")]
pub dark: bool,
#[structopt(long = "minus-color")]
- /// The background color (RGB hex) to use for removed lines.
+ /// The background color to use for removed lines.
pub minus_color: Option<String>,
#[structopt(long = "minus-emph-color")]
- /// The background color (RGB hex) to use for emphasized sections of removed lines.
+ /// The background color to use for emphasized sections of removed lines.
pub minus_emph_color: Option<String>,
#[structopt(long = "plus-color")]
- /// The background color (RGB hex) to use for added lines.
+ /// The background color to use for added lines.
pub plus_color: Option<String>,
#[structopt(long = "plus-emph-color")]
- /// The background color (RGB hex) to use for emphasized sections of added lines.
+ /// The background color to use for emphasized sections of added lines.
pub plus_emph_color: Option<String>,
#[structopt(long = "theme", env = "BAT_THEME")]
- /// The 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 variable,
- /// if that contains a valid theme name. Use --list-themes and --compare-themes to view available themes.
+ /// 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
+ /// variable, if that contains a valid theme name. Use --list-themes and --compare-themes to
+ /// view available themes. Note that the choice of theme only affects code syntax highlighting.
+ /// See --commit-color, --file-color, --hunk-color to configure the colors of other parts of
+ /// the diff output.
pub theme: Option<String>,
#[structopt(long = "highlight-removed")]
@@ -50,20 +86,32 @@ pub struct Opt {
pub highlight_removed: bool,
#[structopt(long = "commit-style", default_value = "plain")]
- /// Formatting style for commit section of git output. Options
+ /// Formatting style for the commit section of git output. Options
/// are: plain, box.
pub commit_style: SectionStyle,
+ #[structopt(long = "commit-color", default_value = "yellow")]
+ /// Color for the commit section of git output.
+ pub commit_color: String,
+
#[structopt(long = "file-style", default_value = "underline")]
- /// Formatting style for file section of git output. Options
+ /// Formatting style for the file section of git output. Options
/// are: plain, box, underline.
pub file_style: SectionStyle,
+ #[structopt(long = "file-color", default_value = "blue")]
+ /// Color for the file section of git output.
+ pub file_color: String,
+
#[structopt(long = "hunk-style", default_value = "box")]
- /// Formatting style for hunk section of git output. Options
+ /// Formatting style for the hunk-marker section of git output. Options
/// are: plain, box.
pub hunk_style: SectionStyle,
+ #[structopt(long = "hunk-color", default_value = "blue")]
+ /// Color for the hunk-marker section of git output.
+ pub hunk_color: String,
+
/// The width (in characters) of the background color
/// highlighting. By default, the width is the current terminal
/// width. Use --width=variable to apply background colors to the
diff --git a/src/config.rs b/src/config.rs
index 500c5e2e..2135efe8 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,3 +1,4 @@
+use std::process;
use std::str::FromStr;
use syntect::highlighting::{Color, Style, StyleModifier, Theme, ThemeSet};
@@ -5,6 +6,7 @@ use syntect::parsing::SyntaxSet;
use crate::cli;
use crate::env;
+use crate::paint;
use crate::style;
pub struct Config<'a> {
@@ -14,6 +16,9 @@ pub struct Config<'a> {
pub minus_emph_style_modifier: StyleModifier,
pub plus_style_modifier: StyleModifier,
pub plus_emph_style_modifier: StyleModifier,
+ pub commit_color: Color,
+ pub file_color: Color,
+ pub hunk_color: Color,
pub syntax_set: &'a SyntaxSet,
pub terminal_width: usize,
pub width: Option<usize>,
@@ -45,7 +50,7 @@ pub fn get_config<'a>(
};
let minus_style_modifier = StyleModifier {
- background: Some(color_from_arg(
+ background: Some(color_from_arg_or_mode_default(
opt.minus_color.as_ref(),
is_light_mode,
style::LIGHT_THEME_MINUS_COLOR,
@@ -60,7 +65,7 @@ pub fn get_config<'a>(
};
let minus_emph_style_modifier = StyleModifier {
- background: Some(color_from_arg(
+ background: Some(color_from_arg_or_mode_default(
opt.minus_emph_color.as_ref(),
is_light_mode,
style::LIGHT_THEME_MINUS_EMPH_COLOR,
@@ -75,7 +80,7 @@ pub fn get_config<'a>(
};
let plus_style_modifier = StyleModifier {
- background: Some(color_from_arg(
+ background: Some(color_from_arg_or_mode_default(
opt.plus_color.as_ref(),
is_light_mode,
style::LIGHT_THEME_PLUS_COLOR,
@@ -86,7 +91,7 @@ pub fn get_config<'a>(
};
let plus_emph_style_modifier = StyleModifier {
- background: Some(color_from_arg(
+ background: Some(color_from_arg_or_mode_default(
opt.plus_emph_color.as_ref(),
is_light_mode,
style::LIGHT_THEME_PLUS_EMPH_COLOR,
@@ -103,6 +108,9 @@ pub fn get_config<'a>(
minus_emph_style_modifier,
plus_style_modifier,
plus_emph_style_modifier,
+ commit_color: color_from_rgb_or_ansi_code(&opt.commit_color),
+ file_color: color_from_rgb_or_ansi_code(&opt.file_color),
+ hunk_color: color_from_rgb_or_ansi_code(&opt.hunk_color),
terminal_width,
width,
tab_width: opt.tab_width,
@@ -168,18 +176,32 @@ fn valid_theme_name_or_none(theme_name: Option<&String>, theme_set: &ThemeSet) -
}
}
-fn color_from_arg(
+fn color_from_rgb_or_ansi_code(s: &str) -> Color {
+ let die = || {
+ eprintln!("Invalid color: {}", s);
+ process::exit(1);
+ };
+ if s.starts_with("#") {
+ Color::from_str(s).unwrap_or_else(|_| die())
+ } else {
+ paint::color_from_ansi_name(s).unwrap_or_else(die)
+ }
+}
+
+fn color_from_arg_or_mode_default(
arg: Option<&String>,
is_light_mode: bool,
light_theme_default: Color,
dark_theme_default: Color,
) -> Color {
- arg.and_then(|s| Color::from_str(s).ok())
- .unwrap_or_else(|| {
+ match arg {
+ Some(string) => color_from_rgb_or_ansi_code(&string),
+ None => {
if is_light_mode {
light_theme_default
} else {
dark_theme_default
}
- })
+ }
+ }
}
diff --git a/src/delta.rs b/src/delta.rs
index 7f8aff5a..9c6d16f9 100644
--- a/src/delta.rs
+++ b/src/delta.rs
@@ -1,6 +1,5 @@
use std::io::Write;
-use ansi_term::Colour::{Blue, Yellow};
use console::strip_ansi_codes;
use unicode_segmentation::UnicodeSegmentation;
@@ -8,7 +7,7 @@ use crate::bat::assets::HighlightingAssets;
use crate::cli;
use crate::config::Config;
use crate::draw;
-use crate::paint::Painter;
+use crate::paint::{self, Painter};
use crate::parse;
use crate::style;
@@ -203,7 +202,7 @@ fn handle_commit_meta_header_line(
painter.writer,
line,
config.terminal_width,
- Yellow.normal(),
+ config.commit_color,
true,
)?;
Ok(())
@@ -232,13 +231,12 @@ fn handle_generic_file_meta_header_line(
cli::SectionStyle::Underline => draw::write_underlined,
cli::SectionStyle::Plain => panic!(),
};
- let ansi_style = Blue.normal();
writeln!(painter.writer)?;
draw_fn(
painter.writer,
- &ansi_style.paint(line),
+ &paint::paint_text_foreground(line, config.file_color),
config.terminal_width,
- ansi_style,
+ config.file_color,
false,
)?;
Ok(())
@@ -254,7 +252,6 @@ fn handle_hunk_meta_line(
cli::SectionStyle::Underline => draw::write_underlined,
cli::SectionStyle::Plain => panic!(),
};
- let ansi_style = Blue.normal();
let (raw_code_fragment, line_number) = parse::parse_hunk_metadata(&line);
let code_fragment = prepare(raw_code_fragment, config.tab_width, false);
if !code_fragment.is_empty() {
@@ -280,12 +277,16 @@ fn handle_hunk_meta_line(
painter.writer,
&painter.output_buffer,
config.terminal_width,
- ansi_style,
+ config.hunk_color,
false,
)?;
painter.output_buffer.clear();
}
- writeln!(painter.writer, "\n{}", ansi_style.paint(line_number))?;
+ writeln!(
+ painter.writer,
+ "\n{}",
+ paint::paint_text_foreground(line_number, config.hunk_color)
+ )?;
Ok(())
}
@@ -617,8 +618,11 @@ mod tests {
theme: None,
highlight_removed: false,
commit_style: cli::SectionStyle::Plain,
+ commit_color: "Yellow".to_string(),
file_style: cli::SectionStyle::Underline,
+ file_color: "Blue".to_string(),
hunk_style: cli::SectionStyle::Box,
+ hunk_color: "blue".to_string(),
width: Some("variable".to_string()),
tab_width: 4,
show_background_colors: false,
diff --git a/src/draw.rs b/src/draw.rs
index 9487c557..751f99f9 100644
--- a/src/draw.rs
+++ b/src/draw.rs
@@ -1,17 +1,19 @@
use std::io::Write;
-use ansi_term::Style;
use box_drawing;
use console::strip_ansi_codes;
+use syntect::highlighting::Color;
use unicode_segmentation::UnicodeSegmentation;
+use crate::paint;
+
/// Write text to stream, surrounded by a box, leaving the cursor just
/// beyond the bottom right corner.
pub fn write_boxed(
writer: &mut dyn Write,
text: &str,
_line_width: usize, // ignored
- line_style: Style,
+ color: Color,
heavy: bool,
) -> std::io::Result<()> {
let up_left = if heavy {
@@ -20,8 +22,8 @@ pub fn write_boxed(
box_drawing::light::UP_LEFT
};
let box_width = strip_ansi_codes(text).graphemes(true).count() + 1;
- write_boxed_partial(writer, text, box_width, line_style, heavy)?;
- write!(writer, "{}", line_style.paint(up_left))?;
+ write_boxed_partial(writer, text, box_width, color, heavy)?;
+ write!(writer, "{}", paint::paint_text_foreground(up_left, color))?;
Ok(())
}
@@ -31,11 +33,11 @@ pub fn write_boxed_with_line(
writer: &mut dyn Write,
text: &str,
line_width: usize,
- line_style: Style,
+ color: Color,
heavy: bool,
) -> std::io::Result<()> {
let box_width = strip_ansi_codes(text).graphemes(true).count() + 1;
- write_boxed_with_horizontal_whisker(writer, text, box_width, line_style, heavy)?;
+ write_boxed_with_horizontal_whisker(writer, text, box_width, color, heavy)?;
write_horizontal_line(
writer,
if line_width > box_width {
@@ -43,7 +45,7 @@ pub fn write_boxed_with_line(
} else {
0
},
- line_style,
+ color,
heavy,
)?;
write!(writer, "\n")?;
@@ -54,11 +56,11 @@ pub fn write_underlined(
writer: &mut dyn Write,
text: &str,
line_width: usize,
- line_style: Style,
+ color: Color,
heavy: bool,
) -> std::io::Result<()> {
- writeln!(writer, "{}", line_style.paint(text))?;
- write_horizontal_line(writer, line_width - 1, line_style, heavy)?;
+ writeln!(writer, "{}", paint::paint_text_foreground(text, color))?;
+ write_horizontal_line(writer, line_width - 1, color, heavy)?;
write!(writer, "\n")?;
Ok(())
}
@@ -66,7 +68,7 @@ pub fn write_underlined(
fn write_horizontal_line(
writer: &mut dyn Write,
line_width: usize,
- line_style: Style,
+ color: Color,
heavy: bool,
) -> std::io::Result<()> {
let horizontal = if heavy {
@@ -77,7 +79,7 @@ fn write_horizontal_line(
write!(
writer,
"{}",
- line_style.paint(horizontal.repeat(line_width),)
+ paint::paint_text_foreground(&horizontal.repeat(line_width), color)
)
}
@@ -85,7 +87,7 @@ pub fn write_boxed_with_horizontal_whisker(
writer: &mut dyn Write,
text: &str,
box_width: usize,
- line_style: Style,
+ color: Color,
heavy: bool,
) -> std::io::Result<()> {
let up_horizontal = if heavy {
@@ -93,8 +95,12 @@ pub fn write_boxed_with_horizontal_whisker(
} else {
box_drawing::light::UP_HORIZONTAL
};
- write_boxed_partial(writer, text, box_width, line_style, heavy)?;
- write!(writer, "{}", line_style.paint(up_horizontal))?;
+ write_boxed_partial(writer, text, box_width, color, heavy)?;
+ write!(
+ writer,
+ "{}",
+ paint::paint_text_foreground(up_horizontal, color)
+ )?;
Ok(())
}
@@ -102,7 +108,7 @@ fn write_boxed_partial(
writer: &mut dyn Write,
text: &str,
box_width: usize,
- line_style: Style,
+ color: Color,
heavy: bool,
) -> std::io::Result<()> {
let horizontal = if heavy {
@@ -125,10 +131,10 @@ fn write_boxed_partial(
write!(
writer,
"{}{}\n{} {}\n{}",
- line_style.paint(&horizontal_edge),
- line_style.paint(down_left),
- line_style.paint(text),
- line_style.paint(vertical),
- line_style.paint(&horizontal_edge),
+ paint::paint_text_foreground(&horizontal_edge, color),
+ paint::paint_text_foreground(down_left, color),
+ paint::paint_text_foreground(text, color),
+ paint::paint_text_foreground(vertical, color),
+ paint::paint_text_foreground(&horizontal_edge, color),
)
}
diff --git a/src/paint.rs b/src/paint.rs
index 5aec3a15..cf6968e1 100644
--- a/src/paint.rs
+++ b/src/paint.rs
@@ -1,4 +1,5 @@
use std::io::Write;
+use std::str::FromStr;
use syntect::easy::HighlightLines;
use syntect::highlighting::{Color, Style, StyleModifier};
@@ -204,23 +205,31 @@ impl<'a> Painter<'a> {
}
}
-/// Write section text to buffer with color escape codes.
+/// Write section text to buffer with shell escape codes specifying foreground and background color.
pub fn paint_text(text: &str, style: Style, output_buffer: &mut String) {
if text.is_empty() {
return;
}
if style.background != style::NO_COLOR {
- output_buffer.push_str(&get_color_code(style.background, false));
+ output_buffer.push_str(&get_color_escape_sequence(style.background, false));
}
if style.foreground != style::NO_COLOR {
- output_buffer.push_str(&get_color_code(style.foreground, true));
+ output_buffer.push_str(&get_color_escape_sequence(style.foreground, true));
}
output_buffer.push_str(text);
}
-/// ANSI color escape code.
-// See https://github.com/ogham/rust-ansi-term/blob/ff7eba98d55ad609c7fcc8c7bb0859b37c7545cc/src/ansi.rs#L82-L112
-fn get_color_code(color: Color, foreground: bool) -> String {
+/// Return text together with shell escape codes specifying the foreground color.
+pub fn paint_text_foreground(text: &str, color: Color) -> String {
+ format!("{}{}", get_color_escape_sequence(color, true), text)
+}
+
+/// Return shell escape sequence specifying either an RGB color, or a user-customizable 8-bit ANSI
+/// color code.
+// See
+// https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
+// https://github.com/ogham/rust-ansi-term/blob/ff7eba98d55ad609c7fcc8c7bb0859b37c7545cc/src/ansi.rs#L82-L112
+fn get_color_escape_sequence(color: Color, foreground: bool) -> String {
if color.a == 0 {
// See https://github.com/sharkdp/bat/pull/543
format!("\x1b[{};5;{}m", if foreground { 38 } else { 48 }, color.r)
@@ -235,6 +244,42 @@ fn get_color_code(color: Color, foreground: bool) -> String {
}
}
+// See
+// https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
+pub fn ansi_color_name_to_number(name: &str) -> Option<u8> {
+ match name.to_lowercase().as_ref() {
+ "black" => Some(0),
+ "red" => Some(1),
+ "green" => Some(2),
+ "yellow" => Some(3),
+ "blue" => Some(4),
+ "magenta" => Some(5),
+ "purple" => Some(5),
+ "cyan" => Some(6),
+ "white" => Some(7),
+ "bright-black" => Some(8),
+ "bright-red" => Some(9),
+ "bright-green" => Some(10),
+ "bright-yellow" => Some(11),
+ "bright-blue" => Some(12),
+ "bright-magenta" => Some(13),
+ "bright-purple" => Some(13),
+ "bright-cyan" => Some(14),
+ "bright-white" => Some(15),
+ _ => None,
+ }
+}
+
+pub fn color_from_ansi_name(name: &str) -> Option<Color> {
+ ansi_color_name_to_number(name).and_then(|n| Some(color_from_ansi_number(n)))
+}
+
+/// Convert 8-bit ANSI code to #RGBA string with ANSI code in red channel and 0 in alpha channel.
+// See https://github.com/sharkdp/bat/pull/543
+pub fn color_from_ansi_number(n: u8) -> Color {
+ Color::from_str(&format!("#{:02x}000000", n)).unwrap()
+}
+
mod superimpose_style_sections {
use syntect::highlighting::{Style, StyleModifier};