Skip to content

Commit b2dd918

Browse files
committed
Give the Renderer trait more of a reason to be
1 parent 1d1859b commit b2dd918

File tree

3 files changed

+325
-292
lines changed

3 files changed

+325
-292
lines changed

Diff for: src/formatter/types.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ where
130130
fn clone(&self) -> Self {
131131
match self {
132132
SourceLine::Content { span, subspan } => SourceLine::Content {
133-
span: span.clone(),
133+
span: *span,
134134
subspan: subspan.clone(),
135135
},
136136
SourceLine::Annotation { message, underline } => SourceLine::Annotation {

Diff for: src/renderer/default.rs

+288
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
use crate::{
2+
formatter::{DisplayLine, FormattedSnippet, Mark, MarkKind, RawLine, SourceLine},
3+
renderer::{log10usize, max_line_num, max_marks_width, Renderer},
4+
DebugAndDisplay, Level, SpanWriter,
5+
};
6+
use std::io;
7+
8+
pub struct Ascii {
9+
use_ansi: bool,
10+
}
11+
12+
impl Ascii {
13+
pub fn plain() -> Self {
14+
Ascii { use_ansi: false }
15+
}
16+
17+
pub fn ansi() -> Self {
18+
Ascii { use_ansi: true }
19+
}
20+
}
21+
22+
impl Ascii {
23+
#[inline(always)]
24+
fn reset(&self, w: &mut dyn io::Write) -> io::Result<()> {
25+
if self.use_ansi {
26+
write!(w, "\x1B[0m")
27+
} else {
28+
Ok(())
29+
}
30+
}
31+
32+
fn bold(&self, w: &mut dyn io::Write) -> io::Result<()> {
33+
if self.use_ansi {
34+
write!(w, "\x1B[0;1m")
35+
} else {
36+
Ok(())
37+
}
38+
}
39+
40+
// fg(Fixed(9))
41+
#[inline(always)]
42+
fn bright_red(&self, w: &mut dyn io::Write) -> io::Result<()> {
43+
if self.use_ansi {
44+
write!(w, "\x1B[0;31;1m")
45+
} else {
46+
Ok(())
47+
}
48+
}
49+
50+
// bold + fg(Fixed(9))
51+
#[inline(always)]
52+
fn bold_bright_red(&self, w: &mut dyn io::Write) -> io::Result<()> {
53+
if self.use_ansi {
54+
write!(w, "\x1B[1;31;1m")
55+
} else {
56+
Ok(())
57+
}
58+
}
59+
60+
// fg(Fixed(11))
61+
#[inline(always)]
62+
fn bright_yellow(&self, w: &mut dyn io::Write) -> io::Result<()> {
63+
if self.use_ansi {
64+
write!(w, "\x1B[0;33;1m")
65+
} else {
66+
Ok(())
67+
}
68+
}
69+
70+
// bold + fg(Fixed(11))
71+
#[inline(always)]
72+
fn bold_bright_yellow(&self, w: &mut dyn io::Write) -> io::Result<()> {
73+
if self.use_ansi {
74+
write!(w, "\x1B[1;33;1m")
75+
} else {
76+
Ok(())
77+
}
78+
}
79+
80+
// fg(Fixed(12))
81+
#[inline(always)]
82+
fn bright_blue(&self, w: &mut dyn io::Write) -> io::Result<()> {
83+
if self.use_ansi {
84+
write!(w, "\x1B[0;34;1m")
85+
} else {
86+
Ok(())
87+
}
88+
}
89+
90+
// bold + fg(Fixed(12))
91+
#[inline(always)]
92+
fn bold_bright_blue(&self, w: &mut dyn io::Write) -> io::Result<()> {
93+
if self.use_ansi {
94+
write!(w, "\x1B[1;34;1m")
95+
} else {
96+
Ok(())
97+
}
98+
}
99+
100+
// fg(Fixed(14))
101+
#[inline(always)]
102+
fn bright_cyan(&self, w: &mut dyn io::Write) -> io::Result<()> {
103+
if self.use_ansi {
104+
write!(w, "\x1B[0;36;1m")
105+
} else {
106+
Ok(())
107+
}
108+
}
109+
110+
// bold + fg(Fixed(14))
111+
#[inline(always)]
112+
fn bold_bright_cyan(&self, w: &mut dyn io::Write) -> io::Result<()> {
113+
if self.use_ansi {
114+
write!(w, "\x1B[1;36;1m")
115+
} else {
116+
Ok(())
117+
}
118+
}
119+
120+
// FIXME: emitted ANSI codes are highly redundant when repeated
121+
#[inline(always)]
122+
fn style_for(&self, level: Level, w: &mut dyn io::Write) -> io::Result<()> {
123+
match level {
124+
Level::Error => self.bright_red(w),
125+
Level::Warning => self.bright_yellow(w),
126+
Level::Info => self.bright_blue(w),
127+
Level::Note => self.reset(w),
128+
Level::Help => self.bright_cyan(w),
129+
}
130+
}
131+
132+
// FIXME: emitted ANSI codes are highly redundant when repeated
133+
#[inline(always)]
134+
fn style_bold_for(&self, level: Level, w: &mut dyn io::Write) -> io::Result<()> {
135+
match level {
136+
Level::Error => self.bold_bright_red(w),
137+
Level::Warning => self.bold_bright_yellow(w),
138+
Level::Info => self.bold_bright_blue(w),
139+
Level::Note => self.reset(w),
140+
Level::Help => self.bold_bright_cyan(w),
141+
}
142+
}
143+
}
144+
145+
impl Ascii {
146+
fn render_marks(&self, marks: &[Mark], w: &mut dyn io::Write) -> io::Result<()> {
147+
for mark in marks {
148+
self.style_for(mark.level, w)?;
149+
let c = match mark.kind {
150+
MarkKind::Start => '/',
151+
MarkKind::Continue => '|',
152+
MarkKind::Here => '\\',
153+
};
154+
write!(w, "{}", c)?;
155+
}
156+
self.reset(w)
157+
}
158+
159+
fn render_source_line<Span: crate::Span>(
160+
&self,
161+
line: &SourceLine<'_, Span>,
162+
f: &dyn SpanWriter<Span>,
163+
w: &mut dyn io::Write,
164+
) -> io::Result<()> {
165+
match line {
166+
SourceLine::Content { span, subspan } => f.write(w, span, subspan),
167+
SourceLine::Annotation { message, underline } => {
168+
write!(w, "{:>width$}", "", width = underline.0)?;
169+
self.style_bold_for(message.map_or(Level::Info, |message| message.level), w)?;
170+
// FIXME: respect level for pointer character
171+
if underline.0 == 0 {
172+
write!(w, "{:_>width$} ", "^", width = underline.1)?;
173+
} else {
174+
write!(w, "{:->width$} ", "", width = underline.1)?;
175+
}
176+
write!(
177+
w,
178+
"{}",
179+
message.map_or(&"" as &dyn DebugAndDisplay, |message| message.text)
180+
)?;
181+
self.reset(w)
182+
}
183+
SourceLine::Empty => Ok(()),
184+
}
185+
}
186+
187+
fn render_raw_line(
188+
&self,
189+
line: &RawLine<'_>,
190+
line_num_width: usize,
191+
w: &mut dyn io::Write,
192+
) -> io::Result<()> {
193+
match line {
194+
&RawLine::Origin { path, pos } => {
195+
write!(w, "{:>width$}", "", width = line_num_width)?;
196+
self.bold_bright_blue(w)?;
197+
write!(w, "-->")?;
198+
self.reset(w)?;
199+
write!(w, " {}", path)?;
200+
if let Some((line, column)) = pos {
201+
write!(w, ":{}:{}", line, column)?;
202+
}
203+
writeln!(w)
204+
}
205+
RawLine::Message { message } => {
206+
self.style_for(message.level, w)?;
207+
let cta = match message.level {
208+
Level::Error => "error",
209+
Level::Warning => "warning",
210+
Level::Info => "info",
211+
Level::Note => "note",
212+
Level::Help => "help",
213+
};
214+
write!(w, "{:>width$} = {}", "", cta, width = line_num_width)?;
215+
writeln!(w, ": {}", message.text)
216+
}
217+
RawLine::Title { title } => {
218+
self.style_bold_for(title.message.level, w)?;
219+
let cta = match title.message.level {
220+
Level::Error => "error",
221+
Level::Warning => "warning",
222+
Level::Info => "info",
223+
Level::Note => "note",
224+
Level::Help => "help",
225+
};
226+
write!(w, "{}", cta)?;
227+
if let Some(code) = title.code {
228+
write!(w, "[{}]", code)?;
229+
}
230+
self.bold(w)?;
231+
writeln!(w, ": {}", title.message.text)
232+
}
233+
}
234+
}
235+
}
236+
237+
impl Renderer for Ascii {
238+
fn render<'a, Span: crate::Span>(
239+
&self,
240+
snippet: &FormattedSnippet<'a, Span>,
241+
f: &dyn SpanWriter<Span>,
242+
w: &mut dyn io::Write,
243+
) -> io::Result<()> {
244+
let max_line_num = max_line_num(snippet).unwrap_or(0);
245+
let marks_width = max_marks_width(snippet);
246+
247+
for line in &snippet.lines {
248+
self.render_line(line, log10usize(max_line_num), marks_width, f, w)?;
249+
}
250+
251+
self.reset(w)
252+
}
253+
254+
fn render_line<Span: crate::Span>(
255+
&self,
256+
line: &DisplayLine<'_, Span>,
257+
line_num_width: usize,
258+
marks_width: usize,
259+
f: &dyn SpanWriter<Span>,
260+
w: &mut dyn io::Write,
261+
) -> io::Result<()> {
262+
match line {
263+
DisplayLine::Source {
264+
lineno,
265+
inline_marks,
266+
line,
267+
} => {
268+
self.bold_bright_blue(w)?;
269+
if let Some(lineno) = lineno {
270+
write!(w, "{:>width$} | ", lineno, width = line_num_width)?;
271+
} else {
272+
write!(w, "{:>width$} | ", "", width = line_num_width)?;
273+
}
274+
self.reset(w)?;
275+
write!(
276+
w,
277+
"{:>width$}",
278+
"",
279+
width = marks_width - inline_marks.len()
280+
)?;
281+
self.render_marks(inline_marks, w)?;
282+
self.render_source_line(line, f, w)?;
283+
writeln!(w)
284+
}
285+
DisplayLine::Raw(line) => self.render_raw_line(line, line_num_width, w),
286+
}
287+
}
288+
}

0 commit comments

Comments
 (0)