fix: Improve color env variable support

- `CLICOLOR=1` now works correctly
- `NO_COLOR=` now works correctly
- Auto-enable colors in CI


For running `typos` on the Linux kernel (176,210 typos to be printed), we went from 20.082s to
<20.450s.  Where in that range is unclear due to jitter in my system.
```console
$ hyperfine -L typos ./typos-main,./typos-anstream "{typos} ../../../linux" -i
Benchmark 1: ./typos-main ../../../linux
  Time (mean ± σ):     20.082 s ±  0.111 s    [User: 39.668 s, System: 0.474 s]
  Range (min … max):   19.961 s … 20.331 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 2: ./typos-anstream ../../../linux
  Time (mean ± σ):     20.426 s ±  0.104 s    [User: 40.301 s, System: 0.523 s]
  Range (min … max):   20.316 s … 20.661 s    10 runs

  Warning: Ignoring non-zero exit code.

Summary
  './typos-main ../../../linux' ran
    1.02 ± 0.01 times faster than './typos-anstream ../../../linux'

$ CLICOLOR_FORCE=1 hyperfine -L typos ./typos-anstream "{typos} ../../../linux" -i
Benchmark 1: ./typos-anstream ../../../linux
  Time (mean ± σ):     20.262 s ±  0.075 s    [User: 39.961 s, System: 0.542 s]
  Range (min … max):   20.154 s … 20.420 s    10 runs

  Warning: Ignoring non-zero exit code.

$ CLICOLOR=0 hyperfine -L typos ./typos-anstream "{typos} ../../../linux" -i
Benchmark 1: ./typos-anstream ../../../linux
  Time (mean ± σ):     20.296 s ±  0.065 s    [User: 40.003 s, System: 0.565 s]
  Range (min … max):   20.169 s … 20.383 s    10 runs

  Warning: Ignoring non-zero exit code.
```
This commit is contained in:
Ed Page 2023-03-13 23:01:45 -05:00
parent 28e7f17a65
commit 3a29410c1b
4 changed files with 170 additions and 83 deletions

90
Cargo.lock generated
View file

