feat(cli): Smarter color control

This commit is contained in:
Ed Page 2021-10-06 10:08:42 -05:00
parent a5d06c5a65
commit b17b811ec1
5 changed files with 41 additions and 114 deletions

29
Cargo.lock generated
View file

@ -231,6 +231,33 @@ dependencies = [
"unicase",
]
[[package]]
name = "concolor-clap"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb23c99e877c098e60ba3e84410153bfc311ac91b6ac7eabc0da0414ffa12e4"
dependencies = [
"concolor-control",
"structopt",
]
[[package]]
name = "concolor-control"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7104119c2f80d887239879d0c50e033cd40eac9a3f3561e0684ba7d5d654f4da"
dependencies = [
"atty",
"bitflags",
"concolor-query",
]
[[package]]
name = "concolor-query"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad159cc964ac8f9d407cbc0aa44b02436c054b541f2b4b5f06972e1efdc54bc7"
[[package]]
name = "content_inspector"
version = "0.2.4"
@ -1454,6 +1481,8 @@ dependencies = [
"bstr",
"clap",
"clap-verbosity-flag",
"concolor-clap",
"concolor-control",
"content_inspector",
"criterion",
"derive_more",

View file

@ -70,6 +70,8 @@ log = "0.4"
env_logger = { version = "0.9", default-features = false, features = ["termcolor"] }
atty = "0.2.14"
yansi = "0.5.0"
concolor-control = { version = "0.0.7" }
concolor-clap = { version = "0.0.6", features = ["api_unstable"] }
bstr = "0.2"
once_cell = "1.2.0"
ahash = "0.7"

View file

@ -44,7 +44,8 @@ impl Default for Format {
#[structopt(
setting = structopt::clap::AppSettings::UnifiedHelpMessage,
setting = structopt::clap::AppSettings::DeriveDisplayOrder,
setting = structopt::clap::AppSettings::DontCollapseArgsInUsage
setting = structopt::clap::AppSettings::DontCollapseArgsInUsage,
setting = concolor_clap::color_choice(),
)]
#[structopt(group = structopt::clap::ArgGroup::with_name("mode").multiple(false))]
pub(crate) struct Args {
@ -104,7 +105,7 @@ pub(crate) struct Args {
pub(crate) config: ConfigArgs,
#[structopt(flatten)]
pub(crate) color: crate::color::ColorArgs,
pub(crate) color: concolor_clap::Color,
#[structopt(flatten)]
pub(crate) verbose: clap_verbosity_flag::Verbosity,

View file

@ -1,100 +0,0 @@
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
pub struct ColorArgs {
/// "Specify when to use colored output. The automatic mode
/// only enables colors if an interactive terminal is detected -
/// colors are automatically disabled if the output goes to a pipe.
///
/// Possible values: *auto*, never, always.
#[structopt(
long,
value_name="when",
possible_values(&ColorValue::variants()),
case_insensitive(true),
default_value("auto"),
hide_possible_values(true),
hide_default_value(true),
help="When to use colors (*auto*, never, always).")]
color: ColorValue,
}
impl ColorArgs {
pub fn colored(&self) -> Option<bool> {
self.color.colored()
}
}
arg_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ColorValue {
Always,
Never,
Auto,
}
}
impl ColorValue {
fn colored(self) -> Option<bool> {
match self {
ColorValue::Always => Some(true),
ColorValue::Never => Some(false),
ColorValue::Auto => None,
}
}
}
impl Default for ColorValue {
fn default() -> Self {
ColorValue::Auto
}
}
pub fn colored_stdout() -> Option<bool> {
if atty::is(atty::Stream::Stdout) {
None
} else {
Some(false)
}
}
pub fn colored_stderr() -> Option<bool> {
if atty::is(atty::Stream::Stderr) {
None
} else {
Some(false)
}
}
pub fn colored_env() -> Option<bool> {
match std::env::var_os("TERM") {
None => noterm_colored(),
Some(k) => {
if k == "dumb" {
Some(false)
} else {
None
}
}
}
.or_else(|| {
// See https://no-color.org/
std::env::var_os("NO_COLOR").map(|_| true)
})
}
#[cfg(not(windows))]
fn noterm_colored() -> Option<bool> {
// If TERM isn't set, then we are in a weird environment that
// probably doesn't support colors.
Some(false)
}
#[cfg(windows)]
fn noterm_colored() -> Option<bool> {
// On Windows, if TERM isn't set, then we shouldn't automatically
// assume that colors aren't allowed. This is unlike Unix environments
// where TERM is more rigorously set.
None
}

View file

@ -7,7 +7,6 @@ use std::io::Write;
use structopt::StructOpt;
mod args;
mod color;
mod report;
use proc_exit::WithCodeResultExt;
@ -31,22 +30,16 @@ fn run() -> proc_exit::ExitResult {
}
};
let colored = args.color.colored().or_else(color::colored_env);
let mut colored_stdout = colored.or_else(color::colored_stdout).unwrap_or(true);
let mut colored_stderr = colored.or_else(color::colored_stderr).unwrap_or(true);
if (colored_stdout || colored_stderr) && !yansi::Paint::enable_windows_ascii() {
colored_stdout = false;
colored_stderr = false;
}
args.color.apply();
init_logging(args.verbose.log_level(), colored_stderr);
init_logging(args.verbose.log_level());
let stdout_palette = if colored_stdout {
let stdout_palette = if concolor_control::get(concolor_control::Stream::Stdout).ansi_color() {
report::Palette::colored()
} else {
report::Palette::plain()
};
let stderr_palette = if colored_stderr {
let stderr_palette = if concolor_control::get(concolor_control::Stream::Stderr).ansi_color() {
report::Palette::colored()
} else {
report::Palette::plain()
@ -272,9 +265,11 @@ fn run_checks(
}
}
fn init_logging(level: Option<log::Level>, colored: bool) {
fn init_logging(level: Option<log::Level>) {
if let Some(level) = level {
let mut builder = env_logger::Builder::new();
let colored = concolor_control::get(concolor_control::Stream::Stderr).ansi_color();
builder.write_style(if colored {
env_logger::WriteStyle::Always
} else {