From bc42149a726b73e5e4cd6b8afdeb340a3ae23c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sun, 18 Aug 2024 14:59:14 +0200 Subject: [PATCH] Merge color scheme options into theme / BAT_THEME --- assets/completions/_bat.ps1.in | 1 - assets/completions/bat.bash.in | 3 - assets/completions/bat.fish.in | 9 --- assets/completions/bat.zsh.in | 1 - assets/manual/bat.1.in | 26 ++------ doc/long-help.txt | 17 ----- doc/short-help.txt | 2 - src/bin/bat/app.rs | 25 ++------ src/bin/bat/clap_app.rs | 26 -------- src/bin/bat/main.rs | 2 +- src/theme.rs | 111 ++++++++++++++++++++++----------- tests/integration_tests.rs | 2 - 12 files changed, 83 insertions(+), 142 deletions(-) diff --git a/assets/completions/_bat.ps1.in b/assets/completions/_bat.ps1.in index ac66ccc8..b6f62aae 100644 --- a/assets/completions/_bat.ps1.in +++ b/assets/completions/_bat.ps1.in @@ -32,7 +32,6 @@ Register-ArgumentCompleter -Native -CommandName '{{PROJECT_EXECUTABLE}}' -Script [CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'When to use colors (*auto*, never, always).') [CompletionResult]::new('--italic-text', 'italic-text', [CompletionResultType]::ParameterName, 'Use italics in output (always, *never*)') [CompletionResult]::new('--decorations', 'decorations', [CompletionResultType]::ParameterName, 'When to show the decorations (*auto*, never, always).') - [CompletionResult]::new('--color-scheme', 'color-scheme', [CompletionResultType]::ParameterName, 'Whether to choose a dark or light syntax highlighting theme (*auto*, auto:always, dark, light, system).') [CompletionResult]::new('--paging', 'paging', [CompletionResultType]::ParameterName, 'Specify when to use the pager, or use `-P` to disable (*auto*, never, always).') [CompletionResult]::new('--pager', 'pager', [CompletionResultType]::ParameterName, 'Determine which pager to use.') [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Use the specified syntax for files matching the glob pattern (''*.cpp:C++'').') diff --git a/assets/completions/bat.bash.in b/assets/completions/bat.bash.in index 0a01a054..162d1c53 100644 --- a/assets/completions/bat.bash.in +++ b/assets/completions/bat.bash.in @@ -104,8 +104,6 @@ _bat() { COMPREPLY=($(compgen -W "auto never always" -- "$cur")) return 0 ;; - --color-scheme) - COMPREPLY=($(compgen -W "auto auto:always dark light system" -- "$cur")) --italic-text) COMPREPLY=($(compgen -W "always never" -- "$cur")) return 0 @@ -176,7 +174,6 @@ _bat() { --theme --theme-dark --theme-light - --color-scheme --list-themes --squeeze-blank --squeeze-limit diff --git a/assets/completions/bat.fish.in b/assets/completions/bat.fish.in index 33cf8264..9907718f 100644 --- a/assets/completions/bat.fish.in +++ b/assets/completions/bat.fish.in @@ -99,13 +99,6 @@ set -l color_opts ' ' set -l decorations_opts $color_opts set -l paging_opts $color_opts -set -l color_scheme_opts " - auto\t'Use the terminal\'s color scheme if the output is not redirected (default)' - auto:always\t'Always use the terminal\'s color scheme' - dark\t'Use a dark syntax highlighting theme' - light\t'Use a light syntax highlighting theme' - system\t'Query the OS for its color scheme (macOS only)' -" # Include some examples so we can indicate the default. set -l pager_opts ' @@ -150,8 +143,6 @@ complete -c $bat -l config-file -f -d "Display location of configuration file" - complete -c $bat -l decorations -x -a "$decorations_opts" -d "When to use --style decorations" -n __bat_no_excl_args -complete -c $bat -l color-scheme -x -a "$color_scheme_opts" -d "Whether to choose a dark or light syntax highlighting theme" -n __bat_no_excl_args - complete -c $bat -l diagnostic -d "Print diagnostic info for bug reports" -n __fish_is_first_arg complete -c $bat -s d -l diff -d "Only show lines with Git changes" -n __bat_no_excl_args diff --git a/assets/completions/bat.zsh.in b/assets/completions/bat.zsh.in index 4bcae11c..bec0d3c9 100644 --- a/assets/completions/bat.zsh.in +++ b/assets/completions/bat.zsh.in @@ -40,7 +40,6 @@ _{{PROJECT_EXECUTABLE}}_main() { --color='[specify when to use colors]:when:(auto never always)' --italic-text='[use italics in output]:when:(always never)' --decorations='[specify when to show the decorations]:when:(auto never always)' - --color-scheme="[whether to choose a dark or light syntax highlighting theme]:scheme:(auto auto:always dark light system)" --paging='[specify when to use the pager]:when:(auto never always)' '(-m --map-syntax)'{-m+,--map-syntax=}'[map a glob pattern to an existing syntax name]: :->syntax-maps' '(--theme)'--theme='[set the color theme for syntax highlighting]:theme:->themes' diff --git a/assets/manual/bat.1.in b/assets/manual/bat.1.in index 5dcabe2d..05239d50 100644 --- a/assets/manual/bat.1.in +++ b/assets/manual/bat.1.in @@ -117,24 +117,6 @@ Specify when to use the decorations that have been specified via '\-\-style'. Th automatic mode only enables decorations if an interactive terminal is detected. Possible values: *auto*, never, always. .HP -\fB\-\-detect\-color\-scheme\fR -.IP -Specify when to query the terminal for its colors in order to pick an appropriate syntax -highlighting theme. Use \fB\-\-theme-light\fP and \fB\-\-theme-dark\fP (or the environment variables -\fBBAT_THEME_LIGHT\fP and \fBBAT_THEME_DARK\fP) to configure which themes are picked. You can also use -\fP\-\-theme\fP to set a theme that is used regardless of the terminal's colors. -.IP -\fI\fP can be one of: -.RS -.IP "\fBauto\fP" -Only query the terminals colors if the output is not redirected. This is to prevent -race conditions with pagers such as less. -.IP "never" -Never query the terminal for its colors and assume that the terminal has a dark background. -.IP "always" -Always query the terminal for its colors, regardless of whether or not the output is redirected. -.RE -.HP \fB\-f\fR, \fB\-\-force\-colorization\fR .IP Alias for '--decorations=always --color=always'. This is useful \ @@ -177,14 +159,14 @@ export the \fBBAT_THEME\fP environment variable (e.g.: \fBexport BAT_THEME="..." \fB\-\-theme\-dark\fR .IP Sets the theme name for syntax highlighting used when the terminal uses a dark background. -To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or +To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or export the \fBBAT_THEME_DARK\fP environment variable (e.g. \fBexport BAT_THEME_DARK="..."\fP). This option is ignored if \fB\-\-theme\fP option is set. .HP \fB\-\-theme\-light\fR .IP Sets the theme name for syntax highlighting used when the terminal uses a dark background. -To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or +To set a default theme, add the \fB\-\-theme-dark="..."\fP option to the configuration file or export the \fBBAT_THEME_LIGHT\fP environment variable (e.g. \fBexport BAT_THEME_LIGHT="..."\fP). This option is ignored if \fB\-\-theme\fP option is set. .HP @@ -339,7 +321,7 @@ To use the preprocessor, call: \fB{{PROJECT_EXECUTABLE}} --lessopen\fR -Alternatively, the preprocessor may be enabled by default by adding the '\-\-lessopen' option to the configuration file. +Alternatively, the preprocessor may be enabled by default by adding the '\-\-lessopen' option to the configuration file. To temporarily disable the preprocessor if it is enabled by default, call: @@ -355,7 +337,7 @@ Enable the $LESSOPEN preprocessor. .IP Disable the $LESSOPEN preprocessor if enabled (overrides --lessopen) .PP -For more information, see the "INPUT PREPROCESSOR" section of less(1). +For more information, see the "INPUT PREPROCESSOR" section of less(1). .SH "MORE INFORMATION" diff --git a/doc/long-help.txt b/doc/long-help.txt index c374a039..8d5a3a5e 100644 --- a/doc/long-help.txt +++ b/doc/long-help.txt @@ -114,23 +114,6 @@ Options: add the '--theme="..."' option to the configuration file or export the BAT_THEME environment variable (e.g.: export BAT_THEME="..."). - --color-scheme - Specify whether to choose a dark or light syntax highlighting theme. Use '--theme-light' - and '--theme-dark' (or the environment variables BAT_THEME_LIGHT and BAT_THEME_DARK) to - configure which themes are picked. You may also use '--theme' to set a theme that is used - regardless of this choice. - - Possible values: - * auto (default): - Query the terminals for its color scheme if the output is not redirected. This is to - prevent race conditions with pagers such as less. - * 'auto:always': - Always query the terminal for its color scheme, regardless of whether or not the - output is redirected. - * dark: Use a dark syntax highlighting theme. - * light: Use a light syntax highlighting theme. - * system: Query the OS for its color scheme. Only works on macOS. - --theme-light Sets the theme name for syntax highlighting used when the terminal uses a light background. Use '--list-themes' to see all available themes. To set a default theme, add diff --git a/doc/short-help.txt b/doc/short-help.txt index f17a6d9d..d5c35059 100644 --- a/doc/short-help.txt +++ b/doc/short-help.txt @@ -41,8 +41,6 @@ Options: Use the specified syntax for files matching the glob pattern ('*.cpp:C++'). --theme Set the color theme for syntax highlighting. - --color-scheme - Specify whether to choose a dark or light theme. --theme-light Sets the color theme for syntax highlighting used for light backgrounds. --theme-dark diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index d2e5b4db..6d83c9c9 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -9,7 +9,7 @@ use crate::{ config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, }; use bat::style::StyleComponentList; -use bat::theme::{theme, ColorSchemePreference, DetectColorScheme, ThemeOptions, ThemeRequest}; +use bat::theme::{theme, ThemeName, ThemeOptions, ThemePreference}; use bat::StripAnsiMode; use clap::ArgMatches; @@ -418,35 +418,20 @@ impl App { let theme = self .matches .get_one::("theme") - .map(|t| ThemeRequest::from_str(t).unwrap()); + .map(|t| ThemePreference::from_str(t).unwrap()) + .unwrap_or_default(); let theme_dark = self .matches .get_one::("theme-dark") - .map(|t| ThemeRequest::from_str(t).unwrap()); + .map(|t| ThemeName::from_str(t).unwrap()); let theme_light = self .matches .get_one::("theme-light") - .map(|t| ThemeRequest::from_str(t).unwrap()); + .map(|t| ThemeName::from_str(t).unwrap()); ThemeOptions { theme, theme_dark, theme_light, - color_scheme: self.color_scheme_preference(), - } - } - - pub(crate) fn color_scheme_preference(&self) -> ColorSchemePreference { - match self - .matches - .get_one::("color-scheme") - .map(|s| s.as_str()) - { - Some("auto") => ColorSchemePreference::Auto(DetectColorScheme::Auto), - Some("auto:always") => ColorSchemePreference::Auto(DetectColorScheme::Always), - Some("dark") => ColorSchemePreference::Dark, - Some("light") => ColorSchemePreference::Light, - Some("system") => ColorSchemePreference::System, - _ => unreachable!("other values for --color-scheme are not allowed"), } } } diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs index 0857dba4..e8056f3a 100644 --- a/src/bin/bat/clap_app.rs +++ b/src/bin/bat/clap_app.rs @@ -383,32 +383,6 @@ pub fn build_app(interactive_output: bool) -> Command { BAT_THEME=\"...\").", ), ) - .arg( - Arg::new("color-scheme") - .long("color-scheme") - .overrides_with("color-scheme") - .value_name("scheme") - .value_parser(["auto", "auto:always", "dark", "light", "system"]) - .default_value("auto") - .hide_default_value(true) - .help("Specify whether to choose a dark or light theme.") - .long_help( - "Specify whether to choose a dark or light syntax highlighting theme. \ - Use '--theme-light' and '--theme-dark' (or the environment variables \ - BAT_THEME_LIGHT and BAT_THEME_DARK) to configure which themes are picked. \ - You may also use '--theme' to set a theme that is used regardless of this choice.\n\n\ - Possible values:\n\ - * auto (default):\n \ - Query the terminals for its color scheme if the output is not redirected. \ - This is to prevent race conditions with pagers such as less.\n\ - * 'auto:always':\n \ - Always query the terminal for its color scheme, \ - regardless of whether or not the output is redirected.\n\ - * dark: Use a dark syntax highlighting theme.\n\ - * light: Use a light syntax highlighting theme.\n\ - * system: Query the OS for its color scheme. Only works on macOS.\n\ - "), - ) .arg( Arg::new("theme-light") .long("theme-light") diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 891390c1..09470504 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -384,7 +384,7 @@ fn run() -> Result { &config, config_dir, cache_dir, - app.color_scheme_preference(), + ColorSchemePreference::default(), )?; Ok(true) } else if app.matches.get_flag("config-file") { diff --git a/src/theme.rs b/src/theme.rs index 1c6d7e55..5f6460cc 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -29,46 +29,79 @@ pub fn color_scheme(preference: ColorSchemePreference) -> ColorScheme { #[derive(Debug, Default, PartialEq, Eq)] pub struct ThemeOptions { /// Always use this theme regardless of the terminal's background color. - pub theme: Option, + /// This corresponds with the `BAT_THEME` environment variable and the `--theme` option. + pub theme: ThemePreference, /// The theme to use in case the terminal uses a dark background with light text. - pub theme_dark: Option, + /// This corresponds with the `BAT_THEME_DARK` environment variable and the `--theme-dark` option. + pub theme_dark: Option, /// The theme to use in case the terminal uses a light background with dark text. - pub theme_light: Option, - /// How to choose between dark and light. - pub color_scheme: ColorSchemePreference, + /// This corresponds with the `BAT_THEME_LIGHT` environment variable and the `--theme-light` option. + pub theme_light: Option, +} + +/// What theme should `bat` use? +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum ThemePreference { + /// Choose between [`ThemeOptions::theme_dark`] and [`ThemeOptions::theme_light`] + /// based on the terminal's (or the OS') color scheme. + Auto(ColorSchemePreference), + /// Always use the same theme regardless of the terminal's color scheme. + Fixed(ThemeName), +} + +impl Default for ThemePreference { + fn default() -> Self { + ThemePreference::Auto(ColorSchemePreference::default()) + } +} + +impl FromStr for ThemePreference { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + use ThemePreference::*; + match s { + "auto" => Ok(Auto(ColorSchemePreference::default())), + "auto:always" => Ok(Auto(ColorSchemePreference::Auto(DetectColorScheme::Always))), + "auto:system" => Ok(Auto(ColorSchemePreference::System)), + "dark" => Ok(Auto(ColorSchemePreference::Dark)), + "light" => Ok(Auto(ColorSchemePreference::Light)), + _ => ThemeName::from_str(s).map(Fixed), + } + } } /// The name of a theme or the default theme. /// /// ``` -/// # use bat::theme::ThemeRequest; +/// # use bat::theme::ThemeName; /// # use std::str::FromStr as _; -/// assert_eq!(ThemeRequest::Default, ThemeRequest::from_str("default").unwrap()); -/// assert_eq!(ThemeRequest::Named("example".to_string()), ThemeRequest::from_str("example").unwrap()); +/// assert_eq!(ThemeName::Default, ThemeName::from_str("default").unwrap()); +/// assert_eq!(ThemeName::Named("example".to_string()), ThemeName::from_str("example").unwrap()); /// ``` #[derive(Debug, PartialEq, Eq, Hash)] -pub enum ThemeRequest { +pub enum ThemeName { Named(String), Default, } -impl FromStr for ThemeRequest { +impl FromStr for ThemeName { type Err = Infallible; fn from_str(s: &str) -> Result { if s == "default" { - Ok(ThemeRequest::Default) + Ok(ThemeName::Default) } else { - Ok(ThemeRequest::Named(s.to_owned())) + Ok(ThemeName::Named(s.to_owned())) } } } -impl ThemeRequest { +impl ThemeName { fn into_theme(self, color_scheme: ColorScheme) -> String { match self { - ThemeRequest::Named(t) => t, - ThemeRequest::Default => default_theme(color_scheme).to_owned(), + ThemeName::Named(t) => t, + ThemeName::Default => default_theme(color_scheme).to_owned(), } } } @@ -124,17 +157,18 @@ fn color_scheme_impl( fn theme_from_detector(options: ThemeOptions, detector: &dyn ColorSchemeDetector) -> String { // Implementation note: This function is mostly pure (i.e. it has no side effects) for the sake of testing. // All the side effects (e.g. querying the terminal for its colors) are performed in the detector. - if let Some(theme) = options.theme { - theme.into_theme(ColorScheme::default()) - } else { - let color_scheme = color_scheme_impl(options.color_scheme, detector); - choose_theme(options, color_scheme) - .map(|t| t.into_theme(color_scheme)) - .unwrap_or_else(|| default_theme(color_scheme).to_owned()) + match options.theme { + ThemePreference::Fixed(theme_name) => theme_name.into_theme(ColorScheme::default()), + ThemePreference::Auto(color_scheme_preference) => { + let color_scheme = color_scheme_impl(color_scheme_preference, detector); + choose_theme(options, color_scheme) + .map(|t| t.into_theme(color_scheme)) + .unwrap_or_else(|| default_theme(color_scheme).to_owned()) + } } } -fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { +fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option { match color_scheme { ColorScheme::Dark => options.theme_dark, ColorScheme::Light => options.theme_light, @@ -220,7 +254,6 @@ impl ColorSchemeDetector for Option { mod tests { use super::ColorScheme::*; use super::ColorSchemePreference as Pref; - use super::DetectColorScheme::*; use super::*; use std::cell::Cell; use std::iter; @@ -233,7 +266,7 @@ mod tests { for pref in [Pref::Dark, Pref::Light] { let detector = DetectorStub::should_detect(Some(Dark)); let options = ThemeOptions { - color_scheme: pref, + theme: ThemePreference::Auto(pref), ..Default::default() }; _ = theme_from_detector(options, &detector); @@ -249,7 +282,9 @@ mod tests { ]; for detector in detectors { let options = ThemeOptions { - color_scheme: Pref::Auto(Always), + theme: ThemePreference::Auto(ColorSchemePreference::Auto( + DetectColorScheme::Always, + )), ..Default::default() }; _ = theme_from_detector(options, &detector); @@ -280,13 +315,13 @@ mod tests { for color_scheme in optional(color_schemes()) { for options in [ ThemeOptions { - theme: Some(ThemeRequest::Named("Theme".to_string())), + theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())), ..Default::default() }, ThemeOptions { - theme: Some(ThemeRequest::Named("Theme".to_string())), - theme_dark: Some(ThemeRequest::Named("Dark Theme".to_string())), - theme_light: Some(ThemeRequest::Named("Light Theme".to_string())), + theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())), + theme_dark: Some(ThemeName::Named("Dark Theme".to_string())), + theme_light: Some(ThemeName::Named("Light Theme".to_string())), ..Default::default() }, ] { @@ -299,7 +334,7 @@ mod tests { #[test] fn detector_is_not_called_if_theme_is_present() { let options = ThemeOptions { - theme: Some(ThemeRequest::Named("Theme".to_string())), + theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())), ..Default::default() }; let detector = DetectorStub::should_detect(Some(Dark)); @@ -326,7 +361,7 @@ mod tests { fn dark_if_requested_explicitly_through_theme() { for color_scheme in optional(color_schemes()) { let options = ThemeOptions { - theme: Some(ThemeRequest::Default), + theme: ThemePreference::Fixed(ThemeName::Default), ..Default::default() }; let detector = ConstantDetector(color_scheme); @@ -343,8 +378,8 @@ mod tests { for options in [ ThemeOptions::default(), ThemeOptions { - theme_dark: Some(ThemeRequest::Default), - theme_light: Some(ThemeRequest::Default), + theme_dark: Some(ThemeName::Default), + theme_light: Some(ThemeName::Default), ..Default::default() }, ] { @@ -365,8 +400,8 @@ mod tests { fn chooses_dark_theme_if_dark_or_unknown() { for color_scheme in [Some(Dark), None] { let options = ThemeOptions { - theme_dark: Some(ThemeRequest::Named("Dark".to_string())), - theme_light: Some(ThemeRequest::Named("Light".to_string())), + theme_dark: Some(ThemeName::Named("Dark".to_string())), + theme_light: Some(ThemeName::Named("Light".to_string())), ..Default::default() }; let detector = ConstantDetector(color_scheme); @@ -377,8 +412,8 @@ mod tests { #[test] fn chooses_light_theme_if_light() { let options = ThemeOptions { - theme_dark: Some(ThemeRequest::Named("Dark".to_string())), - theme_light: Some(ThemeRequest::Named("Light".to_string())), + theme_dark: Some(ThemeName::Named("Dark".to_string())), + theme_light: Some(ThemeName::Named("Light".to_string())), ..Default::default() }; let detector = ConstantDetector(Some(ColorScheme::Light)); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index e4b73c59..582b255d 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -279,7 +279,6 @@ fn list_themes_with_colors() { bat() .arg("--color=always") - .arg("--color-scheme=dark") .arg("--list-themes") .assert() .success() @@ -296,7 +295,6 @@ fn list_themes_without_colors() { bat() .arg("--color=never") - .arg("--color-scheme=dark") .arg("--list-themes") .assert() .success()