Merge color scheme options into theme / BAT_THEME

This commit is contained in:
Tau Gärtli 2024-08-18 14:59:14 +02:00
parent b9b981f657
commit bc42149a72
No known key found for this signature in database
12 changed files with 83 additions and 142 deletions

View File

@ -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('--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('--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('--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('--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('--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++'').') [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Use the specified syntax for files matching the glob pattern (''*.cpp:C++'').')

View File

@ -104,8 +104,6 @@ _bat() {
COMPREPLY=($(compgen -W "auto never always" -- "$cur")) COMPREPLY=($(compgen -W "auto never always" -- "$cur"))
return 0 return 0
;; ;;
--color-scheme)
COMPREPLY=($(compgen -W "auto auto:always dark light system" -- "$cur"))
--italic-text) --italic-text)
COMPREPLY=($(compgen -W "always never" -- "$cur")) COMPREPLY=($(compgen -W "always never" -- "$cur"))
return 0 return 0
@ -176,7 +174,6 @@ _bat() {
--theme --theme
--theme-dark --theme-dark
--theme-light --theme-light
--color-scheme
--list-themes --list-themes
--squeeze-blank --squeeze-blank
--squeeze-limit --squeeze-limit

View File

@ -99,13 +99,6 @@ set -l color_opts '
' '
set -l decorations_opts $color_opts set -l decorations_opts $color_opts
set -l paging_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. # Include some examples so we can indicate the default.
set -l pager_opts ' 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 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 -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 complete -c $bat -s d -l diff -d "Only show lines with Git changes" -n __bat_no_excl_args

View File

@ -40,7 +40,6 @@ _{{PROJECT_EXECUTABLE}}_main() {
--color='[specify when to use colors]:when:(auto never always)' --color='[specify when to use colors]:when:(auto never always)'
--italic-text='[use italics in output]:when:(always never)' --italic-text='[use italics in output]:when:(always never)'
--decorations='[specify when to show the decorations]:when:(auto never always)' --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)' --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' '(-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' '(--theme)'--theme='[set the color theme for syntax highlighting]:theme:->themes'

View File

@ -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 automatic mode only enables decorations if an interactive terminal is detected. Possible
values: *auto*, never, always. values: *auto*, never, always.
.HP .HP
\fB\-\-detect\-color\-scheme\fR <when>
.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<when>\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 \fB\-f\fR, \fB\-\-force\-colorization\fR
.IP .IP
Alias for '--decorations=always --color=always'. This is useful \ Alias for '--decorations=always --color=always'. This is useful \

View File

@ -114,23 +114,6 @@ Options:
add the '--theme="..."' option to the configuration file or export the BAT_THEME add the '--theme="..."' option to the configuration file or export the BAT_THEME
environment variable (e.g.: export BAT_THEME="..."). environment variable (e.g.: export BAT_THEME="...").
--color-scheme <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 <theme> --theme-light <theme>
Sets the theme name for syntax highlighting used when the terminal uses a 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 background. Use '--list-themes' to see all available themes. To set a default theme, add

View File

@ -41,8 +41,6 @@ Options:
Use the specified syntax for files matching the glob pattern ('*.cpp:C++'). Use the specified syntax for files matching the glob pattern ('*.cpp:C++').
--theme <theme> --theme <theme>
Set the color theme for syntax highlighting. Set the color theme for syntax highlighting.
--color-scheme <scheme>
Specify whether to choose a dark or light theme.
--theme-light <theme> --theme-light <theme>
Sets the color theme for syntax highlighting used for light backgrounds. Sets the color theme for syntax highlighting used for light backgrounds.
--theme-dark <theme> --theme-dark <theme>

View File

@ -9,7 +9,7 @@ use crate::{
config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars}, config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars},
}; };
use bat::style::StyleComponentList; use bat::style::StyleComponentList;
use bat::theme::{theme, ColorSchemePreference, DetectColorScheme, ThemeOptions, ThemeRequest}; use bat::theme::{theme, ThemeName, ThemeOptions, ThemePreference};
use bat::StripAnsiMode; use bat::StripAnsiMode;
use clap::ArgMatches; use clap::ArgMatches;
@ -418,35 +418,20 @@ impl App {
let theme = self let theme = self
.matches .matches
.get_one::<String>("theme") .get_one::<String>("theme")
.map(|t| ThemeRequest::from_str(t).unwrap()); .map(|t| ThemePreference::from_str(t).unwrap())
.unwrap_or_default();
let theme_dark = self let theme_dark = self
.matches .matches
.get_one::<String>("theme-dark") .get_one::<String>("theme-dark")
.map(|t| ThemeRequest::from_str(t).unwrap()); .map(|t| ThemeName::from_str(t).unwrap());
let theme_light = self let theme_light = self
.matches .matches
.get_one::<String>("theme-light") .get_one::<String>("theme-light")
.map(|t| ThemeRequest::from_str(t).unwrap()); .map(|t| ThemeName::from_str(t).unwrap());
ThemeOptions { ThemeOptions {
theme, theme,
theme_dark, theme_dark,
theme_light, theme_light,
color_scheme: self.color_scheme_preference(),
}
}
pub(crate) fn color_scheme_preference(&self) -> ColorSchemePreference {
match self
.matches
.get_one::<String>("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"),
} }
} }
} }

