Skip to content

Commit 78b5a80

Browse files
committed
Auto merge of #147207 - Muscraft:anstyle-anstream, r=davidtwco
refactor: Move to anstream + anstyle for styling `rustc` uses [`termcolor`](https://crates.io/crates/termcolor) for styling and writing, while `annotate-snippets` uses [`anstyle`](https://crates.io/crates/anstyle) for styling and currently writes directly to a `String`. When rendering directly to a terminal, there isn't/shouldn't be any differences. Still, there are differences in the escape sequences, which leads to slightly different output in JSON and SVG tests. As part of my work to have `rustc` use `annotate-snippets`, and to reduce the test differences between the two, I switched `rustc` to use `anstlye` and [`anstream`](https://crates.io/crates/anstream) for styling and writing. The first commit migrates to `anstyle` and `anstream` and notably does not change the output. This is because it includes extra formatting to ensure that `anstyle` + `anstream` match the current output exactly. Most of this code is unnecessary, as it adds redundant resets or uses 256-color (8-bit) when it could be using 4-bit color. The subsequent commits remove this extra formatting while maintaining the correct output when rendered. [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/channel/147480-t-compiler.2Fdiagnostics/topic/annotate-snippets.20hurdles)
2 parents 4a54b26 + ab07e80 commit 78b5a80

29 files changed

+1210
-1242
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ dependencies = [
9595

9696
[[package]]
9797
name = "anstyle"
98-
version = "1.0.11"
98+
version = "1.0.13"
9999
source = "registry+https://github.com/rust-lang/crates.io-index"
100-
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
100+
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
101101

102102
[[package]]
103103
name = "anstyle-lossy"
@@ -3755,6 +3755,8 @@ name = "rustc_errors"
37553755
version = "0.0.0"
37563756
dependencies = [
37573757
"annotate-snippets 0.11.5",
3758+
"anstream",
3759+
"anstyle",
37583760
"derive_setters",
37593761
"rustc_abi",
37603762
"rustc_ast",
@@ -3771,7 +3773,6 @@ dependencies = [
37713773
"rustc_span",
37723774
"serde",
37733775
"serde_json",
3774-
"termcolor",
37753776
"termize",
37763777
"tracing",
37773778
"windows 0.61.3",
@@ -4324,7 +4325,6 @@ dependencies = [
43244325
"rustc_macros",
43254326
"rustc_session",
43264327
"rustc_span",
4327-
"termcolor",
43284328
"thin-vec",
43294329
"tracing",
43304330
"unicode-normalization",

compiler/rustc_driver_impl/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -521,11 +521,11 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) {
521521
};
522522

523523
// Try to prettify the raw markdown text. The result can be used by the pager or on stdout.
524-
let pretty_data = {
524+
let mut pretty_data = {
525525
let mdstream = markdown::MdStream::parse_str(content);
526526
let bufwtr = markdown::create_stdout_bufwtr();
527-
let mut mdbuf = bufwtr.buffer();
528-
if mdstream.write_termcolor_buf(&mut mdbuf).is_ok() { Some((bufwtr, mdbuf)) } else { None }
527+
let mut mdbuf = Vec::new();
528+
if mdstream.write_anstream_buf(&mut mdbuf).is_ok() { Some((bufwtr, mdbuf)) } else { None }
529529
};
530530

531531
// Try to print via the pager, pretty output if possible.
@@ -546,8 +546,8 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) {
546546
}
547547

548548
// The pager failed. Try to print pretty output to stdout.
549-
if let Some((bufwtr, mdbuf)) = &pretty_data
550-
&& bufwtr.print(mdbuf).is_ok()
549+
if let Some((bufwtr, mdbuf)) = &mut pretty_data
550+
&& bufwtr.write_all(&mdbuf).is_ok()
551551
{
552552
return;
553553
}

compiler/rustc_errors/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ edition = "2024"
66
[dependencies]
77
# tidy-alphabetical-start
88
annotate-snippets = "0.11"
9+
anstream = "0.6.20"
10+
anstyle = "1.0.13"
911
derive_setters = "0.1.6"
1012
rustc_abi = { path = "../rustc_abi" }
1113
rustc_ast = { path = "../rustc_ast" }
@@ -22,7 +24,6 @@ rustc_serialize = { path = "../rustc_serialize" }
2224
rustc_span = { path = "../rustc_span" }
2325
serde = { version = "1.0.125", features = ["derive"] }
2426
serde_json = "1.0.59"
25-
termcolor = "1.2.0"
2627
termize = "0.2"
2728
tracing = "0.1"
2829
# tidy-alphabetical-end

compiler/rustc_errors/src/emitter.rs

Lines changed: 42 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use std::iter;
1616
use std::path::Path;
1717
use std::sync::Arc;
1818

19+
use anstream::{AutoStream, ColorChoice};
20+
use anstyle::{AnsiColor, Effects};
1921
use derive_setters::Setters;
2022
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
2123
use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
@@ -25,7 +27,6 @@ use rustc_lint_defs::pluralize;
2527
use rustc_span::hygiene::{ExpnKind, MacroKind};
2628
use rustc_span::source_map::SourceMap;
2729
use rustc_span::{FileLines, FileName, SourceFile, Span, char_width, str_width};
28-
use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
2930
use tracing::{debug, instrument, trace, warn};
3031

3132
use crate::registry::Registry;
@@ -525,10 +526,6 @@ impl Emitter for HumanEmitter {
525526
!self.short_message
526527
}
527528

528-
fn supports_color(&self) -> bool {
529-
self.dst.supports_color()
530-
}
531-
532529
fn translator(&self) -> &Translator {
533530
&self.translator
534531
}
@@ -1701,7 +1698,6 @@ impl HumanEmitter {
17011698
} else {
17021699
col_sep_before_no_show_source = true;
17031700
}
1704-
17051701
// print out the span location and spacer before we print the annotated source
17061702
// to do this, we need to know if this span will be primary
17071703
let is_primary = primary_lo.file.name == annotated_file.file.name;
@@ -3127,7 +3123,6 @@ impl FileWithAnnotatedLines {
31273123
multiline_depth: 0,
31283124
});
31293125
}
3130-
31313126
let mut output = vec![];
31323127
let mut multiline_annotations = vec![];
31333128

@@ -3361,7 +3356,7 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
33613356
('\u{2069}', "�"),
33623357
];
33633358

3364-
fn normalize_whitespace(s: &str) -> String {
3359+
pub(crate) fn normalize_whitespace(s: &str) -> String {
33653360
const {
33663361
let mut i = 1;
33673362
while i < OUTPUT_REPLACEMENTS.len() {
@@ -3406,7 +3401,7 @@ fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool {
34063401
)
34073402
}
34083403

3409-
fn emit_to_destination(
3404+
pub(crate) fn emit_to_destination(
34103405
rendered_buffer: &[Vec<StyledString>],
34113406
lvl: &Level,
34123407
dst: &mut Destination,
@@ -3429,10 +3424,8 @@ fn emit_to_destination(
34293424
let _buffer_lock = lock::acquire_global_lock("rustc_errors");
34303425
for (pos, line) in rendered_buffer.iter().enumerate() {
34313426
for part in line {
3432-
let style = part.style.color_spec(*lvl);
3433-
dst.set_color(&style)?;
3434-
write!(dst, "{}", part.text)?;
3435-
dst.reset()?;
3427+
let style = part.style.anstyle(*lvl);
3428+
write!(dst, "{style}{}{style:#}", part.text)?;
34363429
}
34373430
if !short_message && (!lvl.is_failure_note() || pos != rendered_buffer.len() - 1) {
34383431
writeln!(dst)?;
@@ -3442,11 +3435,11 @@ fn emit_to_destination(
34423435
Ok(())
34433436
}
34443437

3445-
pub type Destination = Box<dyn WriteColor + Send>;
3438+
pub type Destination = AutoStream<Box<dyn Write + Send>>;
34463439

34473440
struct Buffy {
3448-
buffer_writer: BufferWriter,
3449-
buffer: Buffer,
3441+
buffer_writer: std::io::Stderr,
3442+
buffer: Vec<u8>,
34503443
}
34513444

34523445
impl Write for Buffy {
@@ -3455,7 +3448,7 @@ impl Write for Buffy {
34553448
}
34563449

34573450
fn flush(&mut self) -> io::Result<()> {
3458-
self.buffer_writer.print(&self.buffer)?;
3451+
self.buffer_writer.write_all(&self.buffer)?;
34593452
self.buffer.clear();
34603453
Ok(())
34613454
}
@@ -3470,83 +3463,59 @@ impl Drop for Buffy {
34703463
}
34713464
}
34723465

3473-
impl WriteColor for Buffy {
3474-
fn supports_color(&self) -> bool {
3475-
self.buffer.supports_color()
3476-
}
3477-
3478-
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
3479-
self.buffer.set_color(spec)
3480-
}
3481-
3482-
fn reset(&mut self) -> io::Result<()> {
3483-
self.buffer.reset()
3484-
}
3485-
}
3486-
34873466
pub fn stderr_destination(color: ColorConfig) -> Destination {
3467+
let buffer_writer = std::io::stderr();
34883468
let choice = color.to_color_choice();
3469+
// We need to resolve `ColorChoice::Auto` before `Box`ing since
3470+
// `ColorChoice::Auto` on `dyn Write` will always resolve to `Never`
3471+
let choice = if matches!(choice, ColorChoice::Auto) {
3472+
AutoStream::choice(&buffer_writer)
3473+
} else {
3474+
choice
3475+
};
34893476
// On Windows we'll be performing global synchronization on the entire
34903477
// system for emitting rustc errors, so there's no need to buffer
34913478
// anything.
34923479
//
34933480
// On non-Windows we rely on the atomicity of `write` to ensure errors
34943481
// don't get all jumbled up.
34953482
if cfg!(windows) {
3496-
Box::new(StandardStream::stderr(choice))
3483+
AutoStream::new(Box::new(buffer_writer), choice)
34973484
} else {
3498-
let buffer_writer = BufferWriter::stderr(choice);
3499-
let buffer = buffer_writer.buffer();
3500-
Box::new(Buffy { buffer_writer, buffer })
3485+
let buffer = Vec::new();
3486+
AutoStream::new(Box::new(Buffy { buffer_writer, buffer }), choice)
35013487
}
35023488
}
35033489

35043490
/// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead.
35053491
///
35063492
/// See #36178.
3507-
const BRIGHT_BLUE: Color = if cfg!(windows) { Color::Cyan } else { Color::Blue };
3493+
const BRIGHT_BLUE: anstyle::Style = if cfg!(windows) {
3494+
AnsiColor::BrightCyan.on_default()
3495+
} else {
3496+
AnsiColor::BrightBlue.on_default()
3497+
};
35083498

35093499
impl Style {
3510-
fn color_spec(&self, lvl: Level) -> ColorSpec {
3511-
let mut spec = ColorSpec::new();
3500+
pub(crate) fn anstyle(&self, lvl: Level) -> anstyle::Style {
35123501
match self {
3513-
Style::Addition => {
3514-
spec.set_fg(Some(Color::Green)).set_intense(true);
3515-
}
3516-
Style::Removal => {
3517-
spec.set_fg(Some(Color::Red)).set_intense(true);
3518-
}
3519-
Style::LineAndColumn => {}
3520-
Style::LineNumber => {
3521-
spec.set_bold(true);
3522-
spec.set_intense(true);
3523-
spec.set_fg(Some(BRIGHT_BLUE));
3524-
}
3525-
Style::Quotation => {}
3526-
Style::MainHeaderMsg => {
3527-
spec.set_bold(true);
3528-
if cfg!(windows) {
3529-
spec.set_intense(true).set_fg(Some(Color::White));
3530-
}
3531-
}
3532-
Style::UnderlinePrimary | Style::LabelPrimary => {
3533-
spec = lvl.color();
3534-
spec.set_bold(true);
3535-
}
3536-
Style::UnderlineSecondary | Style::LabelSecondary => {
3537-
spec.set_bold(true).set_intense(true);
3538-
spec.set_fg(Some(BRIGHT_BLUE));
3539-
}
3540-
Style::HeaderMsg | Style::NoStyle => {}
3541-
Style::Level(lvl) => {
3542-
spec = lvl.color();
3543-
spec.set_bold(true);
3544-
}
3545-
Style::Highlight => {
3546-
spec.set_bold(true).set_fg(Some(Color::Magenta));
3502+
Style::Addition => AnsiColor::BrightGreen.on_default(),
3503+
Style::Removal => AnsiColor::BrightRed.on_default(),
3504+
Style::LineAndColumn => anstyle::Style::new(),
3505+
Style::LineNumber => BRIGHT_BLUE.effects(Effects::BOLD),
3506+
Style::Quotation => anstyle::Style::new(),
3507+
Style::MainHeaderMsg => if cfg!(windows) {
3508+
AnsiColor::BrightWhite.on_default()
3509+
} else {
3510+
anstyle::Style::new()
35473511
}
3512+
.effects(Effects::BOLD),
3513+
Style::UnderlinePrimary | Style::LabelPrimary => lvl.color().effects(Effects::BOLD),
3514+
Style::UnderlineSecondary | Style::LabelSecondary => BRIGHT_BLUE.effects(Effects::BOLD),
3515+
Style::HeaderMsg | Style::NoStyle => anstyle::Style::new(),
3516+
Style::Level(lvl) => lvl.color().effects(Effects::BOLD),
3517+
Style::Highlight => AnsiColor::Magenta.on_default().effects(Effects::BOLD),
35483518
}
3549-
spec
35503519
}
35513520
}
35523521

compiler/rustc_errors/src/json.rs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::path::Path;
1515
use std::sync::{Arc, Mutex};
1616
use std::vec;
1717

18+
use anstream::{AutoStream, ColorChoice};
1819
use derive_setters::Setters;
1920
use rustc_data_structures::sync::IntoDynSyncSend;
2021
use rustc_error_messages::FluentArgs;
@@ -23,7 +24,6 @@ use rustc_span::Span;
2324
use rustc_span::hygiene::ExpnData;
2425
use rustc_span::source_map::{FilePathMapping, SourceMap};
2526
use serde::Serialize;
26-
use termcolor::{ColorSpec, WriteColor};
2727

2828
use crate::diagnostic::IsLint;
2929
use crate::emitter::{
@@ -333,7 +333,7 @@ impl Diagnostic {
333333
// generate regular command line output and store it in the json
334334

335335
// A threadsafe buffer for writing.
336-
#[derive(Default, Clone)]
336+
#[derive(Clone)]
337337
struct BufWriter(Arc<Mutex<Vec<u8>>>);
338338

339339
impl Write for BufWriter {
@@ -344,19 +344,6 @@ impl Diagnostic {
344344
self.0.lock().unwrap().flush()
345345
}
346346
}
347-
impl WriteColor for BufWriter {
348-
fn supports_color(&self) -> bool {
349-
false
350-
}
351-
352-
fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
353-
Ok(())
354-
}
355-
356-
fn reset(&mut self) -> io::Result<()> {
357-
Ok(())
358-
}
359-
}
360347

361348
let translated_message = je.translator.translate_messages(&diag.messages, &args);
362349

@@ -382,13 +369,15 @@ impl Diagnostic {
382369
children
383370
.insert(0, Diagnostic::from_sub_diagnostic(&diag.emitted_at_sub_diag(), &args, je));
384371
}
385-
let buf = BufWriter::default();
386-
let mut dst: Destination = Box::new(buf.clone());
372+
let buf = BufWriter(Arc::new(Mutex::new(Vec::new())));
387373
let short = je.json_rendered.short();
388-
match je.color_config {
389-
ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)),
390-
ColorConfig::Never => {}
391-
}
374+
let dst: Destination = AutoStream::new(
375+
Box::new(buf.clone()),
376+
match je.color_config.to_color_choice() {
377+
ColorChoice::Auto => ColorChoice::Always,
378+
choice => choice,
379+
},
380+
);
392381
HumanEmitter::new(dst, je.translator.clone())
393382
.short_message(short)
394383
.sm(je.sm.clone())

0 commit comments

Comments
 (0)