diff --git a/Cargo.lock b/Cargo.lock index bcb7dd1b25bcb..2c290b392d591 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-lossy" @@ -3757,6 +3757,8 @@ name = "rustc_errors" version = "0.0.0" dependencies = [ "annotate-snippets 0.11.5", + "anstream", + "anstyle", "derive_setters", "rustc_abi", "rustc_ast", @@ -3773,7 +3775,6 @@ dependencies = [ "rustc_span", "serde", "serde_json", - "termcolor", "termize", "tracing", "windows 0.61.3", @@ -4327,7 +4328,6 @@ dependencies = [ "rustc_macros", "rustc_session", "rustc_span", - "termcolor", "thin-vec", "tracing", "unicode-normalization", diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8dab520cf36b9..2234318b67824 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -521,11 +521,11 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { }; // Try to prettify the raw markdown text. The result can be used by the pager or on stdout. - let pretty_data = { + let mut pretty_data = { let mdstream = markdown::MdStream::parse_str(content); let bufwtr = markdown::create_stdout_bufwtr(); - let mut mdbuf = bufwtr.buffer(); - if mdstream.write_termcolor_buf(&mut mdbuf).is_ok() { Some((bufwtr, mdbuf)) } else { None } + let mut mdbuf = Vec::new(); + if mdstream.write_anstream_buf(&mut mdbuf).is_ok() { Some((bufwtr, mdbuf)) } else { None } }; // Try to print via the pager, pretty output if possible. @@ -546,8 +546,8 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { } // The pager failed. Try to print pretty output to stdout. - if let Some((bufwtr, mdbuf)) = &pretty_data - && bufwtr.print(mdbuf).is_ok() + if let Some((bufwtr, mdbuf)) = &mut pretty_data + && bufwtr.write_all(&mdbuf).is_ok() { return; } diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 7912b8e6098b3..b7b5cbd35741f 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -6,6 +6,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start annotate-snippets = "0.11" +anstream = "0.6.20" +anstyle = "1.0.13" derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } @@ -22,7 +24,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } serde = { version = "1.0.125", features = ["derive"] } serde_json = "1.0.59" -termcolor = "1.2.0" termize = "0.2" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 0d10f29d31d64..66a0bfd777a2f 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -16,6 +16,8 @@ use std::iter; use std::path::Path; use std::sync::Arc; +use anstream::{AutoStream, ColorChoice}; +use anstyle::{AnsiColor, Effects}; use derive_setters::Setters; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; @@ -25,7 +27,6 @@ use rustc_lint_defs::pluralize; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::{FileLines, FileName, SourceFile, Span, char_width, str_width}; -use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use tracing::{debug, instrument, trace, warn}; use crate::registry::Registry; @@ -525,10 +526,6 @@ impl Emitter for HumanEmitter { !self.short_message } - fn supports_color(&self) -> bool { - self.dst.supports_color() - } - fn translator(&self) -> &Translator { &self.translator } @@ -1701,7 +1698,6 @@ impl HumanEmitter { } else { col_sep_before_no_show_source = true; } - // print out the span location and spacer before we print the annotated source // to do this, we need to know if this span will be primary let is_primary = primary_lo.file.name == annotated_file.file.name; @@ -3127,7 +3123,6 @@ impl FileWithAnnotatedLines { multiline_depth: 0, }); } - let mut output = vec![]; let mut multiline_annotations = vec![]; @@ -3361,7 +3356,7 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{2069}', "�"), ]; -fn normalize_whitespace(s: &str) -> String { +pub(crate) fn normalize_whitespace(s: &str) -> String { const { let mut i = 1; while i < OUTPUT_REPLACEMENTS.len() { @@ -3406,7 +3401,7 @@ fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool { ) } -fn emit_to_destination( +pub(crate) fn emit_to_destination( rendered_buffer: &[Vec], lvl: &Level, dst: &mut Destination, @@ -3429,10 +3424,8 @@ fn emit_to_destination( let _buffer_lock = lock::acquire_global_lock("rustc_errors"); for (pos, line) in rendered_buffer.iter().enumerate() { for part in line { - let style = part.style.color_spec(*lvl); - dst.set_color(&style)?; - write!(dst, "{}", part.text)?; - dst.reset()?; + let style = part.style.anstyle(*lvl); + write!(dst, "{style}{}{style:#}", part.text)?; } if !short_message && (!lvl.is_failure_note() || pos != rendered_buffer.len() - 1) { writeln!(dst)?; @@ -3442,11 +3435,11 @@ fn emit_to_destination( Ok(()) } -pub type Destination = Box; +pub type Destination = AutoStream>; struct Buffy { - buffer_writer: BufferWriter, - buffer: Buffer, + buffer_writer: std::io::Stderr, + buffer: Vec, } impl Write for Buffy { @@ -3455,7 +3448,7 @@ impl Write for Buffy { } fn flush(&mut self) -> io::Result<()> { - self.buffer_writer.print(&self.buffer)?; + self.buffer_writer.write_all(&self.buffer)?; self.buffer.clear(); Ok(()) } @@ -3470,22 +3463,16 @@ impl Drop for Buffy { } } -impl WriteColor for Buffy { - fn supports_color(&self) -> bool { - self.buffer.supports_color() - } - - fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { - self.buffer.set_color(spec) - } - - fn reset(&mut self) -> io::Result<()> { - self.buffer.reset() - } -} - pub fn stderr_destination(color: ColorConfig) -> Destination { + let buffer_writer = std::io::stderr(); let choice = color.to_color_choice(); + // We need to resolve `ColorChoice::Auto` before `Box`ing since + // `ColorChoice::Auto` on `dyn Write` will always resolve to `Never` + let choice = if matches!(choice, ColorChoice::Auto) { + AutoStream::choice(&buffer_writer) + } else { + choice + }; // On Windows we'll be performing global synchronization on the entire // system for emitting rustc errors, so there's no need to buffer // anything. @@ -3493,60 +3480,42 @@ pub fn stderr_destination(color: ColorConfig) -> Destination { // On non-Windows we rely on the atomicity of `write` to ensure errors // don't get all jumbled up. if cfg!(windows) { - Box::new(StandardStream::stderr(choice)) + AutoStream::new(Box::new(buffer_writer), choice) } else { - let buffer_writer = BufferWriter::stderr(choice); - let buffer = buffer_writer.buffer(); - Box::new(Buffy { buffer_writer, buffer }) + let buffer = Vec::new(); + AutoStream::new(Box::new(Buffy { buffer_writer, buffer }), choice) } } /// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead. /// /// See #36178. -const BRIGHT_BLUE: Color = if cfg!(windows) { Color::Cyan } else { Color::Blue }; +const BRIGHT_BLUE: anstyle::Style = if cfg!(windows) { + AnsiColor::BrightCyan.on_default() +} else { + AnsiColor::BrightBlue.on_default() +}; impl Style { - fn color_spec(&self, lvl: Level) -> ColorSpec { - let mut spec = ColorSpec::new(); + pub(crate) fn anstyle(&self, lvl: Level) -> anstyle::Style { match self { - Style::Addition => { - spec.set_fg(Some(Color::Green)).set_intense(true); - } - Style::Removal => { - spec.set_fg(Some(Color::Red)).set_intense(true); - } - Style::LineAndColumn => {} - Style::LineNumber => { - spec.set_bold(true); - spec.set_intense(true); - spec.set_fg(Some(BRIGHT_BLUE)); - } - Style::Quotation => {} - Style::MainHeaderMsg => { - spec.set_bold(true); - if cfg!(windows) { - spec.set_intense(true).set_fg(Some(Color::White)); - } - } - Style::UnderlinePrimary | Style::LabelPrimary => { - spec = lvl.color(); - spec.set_bold(true); - } - Style::UnderlineSecondary | Style::LabelSecondary => { - spec.set_bold(true).set_intense(true); - spec.set_fg(Some(BRIGHT_BLUE)); - } - Style::HeaderMsg | Style::NoStyle => {} - Style::Level(lvl) => { - spec = lvl.color(); - spec.set_bold(true); - } - Style::Highlight => { - spec.set_bold(true).set_fg(Some(Color::Magenta)); + Style::Addition => AnsiColor::BrightGreen.on_default(), + Style::Removal => AnsiColor::BrightRed.on_default(), + Style::LineAndColumn => anstyle::Style::new(), + Style::LineNumber => BRIGHT_BLUE.effects(Effects::BOLD), + Style::Quotation => anstyle::Style::new(), + Style::MainHeaderMsg => if cfg!(windows) { + AnsiColor::BrightWhite.on_default() + } else { + anstyle::Style::new() } + .effects(Effects::BOLD), + Style::UnderlinePrimary | Style::LabelPrimary => lvl.color().effects(Effects::BOLD), + Style::UnderlineSecondary | Style::LabelSecondary => BRIGHT_BLUE.effects(Effects::BOLD), + Style::HeaderMsg | Style::NoStyle => anstyle::Style::new(), + Style::Level(lvl) => lvl.color().effects(Effects::BOLD), + Style::Highlight => AnsiColor::Magenta.on_default().effects(Effects::BOLD), } - spec } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 719d4ca625ae7..03ce1d82ef3c4 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -15,6 +15,7 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use std::vec; +use anstream::{AutoStream, ColorChoice}; use derive_setters::Setters; use rustc_data_structures::sync::IntoDynSyncSend; use rustc_error_messages::FluentArgs; @@ -23,7 +24,6 @@ use rustc_span::Span; use rustc_span::hygiene::ExpnData; use rustc_span::source_map::{FilePathMapping, SourceMap}; use serde::Serialize; -use termcolor::{ColorSpec, WriteColor}; use crate::diagnostic::IsLint; use crate::emitter::{ @@ -333,7 +333,7 @@ impl Diagnostic { // generate regular command line output and store it in the json // A threadsafe buffer for writing. - #[derive(Default, Clone)] + #[derive(Clone)] struct BufWriter(Arc>>); impl Write for BufWriter { @@ -344,19 +344,6 @@ impl Diagnostic { self.0.lock().unwrap().flush() } } - impl WriteColor for BufWriter { - fn supports_color(&self) -> bool { - false - } - - fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> { - Ok(()) - } - - fn reset(&mut self) -> io::Result<()> { - Ok(()) - } - } let translated_message = je.translator.translate_messages(&diag.messages, &args); @@ -382,13 +369,15 @@ impl Diagnostic { children .insert(0, Diagnostic::from_sub_diagnostic(&diag.emitted_at_sub_diag(), &args, je)); } - let buf = BufWriter::default(); - let mut dst: Destination = Box::new(buf.clone()); + let buf = BufWriter(Arc::new(Mutex::new(Vec::new()))); let short = je.json_rendered.short(); - match je.color_config { - ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), - ColorConfig::Never => {} - } + let dst: Destination = AutoStream::new( + Box::new(buf.clone()), + match je.color_config.to_color_choice() { + ColorChoice::Auto => ColorChoice::Always, + choice => choice, + }, + ); HumanEmitter::new(dst, je.translator.clone()) .short_message(short) .sm(je.sm.clone()) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8869799ce90d9..b0aa90b18817a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -39,6 +39,12 @@ use std::path::{Path, PathBuf}; use std::{fmt, panic}; use Level::*; +// Used by external projects such as `rust-gpu`. +// See https://github.com/rust-lang/rust/pull/115393. +pub use anstream::{AutoStream, ColorChoice}; +pub use anstyle::{ + Ansi256Color, AnsiColor, Color, EffectIter, Effects, Reset, RgbColor, Style as Anstyle, +}; pub use codes::*; pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ @@ -69,9 +75,6 @@ pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DUMMY_SP, Loc, Span}; pub use snippet::Style; -// Used by external projects such as `rust-gpu`. -// See https://github.com/rust-lang/rust/pull/115393. -pub use termcolor::{Color, ColorSpec, WriteColor}; use tracing::debug; use crate::emitter::TimingEvent; @@ -1961,25 +1964,21 @@ impl fmt::Display for Level { } impl Level { - fn color(self) -> ColorSpec { - let mut spec = ColorSpec::new(); + fn color(self) -> anstyle::Style { match self { - Bug | Fatal | Error | DelayedBug => { - spec.set_fg(Some(Color::Red)).set_intense(true); - } + Bug | Fatal | Error | DelayedBug => AnsiColor::BrightRed.on_default(), ForceWarning | Warning => { - spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); - } - Note | OnceNote => { - spec.set_fg(Some(Color::Green)).set_intense(true); - } - Help | OnceHelp => { - spec.set_fg(Some(Color::Cyan)).set_intense(true); + if cfg!(windows) { + AnsiColor::BrightYellow.on_default() + } else { + AnsiColor::Yellow.on_default() + } } - FailureNote => {} + Note | OnceNote => AnsiColor::BrightGreen.on_default(), + Help | OnceHelp => AnsiColor::BrightCyan.on_default(), + FailureNote => anstyle::Style::new(), Allow | Expect => unreachable!(), } - spec } pub fn to_str(self) -> &'static str { diff --git a/compiler/rustc_errors/src/markdown/mod.rs b/compiler/rustc_errors/src/markdown/mod.rs index 64576cdc8caca..4f5e2328234d8 100644 --- a/compiler/rustc_errors/src/markdown/mod.rs +++ b/compiler/rustc_errors/src/markdown/mod.rs @@ -4,7 +4,6 @@ use std::io; -use termcolor::{Buffer, BufferWriter, ColorChoice}; mod parse; mod term; @@ -19,15 +18,15 @@ impl<'a> MdStream<'a> { parse::entrypoint(s) } - /// Write formatted output to a termcolor buffer - pub fn write_termcolor_buf(&self, buf: &mut Buffer) -> io::Result<()> { + /// Write formatted output to an anstream buffer + pub fn write_anstream_buf(&self, buf: &mut Vec) -> io::Result<()> { term::entrypoint(self, buf) } } -/// Create a termcolor buffer with the `Always` color choice -pub fn create_stdout_bufwtr() -> BufferWriter { - BufferWriter::stdout(ColorChoice::Always) +/// Create an anstream buffer with the `Always` color choice +pub fn create_stdout_bufwtr() -> anstream::Stdout { + anstream::Stdout::always(std::io::stdout()) } /// A single tokentree within a Markdown document diff --git a/compiler/rustc_errors/src/markdown/term.rs b/compiler/rustc_errors/src/markdown/term.rs index fe1d80bdbe8e0..b0ce01548f00f 100644 --- a/compiler/rustc_errors/src/markdown/term.rs +++ b/compiler/rustc_errors/src/markdown/term.rs @@ -1,7 +1,7 @@ use std::cell::Cell; use std::io::{self, Write}; -use termcolor::{Buffer, Color, ColorSpec, WriteColor}; +use anstyle::{AnsiColor, Effects, Style}; use crate::markdown::{MdStream, MdTree}; @@ -15,7 +15,7 @@ thread_local! { } /// Print to terminal output to a buffer -pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result<()> { +pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Vec) -> io::Result<()> { #[cfg(not(test))] if let Some((w, _)) = termize::dimensions() { WIDTH.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH)); @@ -23,57 +23,65 @@ pub(crate) fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result< write_stream(stream, buf, None, 0)?; buf.write_all(b"\n") } - /// Write the buffer, reset to the default style after each fn write_stream( MdStream(stream): &MdStream<'_>, - buf: &mut Buffer, - default: Option<&ColorSpec>, + buf: &mut Vec, + default: Option