refactor: Move to anstream + anstyle for styling

This commit is contained in:
Scott Schafer 2025-06-03 19:27:03 -06:00
parent bd4a8004c2
commit 926d4535cd
No known key found for this signature in database
14 changed files with 166 additions and 203 deletions

View file

@ -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",

View file

@ -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;
}

View file

@ -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

View file

@ -16,6 +16,8 @@ use std::iter;
use std::path::Path;
use std::sync::Arc;
use anstream::{AutoStream, ColorChoice};
use anstyle::{Ansi256Color, 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}', "<EFBFBD>"),
];
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,13 +3401,14 @@ fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool {
)
}
fn emit_to_destination(
pub(crate) fn emit_to_destination(
rendered_buffer: &[Vec<StyledString>],
lvl: &Level,
dst: &mut Destination,
short_message: bool,
) -> io::Result<()> {
use crate::lock;
const RESET: anstyle::Reset = anstyle::Reset;
// In order to prevent error message interleaving, where multiple error lines get intermixed
// when multiple compiler processes error simultaneously, we emit errors with additional
@ -3429,10 +3425,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, "{RESET}{style}{}{RESET}", part.text)?;
}
if !short_message && (!lvl.is_failure_note() || pos != rendered_buffer.len() - 1) {
writeln!(dst)?;
@ -3442,11 +3436,11 @@ fn emit_to_destination(
Ok(())
}
pub type Destination = Box<dyn WriteColor + Send>;
pub type Destination = AutoStream<Box<dyn Write + Send>>;
struct Buffy {
buffer_writer: BufferWriter,
buffer: Buffer,
buffer_writer: std::io::Stderr,
buffer: Vec<u8>,
}
impl Write for Buffy {
@ -3455,7 +3449,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 +3464,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 +3481,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) {
Ansi256Color::from_ansi(AnsiColor::BrightCyan).on_default()
} else {
Ansi256Color::from_ansi(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 => Ansi256Color::from_ansi(AnsiColor::BrightGreen).on_default(),
Style::Removal => Ansi256Color::from_ansi(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) {
Ansi256Color::from_ansi(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
}
}

View file

@ -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<Mutex<Vec<u8>>>);
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())

View file

@ -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,23 @@ 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);
Ansi256Color::from_ansi(AnsiColor::BrightRed).on_default()
}
ForceWarning | Warning => {
spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
if cfg!(windows) {
Ansi256Color::from_ansi(AnsiColor::BrightYellow).on_default()
} else {
AnsiColor::Yellow.on_default()
}
}
Note | OnceNote => {
spec.set_fg(Some(Color::Green)).set_intense(true);
}
Help | OnceHelp => {
spec.set_fg(Some(Color::Cyan)).set_intense(true);
}
FailureNote => {}
Note | OnceNote => Ansi256Color::from_ansi(AnsiColor::BrightGreen).on_default(),
Help | OnceHelp => Ansi256Color::from_ansi(AnsiColor::BrightCyan).on_default(),
FailureNote => anstyle::Style::new(),
Allow | Expect => unreachable!(),
}
spec
}
pub fn to_str(self) -> &'static str {

View file

@ -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<u8>) -> 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

View file

