1
0
mirror of https://github.com/sharkdp/bat.git synced 2025-09-02 03:12:25 +01:00

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
12 changed files with 83 additions and 142 deletions

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},
};
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::<String>("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::<String>("theme-dark")
.map(|t| ThemeRequest::from_str(t).unwrap());
.map(|t| ThemeName::from_str(t).unwrap());
let theme_light = self
.matches
.get_one::<String>("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::<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=\"...\").",
),
)
.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")

View File

@@ -384,7 +384,7 @@ fn run() -> Result<bool> {
&config,
config_dir,
cache_dir,
app.color_scheme_preference(),
ColorSchemePreference::default(),
)?;
Ok(true)
} 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)]
pub struct ThemeOptions {
/// 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.
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.
pub theme_light: Option<ThemeRequest>,
/// 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<ThemeName>,
}
/// 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.
///
/// ```
/// # 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<Self, Self::Err> {
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<ThemeRequest> {
fn choose_theme(options: ThemeOptions, color_scheme: ColorScheme) -> Option<ThemeName> {
match color_scheme {
ColorScheme::Dark => options.theme_dark,
ColorScheme::Light => options.theme_light,
@@ -220,7 +254,6 @@ impl ColorSchemeDetector for Option<ColorScheme> {
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));