diff options
author | Dan Davison <dandavison7@gmail.com> | 2020-06-29 22:34:02 -0400 |
---|---|---|
committer | Dan Davison <dandavison7@gmail.com> | 2020-06-30 08:00:30 -0400 |
commit | 7d03c25266d84e64bab3bbf3c1782b980e8f5385 (patch) | |
tree | c8c9db167c5bd82666ae06fc9ad6a4a95dd77e54 /src/options | |
parent | 3b2f709bdc9058d14efacaa4bc58b994c0885aea (diff) |
Rearrange: options::theme module
Diffstat (limited to 'src/options')
-rw-r--r-- | src/options/mod.rs | 2 | ||||
-rw-r--r-- | src/options/preprocess.rs | 29 | ||||
-rw-r--r-- | src/options/set.rs | 79 | ||||
-rw-r--r-- | src/options/theme.rs | 251 |
4 files changed, 296 insertions, 65 deletions
diff --git a/src/options/mod.rs b/src/options/mod.rs index 7ccd4b90..f15e5806 100644 --- a/src/options/mod.rs +++ b/src/options/mod.rs @@ -1,5 +1,5 @@ pub mod get; pub mod option_value; -pub mod preprocess; pub mod rewrite; pub mod set; +pub mod theme; diff --git a/src/options/preprocess.rs b/src/options/preprocess.rs deleted file mode 100644 index c1951892..00000000 --- a/src/options/preprocess.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::process; - -use crate::bat::assets::HighlightingAssets; -use crate::cli; -use crate::env; -use crate::syntax_theme; - -#[allow(non_snake_case)] -pub fn set__is_light_mode__syntax_theme__syntax_set( - opt: &mut cli::Opt, - assets: HighlightingAssets, -) { - let syntax_theme_name_from_bat_theme = env::get_env_var("BAT_THEME"); - let (is_light_mode, syntax_theme_name) = syntax_theme::get_is_light_mode_and_theme_name( - opt.syntax_theme.as_ref(), - syntax_theme_name_from_bat_theme.as_ref(), - opt.light, - &assets.theme_set, - ); - opt.computed.is_light_mode = is_light_mode; - - opt.computed.syntax_theme = - if syntax_theme::is_no_syntax_highlighting_theme_name(&syntax_theme_name) { - None - } else { - Some(assets.theme_set.themes[&syntax_theme_name].clone()) - }; - opt.computed.syntax_set = assets.syntax_set; -} diff --git a/src/options/set.rs b/src/options/set.rs index 339ceffc..7a2df4b6 100644 --- a/src/options/set.rs +++ b/src/options/set.rs @@ -1,4 +1,4 @@ -use std::collections::{HashSet, VecDeque}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::process; use structopt::clap; @@ -6,10 +6,9 @@ use structopt::clap; use crate::bat::assets::HighlightingAssets; use crate::cli; use crate::config; - use crate::features; use crate::git_config; -use crate::options::preprocess; +use crate::options::theme; macro_rules! set_options { ([$( ($option_name:expr, $field_ident:ident) ),* ], @@ -34,8 +33,12 @@ macro_rules! set_options { option_names.extend(&[ "diff-highlight", // Does not exist as a flag on config "diff-so-fancy", // Does not exist as a flag on config - "features", + "features", // Processes differently + // Set prior to the rest "no-gitconfig", + "dark", + "light", + "syntax-theme", ]); let expected_option_names = $crate::cli::Opt::get_option_or_flag_names(); if option_names != expected_option_names { @@ -77,34 +80,8 @@ pub fn set_options( .unwrap_or_else(|| "magenta reverse".to_string()) } - // Set light, dark and syntax-theme - let validate_light_and_dark = |opt: &cli::Opt| { - if opt.light && opt.dark { - eprintln!("--light and --dark cannot be used together."); - process::exit(1); - } - }; - validate_light_and_dark(&opt); - if !(opt.light || opt.dark) { - set_options!( - [("dark", dark), ("light", light)], - opt, - builtin_features, - git_config, - arg_matches, - false - ); - } - validate_light_and_dark(&opt); - set_options!( - [("syntax-theme", syntax_theme)], - opt, - builtin_features, - git_config, - arg_matches, - false - ); - preprocess::set__is_light_mode__syntax_theme__syntax_set(opt, assets); + set__light__dark__syntax_theme__options(opt, &builtin_features, git_config, arg_matches); + theme::set__is_light_mode__syntax_theme__syntax_set(opt, assets); set_options!( [ @@ -112,7 +89,6 @@ pub fn set_options( ("color-only", color_only), ("commit-decoration-style", commit_decoration_style), ("commit-style", commit_style), - ("dark", dark), ("file-added-label", file_added_label), ("file-decoration-style", file_decoration_style), ("file-modified-label", file_modified_label), @@ -122,7 +98,6 @@ pub fn set_options( ("hunk-header-decoration-style", hunk_header_decoration_style), ("hunk-header-style", hunk_header_style), ("keep-plus-minus-markers", keep_plus_minus_markers), - ("light", light), ("max-line-distance", max_line_distance), // Hack: minus-style must come before minus-*emph-style because the latter default // dynamically to the value of the former. @@ -151,7 +126,6 @@ pub fn set_options( ("plus-empty-line-marker-style", plus_empty_line_marker_style), ("plus-non-emph-style", plus_non_emph_style), ("raw", raw), - ("syntax-theme", syntax_theme), ("tabs", tab_width), ("whitespace-error-style", whitespace_error_style), ("width", width), @@ -166,6 +140,41 @@ pub fn set_options( ); } +#[allow(non_snake_case)] +fn set__light__dark__syntax_theme__options( + opt: &mut cli::Opt, + builtin_features: &HashMap<String, features::BuiltinFeature>, + git_config: &mut Option<git_config::GitConfig>, + arg_matches: &clap::ArgMatches, +) { + let validate_light_and_dark = |opt: &cli::Opt| { + if opt.light && opt.dark { + eprintln!("--light and --dark cannot be used together."); + process::exit(1); + } + }; + validate_light_and_dark(&opt); + if !(opt.light || opt.dark) { + set_options!( + [("dark", dark), ("light", light)], + opt, + builtin_features, + git_config, + arg_matches, + false + ); + } + validate_light_and_dark(&opt); + set_options!( + [("syntax-theme", syntax_theme)], + opt, + builtin_features, + git_config, + arg_matches, + false + ); +} + /// Features are processed differently from all other options. The role of this function is to /// collect all configuration related to features and summarize it as a single list /// (space-separated string) of enabled features. The list is arranged in order of increasing diff --git a/src/options/theme.rs b/src/options/theme.rs new file mode 100644 index 00000000..fdee8b03 --- /dev/null +++ b/src/options/theme.rs @@ -0,0 +1,251 @@ +/// Delta doesn't have a formal concept of a "theme". What it has is +/// (a) the choice of syntax-highlighting theme +/// (b) the choice of light-background-mode vs dark-background-mode, which determine certain +/// default color choices +/// This module sets those options. If the light/dark background mode choice is not made explicitly +/// by the user, it is determined by the classification of the syntax theme into light-background +/// vs dark-background syntax themes. If the user didn't choose a syntax theme, a dark-background +/// default is selected. +use syntect::highlighting::ThemeSet; + +use crate::bat::assets::HighlightingAssets; +use crate::cli; +use crate::env; + +#[allow(non_snake_case)] +pub fn set__is_light_mode__syntax_theme__syntax_set( + opt: &mut cli::Opt, + assets: HighlightingAssets, +) { + let syntax_theme_name_from_bat_theme = env::get_env_var("BAT_THEME"); + let (is_light_mode, syntax_theme_name) = get_is_light_mode_and_syntax_theme_name( + opt.syntax_theme.as_ref(), + syntax_theme_name_from_bat_theme.as_ref(), + opt.light, + &assets.theme_set, + ); + opt.computed.is_light_mode = is_light_mode; + + opt.computed.syntax_theme = if is_no_syntax_highlighting_syntax_theme_name(&syntax_theme_name) { + None + } else { + Some(assets.theme_set.themes[&syntax_theme_name].clone()) + }; + opt.computed.syntax_set = assets.syntax_set; +} + +pub fn is_light_syntax_theme(theme: &str) -> bool { + LIGHT_SYNTAX_THEMES.contains(&theme) +} + +const LIGHT_SYNTAX_THEMES: [&str; 5] = [ + "GitHub", + "Monokai Extended Light", + "OneHalfLight", + "ansi-light", + "Solarized (light)", +]; + +const DEFAULT_LIGHT_SYNTAX_THEME: &str = "GitHub"; +const DEFAULT_DARK_SYNTAX_THEME: &str = "Monokai Extended"; + +fn is_no_syntax_highlighting_syntax_theme_name(theme_name: &str) -> bool { + theme_name.to_lowercase() == "none" +} + +/// 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_syntax_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_syntax_theme_name_or_none(theme_arg, theme_set); + let bat_theme_env_var = valid_syntax_theme_name_or_none(bat_theme_env_var, theme_set); + match (theme_arg, bat_theme_env_var, light_mode_arg) { + (None, None, false) => (false, DEFAULT_DARK_SYNTAX_THEME.to_string()), + (Some(theme_name), _, false) => (is_light_syntax_theme(&theme_name), theme_name), + (None, Some(theme_name), false) => (is_light_syntax_theme(&theme_name), theme_name), + (None, None, true) => (true, DEFAULT_LIGHT_SYNTAX_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_syntax_theme_name_or_none( + theme_name: Option<&String>, + theme_set: &ThemeSet, +) -> Option<String> { + match theme_name { + Some(name) + if is_no_syntax_highlighting_syntax_theme_name(name) + || theme_set.themes.contains_key(name) => + { + Some(name.to_string()) + } + _ => None, + } +} + +#[cfg(test)] +mod tests { + use std::env; + + use super::*; + use crate::color; + use crate::tests::integration_test_utils::integration_test_utils; + + #[test] + fn test_syntax_theme_selection() { + #[derive(PartialEq)] + enum Mode { + Light, + Dark, + }; + for ( + syntax_theme, + bat_theme_env_var, + mode, // (--light, --dark) + expected_syntax_theme, + expected_mode, + ) in vec![ + (None, "", None, DEFAULT_DARK_SYNTAX_THEME, Mode::Dark), + (Some("GitHub"), "", None, "GitHub", Mode::Light), + (Some("GitHub"), "1337", None, "GitHub", Mode::Light), + (None, "1337", None, "1337", Mode::Dark), + ( + None, + "<not set>", + None, + DEFAULT_DARK_SYNTAX_THEME, + Mode::Dark, + ), + ( + None, + "", + Some(Mode::Light), + DEFAULT_LIGHT_SYNTAX_THEME, + Mode::Light, + ), + ( + None, + "", + Some(Mode::Dark), + DEFAULT_DARK_SYNTAX_THEME, + Mode::Dark, + ), + ( + None, + "<@@@@@>", + Some(Mode::Light), + DEFAULT_LIGHT_SYNTAX_THEME, + Mode::Light, + ), + (None, "GitHub", Some(Mode::Light), "GitHub", Mode::Light), + (Some("none"), "", None, "none", Mode::Dark), + (Some("None"), "", 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 mut args = vec![]; + if let Some(syntax_theme) = syntax_theme { + args.push("--syntax-theme"); + args.push(syntax_theme); + } + let is_true_color = true; + if is_true_color { + args.push("--24-bit-color"); + args.push("always"); + } else { + args.push("--24-bit-color"); + args.push("never"); + } + match mode { + Some(Mode::Light) => { + args.push("--light"); + } + Some(Mode::Dark) => { + args.push("--dark"); + } + None => {} + } + let config = integration_test_utils::make_config(&args); + assert_eq!( + &config + .syntax_theme + .clone() + .map(|t| t.name.unwrap()) + .unwrap_or("none".to_string()), + expected_syntax_theme + ); + if is_no_syntax_highlighting_syntax_theme_name(expected_syntax_theme) { + assert!(config.syntax_theme.is_none()) + } else { + assert_eq!( + config.syntax_theme.unwrap().name.as_ref().unwrap(), + expected_syntax_theme + ); + } + assert_eq!( + config.minus_style.ansi_term_style.background.unwrap(), + color::get_minus_background_color_default( + expected_mode == Mode::Light, + is_true_color + ) + ); + assert_eq!( + config.minus_emph_style.ansi_term_style.background.unwrap(), + color::get_minus_emph_background_color_default( + expected_mode == Mode::Light, + is_true_color + ) + ); + assert_eq!( + config.plus_style.ansi_term_style.background.unwrap(), + color::get_plus_background_color_default( + expected_mode == Mode::Light, + is_true_color + ) + ); + assert_eq!( + config.plus_emph_style.ansi_term_style.background.unwrap(), + color::get_plus_emph_background_color_default( + expected_mode == Mode::Light, + is_true_color + ) + ); + } + } +} |