From 3a29410c1b31186692cf1079174499ddaa26bd52 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 13 Mar 2023 23:01:45 -0500 Subject: [PATCH] fix: Improve color env variable support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `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. ``` --- Cargo.lock | 90 +++++++++++-- crates/typos-cli/Cargo.toml | 7 +- crates/typos-cli/src/bin/typos-cli/main.rs | 21 +-- crates/typos-cli/src/bin/typos-cli/report.rs | 135 +++++++++++-------- 4 files changed, 170 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c6e5fd..751baa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,46 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "anyhow" version = "1.0.66" @@ -281,26 +321,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7b3e3c41e9488eeda196b6806dbf487742107d61b2e16485bcca6c25ed5755b" dependencies = [ "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", ] [[package]] name = "concolor-clap" -version = "0.0.14" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf2a9ab06b97f9c7a0413cffaa2573c67497b41f59b6c4188b5464e0078a2cb" +checksum = "435ff0007a3bb04099fe1beedc6b76e7dd5340c90b168008ac0d7e87441de1bf" dependencies = [ "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]] name = "concolor-query" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "content_inspector" version = "0.2.4" @@ -761,7 +828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86d13dc3bae03e53a5e81a3944773631df2c5a33c060e195c1f7bf3fd0d2a696" dependencies = [ "backtrace", - "concolor", + "concolor 0.0.12", "os_info", "serde", "serde_derive", @@ -1379,7 +1446,7 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4389a6395e9925166f19d67b64874e526ec28a4b8455f3321b686c912299c3ea" dependencies = [ - "concolor", + "concolor 0.0.12", "content_inspector", "dunce", "filetime", @@ -1581,13 +1648,15 @@ name = "typos-cli" version = "1.13.23" dependencies = [ "ahash", + "anstyle", + "anstyle-stream", "anyhow", "assert_fs", "atty", "bstr", "clap 4.1.8", "clap-verbosity-flag", - "concolor", + "concolor 0.1.1", "concolor-clap", "content_inspector", "criterion", @@ -1619,7 +1688,6 @@ dependencies = [ "unicode-segmentation", "unicode-width", "varcon-core", - "yansi", ] [[package]] @@ -1724,6 +1792,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.0" diff --git a/crates/typos-cli/Cargo.toml b/crates/typos-cli/Cargo.toml index 99e02cb..bd6f0ac 100644 --- a/crates/typos-cli/Cargo.toml +++ b/crates/typos-cli/Cargo.toml @@ -56,9 +56,8 @@ toml = "0.7.2" log = "0.4" env_logger = { version = "0.10", default-features = false, features = ["auto-color"] } atty = "0.2.14" -yansi = "0.5.1" -concolor = { version = "0.0.12" } -concolor-clap = { version = "0.0.14", features = ["api_unstable"] } +concolor = { version = "0.1.1" } +concolor-clap = { version = "0.1.0", features = ["api"] } bstr = "1.3" once_cell = "1.17.1" ahash = "0.8" @@ -79,6 +78,8 @@ unicode-width = "0.1.10" unic-emoji-char = "0.9.0" thread_local = "1.1.7" globset = "0.4.10" +anstyle = "0.3.1" +anstyle-stream = "0.2.0" [dev-dependencies] assert_fs = "1.0" diff --git a/crates/typos-cli/src/bin/typos-cli/main.rs b/crates/typos-cli/src/bin/typos-cli/main.rs index f0a2215..342073c 100644 --- a/crates/typos-cli/src/bin/typos-cli/main.rs +++ b/crates/typos-cli/src/bin/typos-cli/main.rs @@ -31,23 +31,12 @@ fn run() -> proc_exit::ExitResult { 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() { run_dump_config(&args, output_path) } else if args.type_list { run_type_list(&args) } 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(()) } -fn run_checks( - args: &args::Args, - stdout_palette: report::Palette, - stderr_palette: report::Palette, -) -> proc_exit::ExitResult { +fn run_checks(args: &args::Args) -> proc_exit::ExitResult { let global_cwd = std::env::current_dir().to_sysexits()?; let storage = typos_cli::policy::ConfigStorage::new(); @@ -219,6 +204,8 @@ fn run_checks( let output_reporter = if args.diff { Box::new(crate::report::PrintSilent) } else { + let stdout_palette = report::Palette::colored(); + let stderr_palette = report::Palette::colored(); args.format.reporter(stdout_palette, stderr_palette) }; let status_reporter = report::MessageStatus::new(output_reporter.as_ref()); diff --git a/crates/typos-cli/src/bin/typos-cli/report.rs b/crates/typos-cli/src/bin/typos-cli/report.rs index 9e7517f..7e5e0fc 100644 --- a/crates/typos-cli/src/bin/typos-cli/report.rs +++ b/crates/typos-cli/src/bin/typos-cli/report.rs @@ -1,33 +1,67 @@ #![allow(clippy::needless_update)] -use std::io::{self, Write}; +use std::io::Write as _; use std::sync::atomic; +use anstyle_stream::stdout; use unicode_width::UnicodeWidthStr; use typos_cli::report::{Context, Message, Report, Typo}; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default)] pub struct Palette { - error: yansi::Style, - info: yansi::Style, - strong: yansi::Style, + error: anstyle::Style, + info: anstyle::Style, + strong: anstyle::Style, } impl Palette { pub fn colored() -> Self { Self { - error: yansi::Style::new(yansi::Color::Red), - info: yansi::Style::new(yansi::Color::Blue), - strong: yansi::Style::default().bold(), + error: anstyle::AnsiColor::Red.into(), + info: anstyle::AnsiColor::Blue.into(), + strong: anstyle::Effects::BOLD.into(), } } - pub fn plain() -> Self { - Self { - error: yansi::Style::default(), - info: yansi::Style::default(), - strong: yansi::Style::default(), + pub(crate) fn error(self, display: D) -> Styled { + Styled::new(display, self.error) + } + + pub(crate) fn info(self, display: D) -> Styled { + Styled::new(display, self.info) + } + + pub(crate) fn strong(self, display: D) -> Styled { + Styled::new(display, self.strong) + } +} + +#[derive(Debug)] +pub(crate) struct Styled { + display: D, + style: anstyle::Style, +} + +impl Styled { + pub(crate) fn new(display: D, style: anstyle::Style) -> Self { + Self { display, style } + } +} + +impl std::fmt::Display for Styled { + #[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::FileType(msg) => { writeln!( - io::stdout(), + stdout(), "{}:{}", msg.path.display(), msg.file_type.unwrap_or("-") )?; } Message::File(msg) => { - writeln!(io::stdout(), "{}", msg.path.display())?; + writeln!(stdout(), "{}", msg.path.display())?; } Message::Parse(msg) => { - writeln!(io::stdout(), "{}", msg.data)?; + writeln!(stdout(), "{}", msg.data)?; } Message::Error(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::FileType(msg) => { writeln!( - io::stdout(), + stdout(), "{}:{}", msg.path.display(), msg.file_type.unwrap_or("-") )?; } Message::File(msg) => { - writeln!(io::stdout(), "{}", msg.path.display())?; + writeln!(stdout(), "{}", msg.path.display())?; } Message::Parse(msg) => { - writeln!(io::stdout(), "{}", msg.data)?; + writeln!(stdout(), "{}", msg.data)?; } Message::Error(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 => { let divider = ":"; writeln!( - io::stdout(), - "{}{}{}: {}", - palette.info.paint(context_display(&msg.context)), - palette.info.paint(divider), - palette.info.paint(column_number), - palette - .strong - .paint(format_args!("`{}` is disallowed:", msg.typo)), + stdout(), + "{:#}{:#}{:#}: {:#}", + palette.info(context_display(&msg.context)), + palette.info(divider), + palette.info(column_number), + palette.strong(format_args!("`{}` is disallowed:", msg.typo)), )?; } typos::Status::Corrections(corrections) => { let divider = ":"; writeln!( - io::stdout(), - "{}{}{}: {}", - palette.info.paint(context_display(&msg.context)), - palette.info.paint(divider), - palette.info.paint(column_number), - palette.strong.paint(format_args!( + stdout(), + "{:#}{:#}{:#}: {:#}", + palette.info(context_display(&msg.context)), + palette.info(divider), + palette.info(column_number), + palette.strong(format_args!( "`{}` -> {}", msg.typo, 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> { - let stdout = io::stdout(); + let stdout = stdout(); let mut handle = stdout.lock(); 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 => { writeln!( handle, - "{}: {}", - palette.error.paint("error"), - palette - .strong - .paint(format_args!("`{}` is disallowed`", msg.typo)) + "{:#}: {:#}", + palette.error("error"), + palette.strong(format_args!("`{}` is disallowed`", msg.typo)) )?; } typos::Status::Corrections(corrections) => { writeln!( handle, - "{}: {}", - palette.error.paint("error"), - palette.strong.paint(format_args!( + "{:#}: {:#}", + palette.error("error"), + palette.strong(format_args!( "`{}` should be {}", msg.typo, 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 = ":"; writeln!( handle, - " --> {}{}{}", - palette.info.paint(context_display(&msg.context)), - palette.info.paint(divider), - palette.info.paint(column_number) + " --> {:#}{:#}{:#}", + palette.info(context_display(&msg.context)), + palette.info(divider), + palette.info(column_number) )?; 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(); writeln!(handle, "{} |", line_indent)?; + writeln!(handle, "{:#} | {}", palette.info(line_num), line.trim_end())?; writeln!( handle, - "{} | {}", - palette.info.paint(line_num), - line.trim_end() - )?; - writeln!( - handle, - "{} | {}{}", + "{} | {}{:#}", line_indent, hl_indent, - palette.error.paint(hl) + palette.error(hl) )?; writeln!(handle, "{} |", line_indent)?; } @@ -306,7 +331,7 @@ pub struct PrintJson; impl Report for PrintJson { 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(()) } }