Skip to content

Commit

Permalink
Provide CLI options for all colors
Browse files Browse the repository at this point in the history
- Fixes #99, #103
- Support both RGB and ANSI codes
- Eliminate (almost) ansi_term crate in favor of syntect for representing colors
  • Loading branch information
dandavison committed Feb 27, 2020
1 parent 1f83397 commit 4bc3b60
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 45 deletions.
53 changes: 49 additions & 4 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,40 @@ 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.
Expand Down Expand Up @@ -50,20 +83,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. See below for how to specify colors.
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. See below for how to specify colors.
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. See below for how to specify colors.
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
Expand Down
30 changes: 25 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::process;
use std::str::FromStr;

use syntect::highlighting::{Color, Style, StyleModifier, Theme, ThemeSet};
use syntect::parsing::SyntaxSet;

use crate::cli;
use crate::env;
use crate::paint;
use crate::style;

pub struct Config<'a> {
Expand All @@ -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>,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -168,7 +176,19 @@ 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,
Expand Down
22 changes: 13 additions & 9 deletions src/delta.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::io::Write;

use ansi_term::Colour::{Blue, Yellow};
use console::strip_ansi_codes;
use unicode_segmentation::UnicodeSegmentation;

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;

Expand Down Expand Up @@ -203,7 +202,7 @@ fn handle_commit_meta_header_line(
painter.writer,
line,
config.terminal_width,
Yellow.normal(),
config.commit_color,
true,
)?;
Ok(())
Expand Down Expand Up @@ -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(())
Expand All @@ -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() {
Expand All @@ -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(())
}

Expand Down Expand Up @@ -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,
Expand Down
48 changes: 27 additions & 21 deletions src/draw.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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(())
}

Expand All @@ -31,19 +33,19 @@ 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 {
line_width - box_width - 1
} else {
0
},
line_style,
color,
heavy,
)?;
write!(writer, "\n")?;
Expand All @@ -54,19 +56,19 @@ 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(())
}

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 {
Expand All @@ -77,32 +79,36 @@ fn write_horizontal_line(
write!(
writer,
"{}",
line_style.paint(horizontal.repeat(line_width),)
paint::paint_text_foreground(&horizontal.repeat(line_width), color)
)
}

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 {
box_drawing::heavy::UP_HORIZONTAL
} 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(())
}

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 {
Expand All @@ -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),
)
}
Loading

0 comments on commit 4bc3b60

Please sign in to comment.