Merge pull request #982 from paper-lark/no-color

NO_COLOR environment variable support
This commit is contained in:
Mélanie Chauvel 2021-12-06 21:43:55 +01:00 committed by GitHub
commit 7c957f95b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 20 deletions

View File

@ -224,6 +224,12 @@ Specifies the number of spaces to print between an icon (see the `--icons`
Different terminals display icons differently, as they usually take up more than one character width on screen, so theres no “standard” number of spaces that exa can use to separate an icon from text. One space may place the icon too close to the text, and two spaces may place it too far away. So the choice is left up to the user to configure depending on their terminal emulator. Different terminals display icons differently, as they usually take up more than one character width on screen, so theres no “standard” number of spaces that exa can use to separate an icon from text. One space may place the icon too close to the text, and two spaces may place it too far away. So the choice is left up to the user to configure depending on their terminal emulator.
## `NO_COLOR`
Disables colours in the output (regardless of its value). Can be overridden by `--color` option.
See `https://no-color.org/` for details.
## `LS_COLORS`, `EXA_COLORS` ## `LS_COLORS`, `EXA_COLORS`
Specifies the colour scheme used to highlight files based on their name and kind, as well as highlighting metadata and parts of the UI. Specifies the colour scheme used to highlight files based on their name and kind, as well as highlighting metadata and parts of the UI.

View File

@ -5,7 +5,7 @@ use crate::theme::{Options, UseColours, ColourScale, Definitions};
impl Options { impl Options {
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> { pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
let use_colours = UseColours::deduce(matches)?; let use_colours = UseColours::deduce(matches, vars)?;
let colour_scale = ColourScale::deduce(matches)?; let colour_scale = ColourScale::deduce(matches)?;
let definitions = if use_colours == UseColours::Never { let definitions = if use_colours == UseColours::Never {
@ -21,10 +21,15 @@ impl Options {
impl UseColours { impl UseColours {
fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> { fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
let default_value = match vars.get(vars::NO_COLOR) {
Some(_) => Self::Never,
None => Self::Automatic,
};
let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? { let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? {
Some(w) => w, Some(w) => w,
None => return Ok(Self::Automatic), None => return Ok(default_value),
}; };
if word == "always" { if word == "always" {
@ -87,6 +92,16 @@ mod terminal_test {
} }
}; };
($name:ident: $type:ident <- $inputs:expr, $env:expr; $stricts:expr => $result:expr) => {
#[test]
fn $name() {
let env = $env;
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf, &env)) {
assert_eq!(result, $result);
}
}
};
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => { ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
#[test] #[test]
fn $name() { fn $name() {
@ -95,11 +110,39 @@ mod terminal_test {
} }
} }
}; };
($name:ident: $type:ident <- $inputs:expr, $env:expr; $stricts:expr => err $result:expr) => {
#[test]
fn $name() {
let env = $env;
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf, &env)) {
assert_eq!(result.unwrap_err(), $result);
}
}
};
} }
struct MockVars { struct MockVars {
ls: &'static str, ls: &'static str,
exa: &'static str, exa: &'static str,
no_color: &'static str,
}
impl MockVars {
fn empty() -> MockVars {
return MockVars {
ls: "",
exa: "",
no_color: "",
};
}
fn with_no_color() -> MockVars {
return MockVars {
ls: "",
exa: "",
no_color: "true",
};
}
} }
// Test impl that just returns the value it has. // Test impl that just returns the value it has.
@ -111,6 +154,9 @@ mod terminal_test {
else if name == vars::EXA_COLORS && ! self.exa.is_empty() { else if name == vars::EXA_COLORS && ! self.exa.is_empty() {
Some(OsString::from(self.exa.clone())) Some(OsString::from(self.exa.clone()))
} }
else if name == vars::NO_COLOR && ! self.no_color.is_empty() {
Some(OsString::from(self.no_color.clone()))
}
else { else {
None None
} }
@ -120,32 +166,33 @@ mod terminal_test {
// Default // Default
test!(empty: UseColours <- []; Both => Ok(UseColours::Automatic)); test!(empty: UseColours <- [], MockVars::empty(); Both => Ok(UseColours::Automatic));
test!(empty_with_no_color: UseColours <- [], MockVars::with_no_color(); Both => Ok(UseColours::Never));
// --colour // --colour
test!(u_always: UseColours <- ["--colour=always"]; Both => Ok(UseColours::Always)); test!(u_always: UseColours <- ["--colour=always"], MockVars::empty(); Both => Ok(UseColours::Always));
test!(u_auto: UseColours <- ["--colour", "auto"]; Both => Ok(UseColours::Automatic)); test!(u_auto: UseColours <- ["--colour", "auto"], MockVars::empty(); Both => Ok(UseColours::Automatic));
test!(u_never: UseColours <- ["--colour=never"]; Both => Ok(UseColours::Never)); test!(u_never: UseColours <- ["--colour=never"], MockVars::empty(); Both => Ok(UseColours::Never));
// --color // --color
test!(no_u_always: UseColours <- ["--color", "always"]; Both => Ok(UseColours::Always)); test!(no_u_always: UseColours <- ["--color", "always"], MockVars::empty(); Both => Ok(UseColours::Always));
test!(no_u_auto: UseColours <- ["--color=auto"]; Both => Ok(UseColours::Automatic)); test!(no_u_auto: UseColours <- ["--color=auto"], MockVars::empty(); Both => Ok(UseColours::Automatic));
test!(no_u_never: UseColours <- ["--color", "never"]; Both => Ok(UseColours::Never)); test!(no_u_never: UseColours <- ["--color", "never"], MockVars::empty(); Both => Ok(UseColours::Never));
// Errors // Errors
test!(no_u_error: UseColours <- ["--color=upstream"]; Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("upstream"))); // the error is for --color test!(no_u_error: UseColours <- ["--color=upstream"], MockVars::empty(); Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("upstream"))); // the error is for --color
test!(u_error: UseColours <- ["--colour=lovers"]; Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("lovers"))); // and so is this one! test!(u_error: UseColours <- ["--colour=lovers"], MockVars::empty(); Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("lovers"))); // and so is this one!
// Overriding // Overriding
test!(overridden_1: UseColours <- ["--colour=auto", "--colour=never"]; Last => Ok(UseColours::Never)); test!(overridden_1: UseColours <- ["--colour=auto", "--colour=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_2: UseColours <- ["--color=auto", "--colour=never"]; Last => Ok(UseColours::Never)); test!(overridden_2: UseColours <- ["--color=auto", "--colour=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_3: UseColours <- ["--colour=auto", "--color=never"]; Last => Ok(UseColours::Never)); test!(overridden_3: UseColours <- ["--colour=auto", "--color=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_4: UseColours <- ["--color=auto", "--color=never"]; Last => Ok(UseColours::Never)); test!(overridden_4: UseColours <- ["--color=auto", "--color=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_5: UseColours <- ["--colour=auto", "--colour=never"]; Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("colour"))); test!(overridden_5: UseColours <- ["--colour=auto", "--colour=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("colour")));
test!(overridden_6: UseColours <- ["--color=auto", "--colour=never"]; Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("colour"))); test!(overridden_6: UseColours <- ["--color=auto", "--colour=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("colour")));
test!(overridden_7: UseColours <- ["--colour=auto", "--color=never"]; Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("color"))); test!(overridden_7: UseColours <- ["--colour=auto", "--color=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("color")));
test!(overridden_8: UseColours <- ["--color=auto", "--color=never"]; Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("color"))); test!(overridden_8: UseColours <- ["--color=auto", "--color=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("color")));
test!(scale_1: ColourScale <- ["--color-scale", "--colour-scale"]; Last => Ok(ColourScale::Gradient)); test!(scale_1: ColourScale <- ["--color-scale", "--colour-scale"]; Last => Ok(ColourScale::Gradient));
test!(scale_2: ColourScale <- ["--color-scale", ]; Last => Ok(ColourScale::Gradient)); test!(scale_2: ColourScale <- ["--color-scale", ]; Last => Ok(ColourScale::Gradient));

View File

@ -15,6 +15,9 @@ pub static COLUMNS: &str = "COLUMNS";
/// Environment variable used to datetime format. /// Environment variable used to datetime format.
pub static TIME_STYLE: &str = "TIME_STYLE"; pub static TIME_STYLE: &str = "TIME_STYLE";
/// Environment variable used to disable colors.
/// See: https://no-color.org/
pub static NO_COLOR: &str = "NO_COLOR";
// exa-specific variables // exa-specific variables