View File

@ -383,32 +383,6 @@ pub fn build_app(interactive_output: bool) -> Command {
BAT_THEME=\"...\").", 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(
Arg::new("theme-light") Arg::new("theme-light")
.long("theme-light") .long("theme-light")

View File

@ -384,7 +384,7 @@ fn run() -> Result<bool> {
&config, &config,
config_dir, config_dir,
cache_dir, cache_dir,
app.color_scheme_preference(), ColorSchemePreference::default(),
)?; )?;
Ok(true) Ok(true)
} else if app.matches.get_flag("config-file") { } else if app.matches.get_flag("config-file") {

View File

@ -29,46 +29,79 @@ pub fn color_scheme(preference: ColorSchemePreference) -> ColorScheme {
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
pub struct ThemeOptions { pub struct ThemeOptions {
/// Always use this theme regardless of the terminal's background color. /// Always use this theme regardless of the terminal's background color.
pub theme: Option<ThemeRequest>, /// 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. /// The theme to use in case the terminal uses a dark background with light text.
pub theme_dark: Option<ThemeRequest>, /// This corresponds with the `BAT_THEME_DARK` environment variable and the `--theme-dark` option.
pub theme_dark: Option<ThemeName>,
/// The theme to use in case the terminal uses a light background with dark text. /// The theme to use in case the terminal uses a light background with dark text.
pub theme_light: Option<ThemeRequest>, /// This corresponds with the `BAT_THEME_LIGHT` environment variable and the `--theme-light` option.
/// How to choose between dark and light. pub theme_light: Option<ThemeName>,
pub color_scheme: ColorSchemePreference, }
/// 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<Self, Self::Err> {
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. /// The name of a theme or the default theme.
/// ///
/// ``` /// ```
/// # use bat::theme::ThemeRequest; /// # use bat::theme::ThemeName;
/// # use std::str::FromStr as _; /// # use std::str::FromStr as _;
/// assert_eq!(ThemeRequest::Default, ThemeRequest::from_str("default").unwrap()); /// assert_eq!(ThemeName::Default, ThemeName::from_str("default").unwrap());
/// assert_eq!(ThemeRequest::Named("example".to_string()), ThemeRequest::from_str("example").unwrap()); /// assert_eq!(ThemeName::Named("example".to_string()), ThemeName::from_str("example").unwrap());
/// ``` /// ```
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
pub enum ThemeRequest { pub enum ThemeName {
Named(String), Named(String),
Default, Default,
} }
impl FromStr for ThemeRequest { impl FromStr for ThemeName {
type Err = Infallible; type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "default" { if s == "default" {
Ok(ThemeRequest::Default) Ok(ThemeName::Default)
} else { } 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 { fn into_theme(self, color_scheme: ColorScheme) -> String {
match self { match self {
ThemeRequest::Named(t) => t, ThemeName::Named(t) => t,
ThemeRequest::Default => default_theme(color_scheme).to_owned(), 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 { 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. // 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. // All the side effects (e.g. querying the terminal for its colors) are performed in the detector.
if let Some(theme) = options.theme { match options.theme {
theme.into_theme(ColorScheme::default()) ThemePreference::Fixed(theme_name) => theme_name.into_theme(ColorScheme::default()),
} else { ThemePreference::Auto(color_scheme_preference) => {
let color_scheme = color_scheme_impl(options.color_scheme, detector); let color_scheme = color_scheme_impl(color_scheme_preference, detector);
choose_theme(options, color_scheme) choose_theme(options, color_scheme)
.map(|t| t.into_theme(color_scheme)) .map(|t| t.into_theme(color_scheme))
.unwrap_or_else(|| default_theme(color_scheme).to_owned()) .unwrap_or_else(|| default_theme(color_scheme).to_owned())
}
} }
} }
fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option<ThemeRequest> { fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option<ThemeName> {
match color_scheme { match color_scheme {
ColorScheme::Dark => options.theme_dark, ColorScheme::Dark => options.theme_dark,
ColorScheme::Light => options.theme_light, ColorScheme::Light => options.theme_light,
@ -220,7 +254,6 @@ impl ColorSchemeDetector for Option<ColorScheme> {
mod tests { mod tests {
use super::ColorScheme::*; use super::ColorScheme::*;
use super::ColorSchemePreference as Pref; use super::ColorSchemePreference as Pref;
use super::DetectColorScheme::*;
use super::*; use super::*;
use std::cell::Cell; use std::cell::Cell;
use std::iter; use std::iter;
@ -233,7 +266,7 @@ mod tests {
for pref in [Pref::Dark, Pref::Light] { for pref in [Pref::Dark, Pref::Light] {
let detector = DetectorStub::should_detect(Some(Dark)); let detector = DetectorStub::should_detect(Some(Dark));
let options = ThemeOptions { let options = ThemeOptions {
color_scheme: pref, theme: ThemePreference::Auto(pref),
..Default::default() ..Default::default()
}; };
_ = theme_from_detector(options, &detector); _ = theme_from_detector(options, &detector);
@ -249,7 +282,9 @@ mod tests {
]; ];
for detector in detectors { for detector in detectors {
let options = ThemeOptions { let options = ThemeOptions {
color_scheme: Pref::Auto(Always), theme: ThemePreference::Auto(ColorSchemePreference::Auto(
DetectColorScheme::Always,
)),
..Default::default() ..Default::default()
}; };
_ = theme_from_detector(options, &detector); _ = theme_from_detector(options, &detector);
@ -280,13 +315,13 @@ mod tests {
for color_scheme in optional(color_schemes()) { for color_scheme in optional(color_schemes()) {
for options in [ for options in [
ThemeOptions { ThemeOptions {
theme: Some(ThemeRequest::Named("Theme".to_string())), theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())),
..Default::default() ..Default::default()
}, },
ThemeOptions { ThemeOptions {
theme: Some(ThemeRequest::Named("Theme".to_string())), theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())),
theme_dark: Some(ThemeRequest::Named("Dark Theme".to_string())), theme_dark: Some(ThemeName::Named("Dark Theme".to_string())),
theme_light: Some(ThemeRequest::Named("Light Theme".to_string())), theme_light: Some(ThemeName::Named("Light Theme".to_string())),
..Default::default() ..Default::default()
}, },
] { ] {
@ -299,7 +334,7 @@ mod tests {
#[test] #[test]
fn detector_is_not_called_if_theme_is_present() { fn detector_is_not_called_if_theme_is_present() {
let options = ThemeOptions { let options = ThemeOptions {
theme: Some(ThemeRequest::Named("Theme".to_string())), theme: ThemePreference::Fixed(ThemeName::Named("Theme".to_string())),
..Default::default() ..Default::default()
}; };
let detector = DetectorStub::should_detect(Some(Dark)); let detector = DetectorStub::should_detect(Some(Dark));
@ -326,7 +361,7 @@ mod tests {
fn dark_if_requested_explicitly_through_theme() { fn dark_if_requested_explicitly_through_theme() {
for color_scheme in optional(color_schemes()) { for color_scheme in optional(color_schemes()) {
let options = ThemeOptions { let options = ThemeOptions {
theme: Some(ThemeRequest::Default), theme: ThemePreference::Fixed(ThemeName::Default),
..Default::default() ..Default::default()
}; };
let detector = ConstantDetector(color_scheme); let detector = ConstantDetector(color_scheme);
@ -343,8 +378,8 @@ mod tests {
for options in [ for options in [
ThemeOptions::default(), ThemeOptions::default(),
ThemeOptions { ThemeOptions {
theme_dark: Some(ThemeRequest::Default), theme_dark: Some(ThemeName::Default),
theme_light: Some(ThemeRequest::Default), theme_light: Some(ThemeName::Default),
..Default::default() ..Default::default()
}, },
] { ] {
@ -365,8 +400,8 @@ mod tests {
fn chooses_dark_theme_if_dark_or_unknown() { fn chooses_dark_theme_if_dark_or_unknown() {
for color_scheme in [Some(Dark), None] { for color_scheme in [Some(Dark), None] {
let options = ThemeOptions { let options = ThemeOptions {
theme_dark: Some(ThemeRequest::Named("Dark".to_string())), theme_dark: Some(ThemeName::Named("Dark".to_string())),
theme_light: Some(ThemeRequest::Named("Light".to_string())), theme_light: Some(ThemeName::Named("Light".to_string())),
..Default::default() ..Default::default()
}; };
let detector = ConstantDetector(color_scheme); let detector = ConstantDetector(color_scheme);
@ -377,8 +412,8 @@ mod tests {
#[test] #[test]
fn chooses_light_theme_if_light() { fn chooses_light_theme_if_light() {
let options = ThemeOptions { let options = ThemeOptions {
theme_dark: Some(ThemeRequest::Named("Dark".to_string())), theme_dark: Some(ThemeName::Named("Dark".to_string())),
theme_light: Some(ThemeRequest::Named("Light".to_string())), theme_light: Some(ThemeName::Named("Light".to_string())),
..Default::default() ..Default::default()
}; };
let detector = ConstantDetector(Some(ColorScheme::Light)); let detector = ConstantDetector(Some(ColorScheme::Light));

View File

@ -279,7 +279,6 @@ fn list_themes_with_colors() {
bat() bat()
.arg("--color=always") .arg("--color=always")
.arg("--color-scheme=dark")
.arg("--list-themes") .arg("--list-themes")
.assert() .assert()
.success() .success()
@ -296,7 +295,6 @@ fn list_themes_without_colors() {
bat() bat()
.arg("--color=never") .arg("--color=never")
.arg("--color-scheme=dark")
.arg("--list-themes") .arg("--list-themes")
.assert() .assert()
.success() .success()