refactor: Move to anstream + anstyle for styling
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user