@ -1,11 +1,12 @@
use std::cell::Cell;
use std::io::{self, Write};
use termcolor::{Buffer, Color, ColorSpec, WriteColor};
use anstyle::{Ansi256Color, AnsiColor, Effects, Style};
use crate::markdown::{MdStream, MdTree};
const DEFAULT_COLUMN_WIDTH: usize = 140;
const RESET: anstyle::Reset = anstyle::Reset;
thread_local! {
/// Track the position of viewable characters in our buffer
@ -15,7 +16,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<u8>) -> io::Result<()> {
#[cfg(not(test))]
if let Some((w, _)) = termize::dimensions() {
WIDTH.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH));
@ -23,57 +24,66 @@ 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<u8>,
default: Option<Style>,
indent: usize,
) -> io::Result<()> {
match default {
Some(c) => buf.set_color(c)?,
None => buf.reset()?,
Some(c) => write!(buf, "{c:#}{c}")?,
None => write!(buf, "{RESET}")?,
}
for tt in stream {
write_tt(tt, buf, indent)?;
write_tt(tt, buf, default, indent)?;
if let Some(c) = default {
buf.set_color(c)?;
write!(buf, "{c:#}{c}")?;
}
}
buf.reset()?;
write!(buf, "{RESET}")?;
Ok(())
}
fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()> {
fn write_tt(
tt: &MdTree<'_>,
buf: &mut Vec<u8>,
_default: Option<Style>,
indent: usize,
) -> io::Result<()> {
match tt {
MdTree::CodeBlock { txt, lang: _ } => {
buf.set_color(ColorSpec::new().set_dimmed(true))?;
buf.write_all(txt.as_bytes())?;
write!(buf, "{RESET}")?;
let style = Style::new().effects(Effects::DIMMED);
write!(buf, "{style}{txt}")?;
}
MdTree::CodeInline(txt) => {
buf.set_color(ColorSpec::new().set_dimmed(true))?;
write_wrapping(buf, txt, indent, None)?;
write!(buf, "{RESET}")?;
let style = Style::new().effects(Effects::DIMMED);
write_wrapping(buf, txt, indent, None, Some(style))?;
}
MdTree::Strong(txt) => {
buf.set_color(ColorSpec::new().set_bold(true))?;
write_wrapping(buf, txt, indent, None)?;
write!(buf, "{RESET}")?;
let style = Style::new().effects(Effects::BOLD);
write_wrapping(buf, txt, indent, None, Some(style))?;
}
MdTree::Emphasis(txt) => {
buf.set_color(ColorSpec::new().set_italic(true))?;
write_wrapping(buf, txt, indent, None)?;
write!(buf, "{RESET}")?;
let style = Style::new().effects(Effects::ITALIC);
write_wrapping(buf, txt, indent, None, Some(style))?;
}
MdTree::Strikethrough(txt) => {
buf.set_color(ColorSpec::new().set_strikethrough(true))?;
write_wrapping(buf, txt, indent, None)?;
write!(buf, "{RESET}")?;
let style = Style::new().effects(Effects::STRIKETHROUGH);
write_wrapping(buf, txt, indent, None, Some(style))?;
}
MdTree::PlainText(txt) => {
write_wrapping(buf, txt, indent, None)?;
write_wrapping(buf, txt, indent, None, None)?;
}
MdTree::Link { disp, link } => {
write_wrapping(buf, disp, indent, Some(link))?;
write_wrapping(buf, disp, indent, Some(link), None)?;
}
MdTree::ParagraphBreak => {
buf.write_all(b"\n\n")?;
@ -88,33 +98,37 @@ fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()>
reset_cursor();
}
MdTree::Heading(n, stream) => {
let mut cs = ColorSpec::new();
cs.set_fg(Some(Color::Cyan));
match n {
1 => cs.set_intense(true).set_bold(true).set_underline(true),
2 => cs.set_intense(true).set_underline(true),
3 => cs.set_intense(true).set_italic(true),
4.. => cs.set_underline(true).set_italic(true),
let cs = match n {
1 => Ansi256Color::from_ansi(AnsiColor::BrightCyan)
.on_default()
.effects(Effects::BOLD | Effects::UNDERLINE),
2 => Ansi256Color::from_ansi(AnsiColor::BrightCyan)
.on_default()
.effects(Effects::UNDERLINE),
3 => Ansi256Color::from_ansi(AnsiColor::BrightCyan)
.on_default()
.effects(Effects::ITALIC),
4.. => AnsiColor::Cyan.on_default().effects(Effects::UNDERLINE | Effects::ITALIC),
0 => unreachable!(),
};
write_stream(stream, buf, Some(&cs), 0)?;
write_stream(stream, buf, Some(cs), 0)?;
buf.write_all(b"\n")?;
}
MdTree::OrderedListItem(n, stream) => {
let base = format!("{n}. ");
write_wrapping(buf, &format!("{base:<4}"), indent, None)?;
write_wrapping(buf, &format!("{base:<4}"), indent, None, None)?;
write_stream(stream, buf, None, indent + 4)?;
}
MdTree::UnorderedListItem(stream) => {
let base = "* ";
write_wrapping(buf, &format!("{base:<4}"), indent, None)?;
write_wrapping(buf, &format!("{base:<4}"), indent, None, None)?;
write_stream(stream, buf, None, indent + 4)?;
}
// Patterns popped in previous step
MdTree::Comment(_) | MdTree::LinkDef { .. } | MdTree::RefLink { .. } => unreachable!(),
}
buf.reset()?;
write!(buf, "{RESET}")?;
Ok(())
}
@ -126,12 +140,16 @@ fn reset_cursor() {
/// Change to be generic on Write for testing. If we have a link URL, we don't
/// count the extra tokens to make it clickable.
fn write_wrapping<B: io::Write>(
buf: &mut B,
fn write_wrapping(
buf: &mut Vec<u8>,
text: &str,
indent: usize,
link_url: Option<&str>,
style: Option<Style>,
) -> io::Result<()> {
if let Some(style) = &style {
write!(buf, "{style}")?;
}
let ind_ws = &b" "[..indent];
let mut to_write = text;
if let Some(url) = link_url {
@ -179,7 +197,6 @@ fn write_wrapping<B: io::Write>(
if link_url.is_some() {
buf.write_all(b"\x1b]8;;\x1b\\")?;
}
Ok(())
})
}

View file

@ -1,8 +1,5 @@
use std::io::BufWriter;
use std::path::PathBuf;
use termcolor::{BufferWriter, ColorChoice};
use super::*;
const INPUT: &str = include_str!("input.md");
@ -35,19 +32,20 @@ quis dolor non venenatis. Aliquam ut. ";
#[test]
fn test_wrapping_write() {
WIDTH.with(|w| w.set(TEST_WIDTH));
let mut buf = BufWriter::new(Vec::new());
let mut buf = Vec::new();
let txt = TXT.replace("-\n", "-").replace("_\n", "_").replace('\n', " ").replace(" ", "");
write_wrapping(&mut buf, &txt, 0, None).unwrap();
write_wrapping(&mut buf, &txt, 4, None).unwrap();
write_wrapping(&mut buf, &txt, 0, None, None).unwrap();
write_wrapping(&mut buf, &txt, 4, None, None).unwrap();
write_wrapping(
&mut buf,
"Sample link lorem ipsum dolor sit amet. ",
4,
Some("link-address-placeholder"),
None,
)
.unwrap();
write_wrapping(&mut buf, &txt, 0, None).unwrap();
let out = String::from_utf8(buf.into_inner().unwrap()).unwrap();
write_wrapping(&mut buf, &txt, 0, None, None).unwrap();
let out = String::from_utf8(buf).unwrap();
let out = out
.replace("\x1b\\", "")
.replace('\x1b', "")
@ -66,18 +64,17 @@ fn test_output() {
// Capture `--bless` when run via ./x
let bless = std::env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
let ast = MdStream::parse_str(INPUT);
let bufwtr = BufferWriter::stderr(ColorChoice::Always);
let mut buffer = bufwtr.buffer();
ast.write_termcolor_buf(&mut buffer).unwrap();
let mut buffer = Vec::new();
ast.write_anstream_buf(&mut buffer).unwrap();
let mut blessed = PathBuf::new();
blessed.extend(OUTPUT_PATH);
if bless {
std::fs::write(&blessed, buffer.into_inner()).unwrap();
std::fs::write(&blessed, buffer.as_slice()).unwrap();
eprintln!("blessed output at {}", blessed.display());
} else {
let output = buffer.into_inner();
let output = buffer.as_slice();
if std::fs::read(blessed).unwrap() != output {
// hack: I don't know any way to write bytes to the captured stdout
// that cargo test uses

View file

@ -26,7 +26,4 @@ unicode-width = "0.2.0"
[dev-dependencies]
# tidy-alphabetical-start
termcolor = "1.2"
# tidy-alphabetical-end

View file

@ -14,13 +14,12 @@ use rustc_ast::{self as ast, PatKind, visit};
use rustc_ast_pretty::pprust::item_to_string;
use rustc_errors::emitter::{HumanEmitter, OutputTheme};
use rustc_errors::translation::Translator;
use rustc_errors::{DiagCtxt, MultiSpan, PResult};
use rustc_errors::{AutoStream, DiagCtxt, MultiSpan, PResult};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{
BytePos, FileName, Pos, Span, Symbol, create_default_session_globals_then, kw, sym,
};
use termcolor::WriteColor;
use crate::lexer::StripTokens;
use crate::parser::{ForceCollect, Parser};
@ -44,9 +43,10 @@ fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc<SourceMap>, Arc<Mut
let output = Arc::new(Mutex::new(Vec::new()));
let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
let translator = Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), translator)
.sm(Some(source_map.clone()))
.diagnostic_width(Some(140));
let mut emitter =
HumanEmitter::new(AutoStream::never(Box::new(Shared { data: output.clone() })), translator)
.sm(Some(source_map.clone()))
.diagnostic_width(Some(140));
emitter = emitter.theme(theme);
let dcx = DiagCtxt::new(Box::new(emitter));
(dcx, source_map, output)
@ -160,20 +160,6 @@ struct Shared<T: Write> {
data: Arc<Mutex<T>>,
}
impl<T: Write> WriteColor for Shared<T> {
fn supports_color(&self) -> bool {
false
}
fn set_color(&mut self, _spec: &termcolor::ColorSpec) -> io::Result<()> {
Ok(())
}
fn reset(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<T: Write> Write for Shared<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.data.lock().unwrap().write(buf)

View file

@ -9,7 +9,7 @@ use rustc_ast::token::{Delimiter, TokenKind};
use rustc_ast::tokenstream::TokenTree;
use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind};
use rustc_errors::emitter::stderr_destination;
use rustc_errors::{ColorConfig, DiagCtxtHandle};
use rustc_errors::{AutoStream, ColorConfig, DiagCtxtHandle};
use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str;
use rustc_session::parse::ParseSess;
@ -463,7 +463,7 @@ fn parse_source(
.supports_color();
// Any errors in parsing should also appear when the doctest is compiled for real, so just
// send all the errors that the parser emits directly into a `Sink` instead of stderr.
let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
let emitter = HumanEmitter::new(AutoStream::never(Box::new(io::sink())), translator);
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();

View file

@ -6,7 +6,7 @@ use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST};
use clippy_utils::diagnostics::span_lint;
use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind};
use rustc_errors::emitter::HumanEmitter;
use rustc_errors::{Diag, DiagCtxt};
use rustc_errors::{AutoStream, Diag, DiagCtxt};
use rustc_lint::LateContext;
use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str;
@ -44,7 +44,7 @@ pub fn check(
let filename = FileName::anon_source_code(&code);
let translator = rustc_driver::default_translator();
let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
let emitter = HumanEmitter::new(AutoStream::never(Box::new(io::sink())), translator);
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
#[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));

View file

@ -329,7 +329,11 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"aho-corasick",
"allocator-api2", // FIXME: only appears in Cargo.lock due to https://github.com/rust-lang/cargo/issues/10801
"annotate-snippets",
"anstream",
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"ar_archive_writer",
"arrayref",
"arrayvec",
@ -341,6 +345,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"cc",
"cfg-if",
"cfg_aliases",
"colorchoice",
"constant_time_eq",
"cpufeatures",
"crc32fast",
@ -390,6 +395,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"indexmap",
"intl-memoizer",
"intl_pluralrules",
"is_terminal_polyfill",
"itertools",
"itoa",
"jiff",
@ -414,6 +420,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"object",
"odht",
"once_cell",
"once_cell_polyfill",
"overload",
"parking_lot",
"parking_lot_core",
@ -475,7 +482,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"syn",
"synstructure",
"tempfile",
"termcolor",
"termize",
"thin-vec",
"thiserror",
@ -507,6 +513,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"unicode-security",
"unicode-width",
"unicode-xid",
"utf8parse",
"valuable",
"version_check",
"wasi",
@ -514,7 +521,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"wasmparser",
"winapi",
"winapi-i686-pc-windows-gnu",
"winapi-util",
"winapi-x86_64-pc-windows-gnu",
"windows",
"windows-collections",