summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2020-05-27 15:26:08 -0400
committerDan Davison <dandavison7@gmail.com>2020-05-27 15:58:47 -0400
commit284392c7580e082070e0b9c05d712da65d52c943 (patch)
tree798df4207d09aa1d600345c9a455e579d9364ddc /src
parent327792f4bc67d05d03785fe127df9990d2ffce37 (diff)
Refactor: Create color and theme modules, consolidate style code
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs138
-rw-r--r--src/color.rs134
-rw-r--r--src/config.rs547
-rw-r--r--src/main.rs8
-rw-r--r--src/style.rs406
-rw-r--r--src/syntect_color.rs4
-rw-r--r--src/tests/ansi_test_utils.rs5
-rw-r--r--src/theme.rs75
8 files changed, 671 insertions, 646 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 23ea5929..51e7e7b6 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -9,7 +9,7 @@ use crate::bat::output::PagingMode;
use crate::config;
use crate::env;
use crate::rewrite;
-use crate::style;
+use crate::theme;
#[derive(StructOpt, Clone, Debug, PartialEq)]
#[structopt(
@@ -351,12 +351,12 @@ fn _check_validity(opt: &Opt, assets: &HighlightingAssets) {
process::exit(1);
}
if let Some(ref theme) = opt.theme {
- if !style::is_no_syntax_highlighting_theme_name(&theme) {
+ if !theme::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);
+ let is_light_theme = theme::is_light_theme(&theme);
if is_light_theme && opt.dark {
eprintln!(
"{} is a light theme, but you supplied --dark. \
@@ -390,135 +390,3 @@ fn is_truecolor_terminal() -> bool {
.map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
.unwrap_or(false)
}
-
-#[cfg(test)]
-mod tests {
- use std::env;
-
- use crate::cli;
- use crate::style;
- use crate::tests::integration_test_utils::integration_test_utils;
-
- #[test]
- #[ignore]
- fn test_theme_selection() {
- #[derive(PartialEq)]
- enum Mode {
- Light,
- Dark,
- };
- 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(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.ansi_term_style.background.unwrap(),
- style::get_minus_background_color_default(
- expected_mode == Mode::Light,
- is_true_color
- )
- );
- assert_eq!(
- config.minus_emph_style.ansi_term_style.background.unwrap(),
- style::get_minus_emph_background_color_default(
- expected_mode == Mode::Light,
- is_true_color
- )
- );
- assert_eq!(
- config.plus_style.ansi_term_style.background.unwrap(),
- style::get_plus_background_color_default(
- expected_mode == Mode::Light,
- is_true_color
- )
- );
- assert_eq!(
- config.plus_emph_style.ansi_term_style.background.unwrap(),
- style::get_plus_emph_background_color_default(
- expected_mode == Mode::Light,
- is_true_color
- )
- );
- }
- }
-}
diff --git a/src/color.rs b/src/color.rs
new file mode 100644
index 00000000..4d0d2813
--- /dev/null
+++ b/src/color.rs
@@ -0,0 +1,134 @@
+use std::process;
+use std::str::FromStr;
+
+use ansi_term::Color;
+use syntect::highlighting::Color as SyntectColor;
+
+use crate::bat::terminal::to_ansi_color;
+use crate::syntect_color;
+
+pub fn color_from_rgb_or_ansi_code(s: &str, true_color: bool) -> Color {
+ let die = || {
+ eprintln!("Invalid color: {}", s);
+ process::exit(1);
+ };
+ let syntect_color = if s.starts_with("#") {
+ SyntectColor::from_str(s).unwrap_or_else(|_| die())
+ } else {
+ s.parse::<u8>()
+ .ok()
+ .and_then(syntect_color::syntect_color_from_ansi_number)
+ .or_else(|| syntect_color::syntect_color_from_ansi_name(s))
+ .unwrap_or_else(die)
+ };
+ to_ansi_color(syntect_color, true_color)
+}
+
+pub fn color_from_rgb_or_ansi_code_with_default(
+ arg: &str,
+ default: Option<Color>,
+ true_color: bool,
+) -> Option<Color> {
+ let arg = arg.to_lowercase();
+ if arg == "normal" {
+ None
+ } else if arg == "auto" {
+ default
+ } else {
+ Some(color_from_rgb_or_ansi_code(&arg, true_color))
+ }
+}
+
+// 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 get_minus_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
+ match (is_light_mode, is_true_color) {
+ (true, true) => LIGHT_THEME_MINUS_COLOR,
+ (true, false) => LIGHT_THEME_MINUS_COLOR_256,
+ (false, true) => DARK_THEME_MINUS_COLOR,
+ (false, false) => DARK_THEME_MINUS_COLOR_256,
+ }
+}
+
+pub fn get_minus_emph_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
+ match (is_light_mode, is_true_color) {
+ (true, true) => LIGHT_THEME_MINUS_EMPH_COLOR,
+ (true, false) => LIGHT_THEME_MINUS_EMPH_COLOR_256,
+ (false, true) => DARK_THEME_MINUS_EMPH_COLOR,
+ (false, false) => DARK_THEME_MINUS_EMPH_COLOR_256,
+ }
+}
+
+pub fn get_plus_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
+ match (is_light_mode, is_true_color) {
+ (true, true) => LIGHT_THEME_PLUS_COLOR,
+ (true, false) => LIGHT_THEME_PLUS_COLOR_256,
+ (false, true) => DARK_THEME_PLUS_COLOR,
+ (false, false) => DARK_THEME_PLUS_COLOR_256,
+ }
+}
+
+pub fn get_plus_emph_background_color_default(is_light_mode: bool, is_true_color: bool) -> Color {
+ match (is_light_mode, is_true_color) {
+ (true, true) => LIGHT_THEME_PLUS_EMPH_COLOR,
+ (true, false) => LIGHT_THEME_PLUS_EMPH_COLOR_256,
+ (false, true) => DARK_THEME_PLUS_EMPH_COLOR,
+ (false, false) => DARK_THEME_PLUS_EMPH_COLOR_256,
+ }
+}
+
+const LIGHT_THEME_MINUS_COLOR: Color = Color::RGB(0xff, 0xe0, 0xe0);
+
+const LIGHT_THEME_MINUS_COLOR_256: Color = Color::Fixed(224);
+
+const LIGHT_THEME_MINUS_EMPH_COLOR: Color = Color::RGB(0xff, 0xc0, 0xc0);
+
+const LIGHT_THEME_MINUS_EMPH_COLOR_256: Color = Color::Fixed(217);
+
+const LIGHT_THEME_PLUS_COLOR: Color = Color::RGB(0xd0, 0xff, 0xd0);
+
+const LIGHT_THEME_PLUS_COLOR_256: Color = Color::Fixed(194);
+
+const LIGHT_THEME_PLUS_EMPH_COLOR: Color = Color::RGB(0xa0, 0xef, 0xa0);
+
+const LIGHT_THEME_PLUS_EMPH_COLOR_256: Color = Color::Fixed(157);
+
+const DARK_THEME_MINUS_COLOR: Color = Color::RGB(0x3f, 0x00, 0x01);
+
+const DARK_THEME_MINUS_COLOR_256: Color = Color::Fixed(52);
+
+const DARK_THEME_MINUS_EMPH_COLOR: Color = Color::RGB(0x90, 0x10, 0x11);
+
+const DARK_THEME_MINUS_EMPH_COLOR_256: Color = Color::Fixed(124);
+
+const DARK_THEME_PLUS_COLOR: Color = Color::RGB(0x00, 0x28, 0x00);
+
+const DARK_THEME_PLUS_COLOR_256: Color = Color::Fixed(22);
+
+const DARK_THEME_PLUS_EMPH_COLOR: Color = Color::RGB(0x00, 0x60, 0x00);
+
+const DARK_THEME_PLUS_EMPH_COLOR_256: Color = Color::Fixed(28);
diff --git a/src/config.rs b/src/config.rs
index 48c185e9..dd1dc060 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,18 +1,13 @@
-use std::process;
-use std::str::FromStr;
-
-use ansi_term::Color;
-use syntect::highlighting::Color as SyntectColor;
use syntect::highlighting::Style as SyntectStyle;
use syntect::highlighting::{Theme, ThemeSet};
use syntect::parsing::SyntaxSet;
use crate::bat::output::PagingMode;
-use crate::bat::terminal::to_ansi_color;
-use crate::cli::{self, unreachable};
+use crate::cli;
+use crate::color;
use crate::env;
-use crate::style::{self, DecorationStyle, Style};
-use crate::syntect_color;
+use crate::style::Style;
+use crate::theme;
pub struct Config<'a> {
pub theme: Option<Theme>,
@@ -53,7 +48,7 @@ pub fn get_config<'a>(
) -> Config<'a> {
let background_color_extends_to_terminal_width = opt.width != Some("variable".to_string());
let theme_name_from_bat_pager = env::get_env_var("BAT_THEME");
- let (is_light_mode, theme_name) = get_is_light_mode_and_theme_name(
+ let (is_light_mode, theme_name) = theme::get_is_light_mode_and_theme_name(
opt.theme.as_ref(),
theme_name_from_bat_pager.as_ref(),
opt.light,
@@ -73,7 +68,7 @@ pub fn get_config<'a>(
let (commit_style, file_style, hunk_header_style) =
make_commit_file_hunk_header_styles(&opt, true_color);
- let theme = if style::is_no_syntax_highlighting_theme_name(&theme_name) {
+ let theme = if theme::is_no_syntax_highlighting_theme_name(&theme_name) {
None
} else {
Some(theme_set.themes[&theme_name].clone())
@@ -126,70 +121,15 @@ pub fn get_config<'a>(
}
}
-/// Return a (theme_name, is_light_mode) tuple.
-/// theme_name == None in return value means syntax highlighting is disabled.
-///
-/// There are two types of color choices that have to be made:
-/// 1. The choice of "theme". This is the language syntax highlighting theme; you have to make this choice when using `bat` also.
-/// 2. The choice of "light vs dark mode". This determines whether the background colors should be chosen for a light or dark terminal background. (`bat` has no equivalent.)
-///
-/// Basically:
-/// 1. The theme is specified by the `--theme` option. If this isn't supplied then it is specified by the `BAT_PAGER` environment variable.
-/// 2. Light vs dark mode is specified by the `--light` or `--dark` options. If these aren't supplied then it is inferred from the chosen theme.
-///
-/// In the absence of other factors, the default assumes a dark terminal background.
-///
-/// Specifically, the rules are as follows:
-///
-/// | --theme | $BAT_THEME | --light/--dark | Behavior |
-/// |------------|------------|----------------|----------------------------------------------------------------------------|
-/// | - | - | - | default dark theme, dark mode |
-/// | some_theme | (IGNORED) | - | some_theme with light/dark mode inferred accordingly |
-/// | - | BAT_THEME | - | BAT_THEME, with light/dark mode inferred accordingly |
-/// | - | - | yes | default light/dark theme, light/dark mode |
-/// | some_theme | (IGNORED) | yes | some_theme, light/dark mode (even if some_theme conflicts with light/dark) |
-/// | - | BAT_THEME | yes | BAT_THEME, light/dark mode (even if BAT_THEME conflicts with light/dark) |
-fn get_is_light_mode_and_theme_name(
- theme_arg: Option<&String>,
- bat_theme_env_var: Option<&String>,
- light_mode_arg: bool,
- theme_set: &ThemeSet,
-) -> (bool, String) {
- let theme_arg = valid_theme_name_or_none(theme_arg, theme_set);
- let bat_theme_env_var = valid_theme_name_or_none(bat_theme_env_var, theme_set);
- match (theme_arg, bat_theme_env_var, light_mode_arg) {
- (None, None, false) => (false, style::DEFAULT_DARK_THEME.to_string()),
- (Some(theme_name), _, false) => (style::is_light_theme(&theme_name), theme_name),
- (None, Some(theme_name), false) => (style::is_light_theme(&theme_name), theme_name),
- (None, None, true) => (true, style::DEFAULT_LIGHT_THEME.to_string()),
- (Some(theme_name), _, is_light_mode) => (is_light_mode, theme_name),
- (None, Some(theme_name), is_light_mode) => (is_light_mode, theme_name),
- }
-}
-
-// At this stage the theme name is considered valid if it is either a real theme name or the special
-// no-syntax-highlighting name.
-fn valid_theme_name_or_none(theme_name: Option<&String>, theme_set: &ThemeSet) -> Option<String> {
- match theme_name {
- Some(name)
- if style::is_no_syntax_highlighting_theme_name(name)
- || theme_set.themes.contains_key(name) =>
- {
- Some(name.to_string())
- }
- _ => None,
- }
-}
-
fn make_hunk_styles<'a>(
opt: &'a cli::Opt,
is_light_mode: bool,
true_color: bool,
) -> (Style, Style, Style, Style, Style, Style, Style) {
- let minus_style = parse_style(
+ let minus_style = Style::from_str(
&opt.minus_style,
None,
- Some(style::get_minus_background_color_default(
+ Some(color::get_minus_background_color_default(
is_light_mode,
true_color,
)),
@@ -197,10 +137,10 @@ fn make_hunk_styles<'a>(
true_color,
);
- let minus_emph_style = parse_style(
+ let minus_emph_style = Style::from_str(
&opt.minus_emph_style,
None,
- Some(style::get_minus_emph_background_color_default(
+ Some(color::get_minus_emph_background_color_default(
is_light_mode,
true_color,
)),
@@ -210,7 +150,7 @@ fn make_hunk_styles<'a>(
// The non-emph styles default to the base style.
let minus_non_emph_style = match &opt.minus_non_emph_style {
- Some(style_string) => parse_style(
+ Some(style_string) => Style::from_str(
&style_string,
None,
minus_style.ansi_term_style.background,
@@ -220,12 +160,12 @@ fn make_hunk_styles<'a>(
None => minus_style,
};
- let zero_style = parse_style(&opt.zero_style, None, None, None, true_color);
+ let zero_style = Style::from_str(&opt.zero_style, None, None, None, true_color);
- let plus_style = parse_style(
+ let plus_style = Style::from_str(
&opt.plus_style,
None,
- Some(style::get_plus_background_color_default(
+ Some(color::get_plus_background_color_default(
is_light_mode,
true_color,
)),
@@ -233,10 +173,10 @@ fn make_hunk_styles<'a>(
true_color,
);
- let plus_emph_style = parse_style(
+ let plus_emph_style = Style::from_str(
&opt.plus_emph_style,
None,
- Some(style::get_plus_emph_background_color_default(
+ Some(color::get_plus_emph_background_color_default(
is_light_mode,
true_color,
)),
@@ -246,7 +186,7 @@ fn make_hunk_styles<'a>(
// The non-emph styles default to the base style.
let plus_non_emph_style = match &opt.plus_non_emph_style {
- Some(style_string) => parse_style(
+ Some(style_string) => Style::from_str(
&style_string,
None,
plus_style.ansi_term_style.background,
@@ -269,7 +209,7 @@ fn make_hunk_styles<'a>(
fn make_commit_file_hunk_header_styles(opt: &cli::Opt, true_color: bool) -> (Style, Style, Style) {
(
- _parse_style_respecting_deprecated_foreground_color_arg(
+ Style::from_str_respecting_deprecated_foreground_color_arg(
&opt.commit_style,
None,
None,
@@ -277,7 +217,7 @@ fn make_commit_file_hunk_header_styles(opt: &cli::Opt, true_color: bool) -> (Sty
opt.deprecated_commit_color.as_deref(),
true_color,
),
- _parse_style_respecting_deprecated_foreground_color_arg(
+ Style::from_str_respecting_deprecated_foreground_color_arg(
&opt.file_style,
None,
None,
@@ -285,7 +225,7 @@ fn make_commit_file_hunk_header_styles(opt: &cli::Opt, true_color: bool) -> (Sty
opt.deprecated_file_color.as_deref(),
true_color,
),
- _parse_style_respecting_deprecated_foreground_color_arg(
+ Style::from_str_respecting_deprecated_foreground_color_arg(
&opt.hunk_header_style,
None,
None,
@@ -296,340 +236,135 @@ fn make_commit_file_hunk_header_styles(opt: &cli::Opt, true_color: bool) -> (Sty
)
}
-fn _parse_style_respecting_deprecated_foreground_color_arg(
- style_string: &str,
- foreground_default: Option<Color>,
- background_default: Option<Color>,
- decoration_style_string: Option<&str>,
- deprecated_foreground_color_arg: Option<&str>,
- true_color: bool,
-) -> Style {
- let mut style = parse_style(
- style_string,
- foreground_default,
- background_default,
- decoration_style_string,
- true_color,
- );
- if let Some(s) = deprecated_foreground_color_arg {
- // The deprecated --{commit,file,hunk}-color args functioned to set the decoration
- // foreground color. In the case of file, it set the text foreground color also.
- let foreground_from_deprecated_arg = parse_ansi_term_style(s, None, None, true_color)
- .0
- .foreground;
- style.ansi_term_style.foreground = foreground_from_deprecated_arg;
- style.decoration_style = match style.decoration_style {
- Some(DecorationStyle::Box(mut ansi_term_style)) => {
- ansi_term_style.foreground = foreground_from_deprecated_arg;
- Some(DecorationStyle::Box(ansi_term_style))
- }
- Some(DecorationStyle::Underline(mut ansi_term_style)) => {
- ansi_term_style.foreground = foreground_from_deprecated_arg;
- Some(DecorationStyle::Underline(ansi_term_style))
- }
- Some(DecorationStyle::Overline(mut ansi_term_style)) => {
- ansi_term_style.foreground = foreground_from_deprecated_arg;
- Some(DecorationStyle::Overline(ansi_term_style))
- }
- Some(DecorationStyle::Underoverline(mut ansi_term_style)) => {
- ansi_term_style.foreground = foreground_from_deprecated_arg;
- Some(DecorationStyle::Underoverline(ansi_term_style))
- }
- _ => style.decoration_style,
- };
- }
- style
-}
-
-/// Construct Style from style and decoration-style strings supplied on command line, together with
-/// defaults.
-pub fn parse_style(
- style_string: &str,
- foreground_default: Option<Color>,
- background_default: Option<Color>,
- decoration_style_string: Option<&str>,
- true_color: bool,
-) -> Style {
- let (style_string, special_attribute_from_style_string) =
- extract_special_attribute(style_string);
- let (ansi_term_style, is_syntax_highlighted) = parse_ansi_term_style(
- &style_string,
- foreground_default,
- background_default,
- true_color,
- );
- let decoration_style = match decoration_style_string {
- Some(s) if s != "" => parse_decoration_style(s, true_color),
- _ => None,
- };
- let mut style = Style {
- ansi_term_style,
- is_syntax_highlighted,
- decoration_style,
- };
- if let Some(special_attribute) = special_attribute_from_style_string {
- if let Some(decoration_style) = apply_special_decoration_attribute(
- style.decoration_style,
- &special_attribute,
- true_color,
- ) {
- style.decoration_style = Some(decoration_style)
- }
- }
- style
-}
-
-fn apply_special_decoration_attribute(
- decoration_style: Option<DecorationStyle>,
- special_attribute: &str,
- true_color: bool,
-) -> Option<DecorationStyle> {
- let ansi_term_style = match decoration_style {
- None => ansi_term::Style::new(),
- Some(DecorationStyle::Box(ansi_term_style)) => ansi_term_style,
- Some(DecorationStyle::Underline(ansi_term_style)) => ansi_term_style,
- Some(DecorationStyle::Overline(ansi_term_style)) => ansi_term_style,
- Some(DecorationStyle::Underoverline(ansi_term_style)) => ansi_term_style,
- Some(DecorationStyle::Omit) => ansi_term::Style::new(),
- };
- match parse_decoration_style(special_attribute, true_color) {
- Some(DecorationStyle::Box(_)) => Some(DecorationStyle::Box(ansi_term_style)),
- Some(DecorationStyle::Underline(_)) => Some(DecorationStyle::Underline(ansi_term_style)),
- Some(DecorationStyle::Overline(_)) => Some(DecorationStyle::Overline(ansi_term_style)),
- Some(DecorationStyle::Underoverline(_)) => {
- Some(DecorationStyle::Underoverline(ansi_term_style))
- }
- Some(DecorationStyle::Omit) => Some(DecorationStyle::Omit),
- None => None,
- }
-}
-
-fn parse_ansi_term_style(
- s: &str,
- foreground_default: Option<Color>,
- background_default: Option<Color>,
- true_color: bool,
-) -> (ansi_term::Style, bool) {
- let mut style = ansi_term::Style::new();
- let mut seen_foreground = false;
- let mut seen_background = false;
- let mut is_syntax_highlighted = false;
- for word in s
- .to_lowercase()
- .split_whitespace()
- .map(|word| word.trim_matches(|c| c == '"' || c == '\''))
- {
- if word == "blink" {
- style.is_blink = true;
- } else if word == "bold" {
- style.is_bold = true;
- } else if word == "dimmed" {
- style.is_dimmed = true;
- } else if word == "hidden" {
- style.is_hidden = true;
- } else if word == "italic" {
- style.is_italic = true;
- } else if word == "reverse" {
- style.is_reverse = true;
- } else if word == "strikethrough" {
- style.is_strikethrough = true;
- } else if !seen_foreground {
- if word == "syntax" {
- is_syntax_highlighted = true;
- } else {
- style.foreground =
- color_from_rgb_or_ansi_code_with_default(word, foreground_default, true_color);
- }
- seen_foreground = true;
- } else if !seen_background {
- if word == "syntax" {
- eprintln!(
- "You have used the special color 'syntax' as a background color \
- (second color in a style string). It may only be used as a foreground \
- color (first color in a style string)."
- );
- process::exit(1);
- } else {
- style.background =
- color_from_rgb_or_ansi_code_with_default(word, background_default, true_color);
- }
- seen_background = true;
- } else {
- eprintln!(
- "Invalid style string: {}. See the STYLES section of delta --help.",
- s
- );
- process::exit(1);
- }
- }
- (style, is_syntax_highlighted)
-}
-
-fn parse_decoration_style(style_string: &str, true_color: bool) -> Option<DecorationStyle> {
- let (style_string, special_attribute) = extract_special_attribute(&style_string);
- let special_attribute = special_attribute.unwrap_or_else(|| {
- eprintln!(
- "Invalid decoration style: '{}'. To specify a decoration style, you must supply one of \
- the special attributes: 'box', 'underline', 'overline', 'underoverline', or 'omit'.",
- style_string
- );
- process::exit(1);
- });
- let (style, is_syntax_highlighted): (ansi_term::Style, bool) =
- parse_ansi_term_style(&style_string, None, None, true_color);
- if is_syntax_highlighted {
- eprintln!("'syntax' may not be used as a color name in a decoration style.");
- process::exit(1);
- };
- match special_attribute.as_ref() {
- "box" => Some(DecorationStyle::Box(style)),
- "underline" => Some(DecorationStyle::Underline(style)),
- "overline" => Some(DecorationStyle::Overline(style)),
- "underoverline" => Some(DecorationStyle::Underoverline(style)),
- "omit" => Some(DecorationStyle::Omit),
- "plain" => None,
- _ => unreachable("Unreachable code path reached in parse_decoration_style."),
- }
-}
-
-/// If the style string contains a 'special decoration attribute' then extract it and return it
-/// along with the modified style string.
-fn extract_special_attribute(style_string: &str) -> (String, Option<String>) {
- let style_string = style_string.to_lowercase();
- let (special_attributes, standard_attributes): (Vec<&str>, Vec<&str>) = style_string
- .split_whitespace()
- .map(|word| word.trim_matches(|c| c == '"' || c == '\''))
- .partition(|&token| {
- // TODO: This should be tied to the enum
- token == "box"
- || token == "underline"
- || token == "overline"
- || token == "underoverline"
- || token == "omit"
- || token == "plain"
- });
- match special_attributes {
- attrs if attrs.len() == 0 => (style_string.to_string(), None),
- attrs if attrs.len() == 1 => (standard_attributes.join(" "), Some(attrs[0].to_string())),
- attrs => {
- eprintln!(
- "Encountered multiple special attributes: {:?}. \
- You may supply no more than one of the special attributes 'box', 'underline', \
- and 'omit'.",
- attrs.join(", ")
- );
- process::exit(1);
- }
- }
-}
-
-pub fn color_from_rgb_or_ansi_code(s: &str, true_color: bool) -> Color {
- let die = || {
- eprintln!("Invalid color: {}", s);
- process::exit(1);
- };
- let syntect_color = if s.starts_with("#") {
- SyntectColor::from_str(s).unwrap_or_else(|_| die())
- } else {
- s.parse::<u8>()
- .ok()
- .and_then(syntect_color::syntect_color_from_ansi_number)
- .or_else(|| syntect_color::syntect_color_from_ansi_name(s))
- .unwrap_or_else(die)
- };
- to_ansi_color(syntect_color, true_color)
-}
-
-fn color_from_rgb_or_ansi_code_with_default(
- arg: &str,
- default: Option<Color>,
- true_color: bool,
-) -> Option<Color> {
- let arg = arg.to_lowercase();
- if arg == "normal" {