@ -44,6 +44,46 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80c697cc33851b02ab0c26b2e8a211684fbe627ff1cc506131f35026dd7686dd"
[[package]]
name = "anstyle-parse"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb854bd007661547402bb1aeee4a00eda2cc9775afb43c9a5158f8706f448a2a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-wincon",
"concolor-override",
"concolor-query 0.3.3",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle-wincon"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
dependencies = [
"anstyle",
"windows-sys 0.45.0",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.66" version = "1.0.66"
@ -281,26 +321,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b3e3c41e9488eeda196b6806dbf487742107d61b2e16485bcca6c25ed5755b" checksum = "f7b3e3c41e9488eeda196b6806dbf487742107d61b2e16485bcca6c25ed5755b"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"concolor-query", "concolor-query 0.1.0",
"is-terminal",
]
[[package]]
name = "concolor"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b946244a988c390a94667ae0e3958411fa40cc46ea496a929b263d883f5f9c3"
dependencies = [
"bitflags",
"concolor-override",
"concolor-query 0.3.3",
"is-terminal", "is-terminal",
] ]
[[package]] [[package]]
name = "concolor-clap" name = "concolor-clap"
version = "0.0.14" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf2a9ab06b97f9c7a0413cffaa2573c67497b41f59b6c4188b5464e0078a2cb" checksum = "435ff0007a3bb04099fe1beedc6b76e7dd5340c90b168008ac0d7e87441de1bf"
dependencies = [ dependencies = [
"clap 4.1.8", "clap 4.1.8",
"concolor", "concolor 0.1.1",
] ]
[[package]]
name = "concolor-override"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
[[package]] [[package]]
name = "concolor-query" name = "concolor-query"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a90734b3d5dcf656e7624cca6bce9c3a90ee11f900e80141a7427ccfb3d317" checksum = "82a90734b3d5dcf656e7624cca6bce9c3a90ee11f900e80141a7427ccfb3d317"
[[package]]
name = "concolor-query"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
dependencies = [
"windows-sys 0.45.0",
]
[[package]] [[package]]
name = "content_inspector" name = "content_inspector"
version = "0.2.4" version = "0.2.4"
@ -761,7 +828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86d13dc3bae03e53a5e81a3944773631df2c5a33c060e195c1f7bf3fd0d2a696" checksum = "86d13dc3bae03e53a5e81a3944773631df2c5a33c060e195c1f7bf3fd0d2a696"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"concolor", "concolor 0.0.12",
"os_info", "os_info",
"serde", "serde",
"serde_derive", "serde_derive",
@ -1379,7 +1446,7 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4389a6395e9925166f19d67b64874e526ec28a4b8455f3321b686c912299c3ea" checksum = "4389a6395e9925166f19d67b64874e526ec28a4b8455f3321b686c912299c3ea"
dependencies = [ dependencies = [
"concolor", "concolor 0.0.12",
"content_inspector", "content_inspector",
"dunce", "dunce",
"filetime", "filetime",
@ -1581,13 +1648,15 @@ name = "typos-cli"
version = "1.13.23" version = "1.13.23"
dependencies = [ dependencies = [
"ahash", "ahash",
"anstyle",
"anstyle-stream",
"anyhow", "anyhow",
"assert_fs", "assert_fs",
"atty", "atty",
"bstr", "bstr",
"clap 4.1.8", "clap 4.1.8",
"clap-verbosity-flag", "clap-verbosity-flag",
"concolor", "concolor 0.1.1",
"concolor-clap", "concolor-clap",
"content_inspector", "content_inspector",
"criterion", "criterion",
@ -1619,7 +1688,6 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
"varcon-core", "varcon-core",
"yansi",
] ]
[[package]] [[package]]
@ -1724,6 +1792,12 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.3.0" version = "1.3.0"

View file

@ -56,9 +56,8 @@ toml = "0.7.2"
log = "0.4" log = "0.4"
env_logger = { version = "0.10", default-features = false, features = ["auto-color"] } env_logger = { version = "0.10", default-features = false, features = ["auto-color"] }
atty = "0.2.14" atty = "0.2.14"
yansi = "0.5.1" concolor = { version = "0.1.1" }
concolor = { version = "0.0.12" } concolor-clap = { version = "0.1.0", features = ["api"] }
concolor-clap = { version = "0.0.14", features = ["api_unstable"] }
bstr = "1.3" bstr = "1.3"
once_cell = "1.17.1" once_cell = "1.17.1"
ahash = "0.8" ahash = "0.8"
@ -79,6 +78,8 @@ unicode-width = "0.1.10"
unic-emoji-char = "0.9.0" unic-emoji-char = "0.9.0"
thread_local = "1.1.7" thread_local = "1.1.7"
globset = "0.4.10" globset = "0.4.10"
anstyle = "0.3.1"
anstyle-stream = "0.2.0"
[dev-dependencies] [dev-dependencies]
assert_fs = "1.0" assert_fs = "1.0"

View file

@ -31,23 +31,12 @@ fn run() -> proc_exit::ExitResult {
init_logging(args.verbose.log_level()); init_logging(args.verbose.log_level());
let stdout_palette = if concolor::get(concolor::Stream::Stdout).ansi_color() {
report::Palette::colored()
} else {
report::Palette::plain()
};
let stderr_palette = if concolor::get(concolor::Stream::Stderr).ansi_color() {
report::Palette::colored()
} else {
report::Palette::plain()
};
if let Some(output_path) = args.dump_config.as_ref() { if let Some(output_path) = args.dump_config.as_ref() {
run_dump_config(&args, output_path) run_dump_config(&args, output_path)
} else if args.type_list { } else if args.type_list {
run_type_list(&args) run_type_list(&args)
} else { } else {
run_checks(&args, stdout_palette, stderr_palette) run_checks(&args)
} }
} }
@ -149,11 +138,7 @@ fn run_type_list(args: &args::Args) -> proc_exit::ExitResult {
Ok(()) Ok(())
} }
fn run_checks( fn run_checks(args: &args::Args) -> proc_exit::ExitResult {
args: &args::Args,
stdout_palette: report::Palette,
stderr_palette: report::Palette,
) -> proc_exit::ExitResult {
let global_cwd = std::env::current_dir().to_sysexits()?; let global_cwd = std::env::current_dir().to_sysexits()?;
let storage = typos_cli::policy::ConfigStorage::new(); let storage = typos_cli::policy::ConfigStorage::new();
@ -219,6 +204,8 @@ fn run_checks(
let output_reporter = if args.diff { let output_reporter = if args.diff {
Box::new(crate::report::PrintSilent) Box::new(crate::report::PrintSilent)
} else { } else {
let stdout_palette = report::Palette::colored();
let stderr_palette = report::Palette::colored();
args.format.reporter(stdout_palette, stderr_palette) args.format.reporter(stdout_palette, stderr_palette)
}; };
let status_reporter = report::MessageStatus::new(output_reporter.as_ref()); let status_reporter = report::MessageStatus::new(output_reporter.as_ref());

View file

@ -1,33 +1,67 @@
#![allow(clippy::needless_update)] #![allow(clippy::needless_update)]
use std::io::{self, Write}; use std::io::Write as _;
use std::sync::atomic; use std::sync::atomic;
use anstyle_stream::stdout;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use typos_cli::report::{Context, Message, Report, Typo}; use typos_cli::report::{Context, Message, Report, Typo};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, Default)]
pub struct Palette { pub struct Palette {
error: yansi::Style, error: anstyle::Style,
info: yansi::Style, info: anstyle::Style,
strong: yansi::Style, strong: anstyle::Style,
} }
impl Palette { impl Palette {
pub fn colored() -> Self { pub fn colored() -> Self {
Self { Self {
error: yansi::Style::new(yansi::Color::Red), error: anstyle::AnsiColor::Red.into(),
info: yansi::Style::new(yansi::Color::Blue), info: anstyle::AnsiColor::Blue.into(),
strong: yansi::Style::default().bold(), strong: anstyle::Effects::BOLD.into(),
} }
} }
pub fn plain() -> Self { pub(crate) fn error<D: std::fmt::Display>(self, display: D) -> Styled<D> {
Self { Styled::new(display, self.error)
error: yansi::Style::default(), }
info: yansi::Style::default(),
strong: yansi::Style::default(), pub(crate) fn info<D: std::fmt::Display>(self, display: D) -> Styled<D> {
Styled::new(display, self.info)
}
pub(crate) fn strong<D: std::fmt::Display>(self, display: D) -> Styled<D> {
Styled::new(display, self.strong)
}
}
#[derive(Debug)]
pub(crate) struct Styled<D> {
display: D,
style: anstyle::Style,
}
impl<D: std::fmt::Display> Styled<D> {
pub(crate) fn new(display: D, style: anstyle::Style) -> Self {
Self { display, style }
}
}
impl<D: std::fmt::Display> std::fmt::Display for Styled<D> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(
f,
"{}{}{}",
self.style.render(),
self.display,
self.style.render_reset()
)
} else {
self.display.fmt(f)
} }
} }
} }
@ -91,17 +125,17 @@ impl Report for PrintBrief {
Message::Typo(msg) => print_brief_correction(msg, self.stdout_palette)?, Message::Typo(msg) => print_brief_correction(msg, self.stdout_palette)?,
Message::FileType(msg) => { Message::FileType(msg) => {
writeln!( writeln!(
io::stdout(), stdout(),
"{}:{}", "{}:{}",
msg.path.display(), msg.path.display(),
msg.file_type.unwrap_or("-") msg.file_type.unwrap_or("-")
)?; )?;
} }
Message::File(msg) => { Message::File(msg) => {
writeln!(io::stdout(), "{}", msg.path.display())?; writeln!(stdout(), "{}", msg.path.display())?;
} }
Message::Parse(msg) => { Message::Parse(msg) => {
writeln!(io::stdout(), "{}", msg.data)?; writeln!(stdout(), "{}", msg.data)?;
} }
Message::Error(msg) => { Message::Error(msg) => {
log::error!("{}: {}", context_display(&msg.context), msg.msg); log::error!("{}: {}", context_display(&msg.context), msg.msg);
@ -126,17 +160,17 @@ impl Report for PrintLong {
Message::Typo(msg) => print_long_correction(msg, self.stdout_palette)?, Message::Typo(msg) => print_long_correction(msg, self.stdout_palette)?,
Message::FileType(msg) => { Message::FileType(msg) => {
writeln!( writeln!(
io::stdout(), stdout(),
"{}:{}", "{}:{}",
msg.path.display(), msg.path.display(),
msg.file_type.unwrap_or("-") msg.file_type.unwrap_or("-")
)?; )?;
} }
Message::File(msg) => { Message::File(msg) => {
writeln!(io::stdout(), "{}", msg.path.display())?; writeln!(stdout(), "{}", msg.path.display())?;
} }
Message::Parse(msg) => { Message::Parse(msg) => {
writeln!(io::stdout(), "{}", msg.data)?; writeln!(stdout(), "{}", msg.data)?;
} }
Message::Error(msg) => { Message::Error(msg) => {
log::error!("{}: {}", context_display(&msg.context), msg.msg); log::error!("{}: {}", context_display(&msg.context), msg.msg);
@ -156,25 +190,23 @@ fn print_brief_correction(msg: &Typo, palette: Palette) -> Result<(), std::io::E
typos::Status::Invalid => { typos::Status::Invalid => {
let divider = ":"; let divider = ":";
writeln!( writeln!(
io::stdout(), stdout(),
"{}{}{}: {}", "{:#}{:#}{:#}: {:#}",
palette.info.paint(context_display(&msg.context)), palette.info(context_display(&msg.context)),
palette.info.paint(divider), palette.info(divider),
palette.info.paint(column_number), palette.info(column_number),
palette palette.strong(format_args!("`{}` is disallowed:", msg.typo)),
.strong
.paint(format_args!("`{}` is disallowed:", msg.typo)),
)?; )?;
} }
typos::Status::Corrections(corrections) => { typos::Status::Corrections(corrections) => {
let divider = ":"; let divider = ":";
writeln!( writeln!(
io::stdout(), stdout(),
"{}{}{}: {}", "{:#}{:#}{:#}: {:#}",
palette.info.paint(context_display(&msg.context)), palette.info(context_display(&msg.context)),
palette.info.paint(divider), palette.info(divider),
palette.info.paint(column_number), palette.info(column_number),
palette.strong.paint(format_args!( palette.strong(format_args!(
"`{}` -> {}", "`{}` -> {}",
msg.typo, msg.typo,
itertools::join(corrections.iter().map(|s| format!("`{}`", s)), ", ") itertools::join(corrections.iter().map(|s| format!("`{}`", s)), ", ")
@ -187,7 +219,7 @@ fn print_brief_correction(msg: &Typo, palette: Palette) -> Result<(), std::io::E
} }
fn print_long_correction(msg: &Typo, palette: Palette) -> Result<(), std::io::Error> { fn print_long_correction(msg: &Typo, palette: Palette) -> Result<(), std::io::Error> {
let stdout = io::stdout(); let stdout = stdout();
let mut handle = stdout.lock(); let mut handle = stdout.lock();
let line = String::from_utf8_lossy(msg.buffer.as_ref()); let line = String::from_utf8_lossy(msg.buffer.as_ref());
@ -200,19 +232,17 @@ fn print_long_correction(msg: &Typo, palette: Palette) -> Result<(), std::io::Er
typos::Status::Invalid => { typos::Status::Invalid => {
writeln!( writeln!(
handle, handle,
"{}: {}", "{:#}: {:#}",
palette.error.paint("error"), palette.error("error"),
palette palette.strong(format_args!("`{}` is disallowed`", msg.typo))
.strong
.paint(format_args!("`{}` is disallowed`", msg.typo))
)?; )?;
} }
typos::Status::Corrections(corrections) => { typos::Status::Corrections(corrections) => {
writeln!( writeln!(
handle, handle,
"{}: {}", "{:#}: {:#}",
palette.error.paint("error"), palette.error("error"),
palette.strong.paint(format_args!( palette.strong(format_args!(
"`{}` should be {}", "`{}` should be {}",
msg.typo, msg.typo,
itertools::join(corrections.iter().map(|s| format!("`{}`", s)), ", ") itertools::join(corrections.iter().map(|s| format!("`{}`", s)), ", ")
@ -223,10 +253,10 @@ fn print_long_correction(msg: &Typo, palette: Palette) -> Result<(), std::io::Er
let divider = ":"; let divider = ":";
writeln!( writeln!(
handle, handle,
" --> {}{}{}", " --> {:#}{:#}{:#}",
palette.info.paint(context_display(&msg.context)), palette.info(context_display(&msg.context)),
palette.info.paint(divider), palette.info(divider),
palette.info.paint(column_number) palette.info(column_number)
)?; )?;
if let Some(Context::File(context)) = &msg.context { if let Some(Context::File(context)) = &msg.context {
@ -240,18 +270,13 @@ fn print_long_correction(msg: &Typo, palette: Palette) -> Result<(), std::io::Er
let hl: String = itertools::repeat_n("^", visible_len).collect(); let hl: String = itertools::repeat_n("^", visible_len).collect();
writeln!(handle, "{} |", line_indent)?; writeln!(handle, "{} |", line_indent)?;
writeln!(handle, "{:#} | {}", palette.info(line_num), line.trim_end())?;
writeln!( writeln!(
handle, handle,
"{} | {}", "{} | {}{:#}",
palette.info.paint(line_num),
line.trim_end()
)?;
writeln!(
handle,
"{} | {}{}",
line_indent, line_indent,
hl_indent, hl_indent,
palette.error.paint(hl) palette.error(hl)
)?; )?;
writeln!(handle, "{} |", line_indent)?; writeln!(handle, "{} |", line_indent)?;
} }
@ -306,7 +331,7 @@ pub struct PrintJson;
impl Report for PrintJson { impl Report for PrintJson {
fn report(&self, msg: Message) -> Result<(), std::io::Error> { fn report(&self, msg: Message) -> Result<(), std::io::Error> {
writeln!(io::stdout(), "{}", serde_json::to_string(&msg).unwrap())?; writeln!(stdout(), "{}", serde_json::to_string(&msg).unwrap())?;
Ok(()) Ok(())
} }
} }