rspack_codespan_reporting/term/config.rs
1use termcolor::{Color, ColorSpec};
2
3use crate::diagnostic::{LabelStyle, Severity};
4
5/// Configures how a diagnostic is rendered.
6#[derive(Clone, Debug)]
7pub struct Config {
8 /// The display style to use when rendering diagnostics.
9 /// Defaults to: [`DisplayStyle::Rich`].
10 ///
11 /// [`DisplayStyle::Rich`]: DisplayStyle::Rich
12 pub display_style: DisplayStyle,
13 /// Column width of tabs.
14 /// Defaults to: `4`.
15 pub tab_width: usize,
16 /// Styles to use when rendering the diagnostic.
17 pub styles: Styles,
18 /// Characters to use when rendering the diagnostic.
19 pub chars: Chars,
20 /// The minimum number of lines to be shown after the line on which a multiline [`Label`] begins.
21 ///
22 /// Defaults to: `3`.
23 ///
24 /// [`Label`]: crate::diagnostic::Label
25 pub start_context_lines: usize,
26 /// The minimum number of lines to be shown before the line on which a multiline [`Label`] ends.
27 ///
28 /// Defaults to: `1`.
29 ///
30 /// [`Label`]: crate::diagnostic::Label
31 pub end_context_lines: usize,
32 /// The minimum number of lines before a label that should be included for context.
33 ///
34 /// Defaults to: `0`.
35 pub before_label_lines: usize,
36 /// The minimum number of lines after a label that should be included for context.
37 ///
38 /// Defaults to: `0`.
39 pub after_label_lines: usize,
40}
41
42impl Default for Config {
43 fn default() -> Config {
44 Config {
45 display_style: DisplayStyle::Rich,
46 tab_width: 4,
47 styles: Styles::default(),
48 chars: Chars::default(),
49 start_context_lines: 3,
50 end_context_lines: 1,
51 before_label_lines: 0,
52 after_label_lines: 0,
53 }
54 }
55}
56
57/// The display style to use when rendering diagnostics.
58#[derive(Clone, Debug)]
59pub enum DisplayStyle {
60 /// Output a richly formatted diagnostic, with source code previews.
61 ///
62 /// ```text
63 /// error[E0001]: unexpected type in `+` application
64 /// ┌─ test:2:9
65 /// │
66 /// 2 │ (+ test "")
67 /// │ ^^ expected `Int` but found `String`
68 /// │
69 /// = expected type `Int`
70 /// found type `String`
71 ///
72 /// error[E0002]: Bad config found
73 ///
74 /// ```
75 Rich,
76 /// Output a condensed diagnostic, with a line number, severity, message and notes (if any).
77 ///
78 /// ```text
79 /// test:2:9: error[E0001]: unexpected type in `+` application
80 /// = expected type `Int`
81 /// found type `String`
82 ///
83 /// error[E0002]: Bad config found
84 /// ```
85 Medium,
86 /// Output a short diagnostic, with a line number, severity, and message.
87 ///
88 /// ```text
89 /// test:2:9: error[E0001]: unexpected type in `+` application
90 /// error[E0002]: Bad config found
91 /// ```
92 Short,
93}
94
95/// Styles to use when rendering the diagnostic.
96#[derive(Clone, Debug)]
97pub struct Styles {
98 /// The style to use when rendering bug headers.
99 /// Defaults to `fg:red bold intense`.
100 pub header_bug: ColorSpec,
101 /// The style to use when rendering error headers.
102 /// Defaults to `fg:red bold intense`.
103 pub header_error: ColorSpec,
104 /// The style to use when rendering warning headers.
105 /// Defaults to `fg:yellow bold intense`.
106 pub header_warning: ColorSpec,
107 /// The style to use when rendering note headers.
108 /// Defaults to `fg:green bold intense`.
109 pub header_note: ColorSpec,
110 /// The style to use when rendering help headers.
111 /// Defaults to `fg:cyan bold intense`.
112 pub header_help: ColorSpec,
113 /// The style to use when the main diagnostic message.
114 /// Defaults to `bold intense`.
115 pub header_message: ColorSpec,
116
117 /// The style to use when rendering bug labels.
118 /// Defaults to `fg:red`.
119 pub primary_label_bug: ColorSpec,
120 /// The style to use when rendering error labels.
121 /// Defaults to `fg:red`.
122 pub primary_label_error: ColorSpec,
123 /// The style to use when rendering warning labels.
124 /// Defaults to `fg:yellow`.
125 pub primary_label_warning: ColorSpec,
126 /// The style to use when rendering note labels.
127 /// Defaults to `fg:green`.
128 pub primary_label_note: ColorSpec,
129 /// The style to use when rendering help labels.
130 /// Defaults to `fg:cyan`.
131 pub primary_label_help: ColorSpec,
132 /// The style to use when rendering secondary labels.
133 /// Defaults `fg:blue` (or `fg:cyan` on windows).
134 pub secondary_label: ColorSpec,
135
136 /// The style to use when rendering the line numbers.
137 /// Defaults `fg:blue` (or `fg:cyan` on windows).
138 pub line_number: ColorSpec,
139 /// The style to use when rendering the source code borders.
140 /// Defaults `fg:blue` (or `fg:cyan` on windows).
141 pub source_border: ColorSpec,
142 /// The style to use when rendering the note bullets.
143 /// Defaults `fg:blue` (or `fg:cyan` on windows).
144 pub note_bullet: ColorSpec,
145}
146
147impl Styles {
148 /// The style used to mark a header at a given severity.
149 pub fn header(&self, severity: Severity) -> &ColorSpec {
150 match severity {
151 Severity::Bug => &self.header_bug,
152 Severity::Error => &self.header_error,
153 Severity::Warning => &self.header_warning,
154 Severity::Note => &self.header_note,
155 Severity::Help => &self.header_help,
156 }
157 }
158
159 /// The style used to mark a primary or secondary label at a given severity.
160 pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec {
161 match (label_style, severity) {
162 (LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug,
163 (LabelStyle::Primary, Severity::Error) => &self.primary_label_error,
164 (LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning,
165 (LabelStyle::Primary, Severity::Note) => &self.primary_label_note,
166 (LabelStyle::Primary, Severity::Help) => &self.primary_label_help,
167 (LabelStyle::Secondary, _) => &self.secondary_label,
168 }
169 }
170
171 #[doc(hidden)]
172 pub fn with_blue(blue: Color) -> Styles {
173 let header = ColorSpec::new().set_bold(true).set_intense(true).clone();
174
175 Styles {
176 header_bug: header.clone().set_fg(Some(Color::Red)).clone(),
177 header_error: header.clone().set_fg(Some(Color::Red)).clone(),
178 header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(),
179 header_note: header.clone().set_fg(Some(Color::Green)).clone(),
180 header_help: header.clone().set_fg(Some(Color::Cyan)).clone(),
181 header_message: header,
182
183 primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
184 primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
185 primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(),
186 primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(),
187 primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
188 secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(),
189
190 line_number: ColorSpec::new().set_fg(Some(blue)).clone(),
191 source_border: ColorSpec::new().set_fg(Some(blue)).clone(),
192 note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(),
193 }
194 }
195}
196
197impl Default for Styles {
198 fn default() -> Styles {
199 // Blue is really difficult to see on the standard windows command line
200 #[cfg(windows)]
201 const BLUE: Color = Color::Cyan;
202 #[cfg(not(windows))]
203 const BLUE: Color = Color::Blue;
204
205 Self::with_blue(BLUE)
206 }
207}
208
209/// Characters to use when rendering the diagnostic.
210///
211/// By using [`Chars::ascii()`] you can switch to an ASCII-only format suitable
212/// for rendering on terminals that do not support box drawing characters.
213#[derive(Clone, Debug)]
214pub struct Chars {
215 /// The characters to use for the top-left border of the snippet.
216 /// Defaults to: `"┌─"` or `"-->"` with [`Chars::ascii()`].
217 pub snippet_start: String,
218 /// The character to use for the left border of the source.
219 /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
220 pub source_border_left: char,
221 /// The character to use for the left border break of the source.
222 /// Defaults to: `'·'` or `'.'` with [`Chars::ascii()`].
223 pub source_border_left_break: char,
224
225 /// The character to use for the note bullet.
226 /// Defaults to: `'='`.
227 pub note_bullet: char,
228
229 /// The character to use for marking a single-line primary label.
230 /// Defaults to: `'^'`.
231 pub single_primary_caret: char,
232 /// The character to use for marking a single-line secondary label.
233 /// Defaults to: `'-'`.
234 pub single_secondary_caret: char,
235
236 /// The character to use for marking the start of a multi-line primary label.
237 /// Defaults to: `'^'`.
238 pub multi_primary_caret_start: char,
239 /// The character to use for marking the end of a multi-line primary label.
240 /// Defaults to: `'^'`.
241 pub multi_primary_caret_end: char,
242 /// The character to use for marking the start of a multi-line secondary label.
243 /// Defaults to: `'\''`.
244 pub multi_secondary_caret_start: char,
245 /// The character to use for marking the end of a multi-line secondary label.
246 /// Defaults to: `'\''`.
247 pub multi_secondary_caret_end: char,
248 /// The character to use for the top-left corner of a multi-line label.
249 /// Defaults to: `'╭'` or `'/'` with [`Chars::ascii()`].
250 pub multi_top_left: char,
251 /// The character to use for the top of a multi-line label.
252 /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`].
253 pub multi_top: char,
254 /// The character to use for the bottom-left corner of a multi-line label.
255 /// Defaults to: `'╰'` or `'\'` with [`Chars::ascii()`].
256 pub multi_bottom_left: char,
257 /// The character to use when marking the bottom of a multi-line label.
258 /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`].
259 pub multi_bottom: char,
260 /// The character to use for the left of a multi-line label.
261 /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
262 pub multi_left: char,
263
264 /// The character to use for the left of a pointer underneath a caret.
265 /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
266 pub pointer_left: char,
267}
268
269impl Default for Chars {
270 fn default() -> Chars {
271 Chars::box_drawing()
272 }
273}
274
275impl Chars {
276 /// A character set that uses Unicode box drawing characters.
277 pub fn box_drawing() -> Chars {
278 Chars {
279 snippet_start: "┌─".into(),
280 source_border_left: '│',
281 source_border_left_break: '·',
282
283 note_bullet: '=',
284
285 single_primary_caret: '^',
286 single_secondary_caret: '-',
287
288 multi_primary_caret_start: '^',
289 multi_primary_caret_end: '^',
290 multi_secondary_caret_start: '\'',
291 multi_secondary_caret_end: '\'',
292 multi_top_left: '╭',
293 multi_top: '─',
294 multi_bottom_left: '╰',
295 multi_bottom: '─',
296 multi_left: '│',
297
298 pointer_left: '│',
299 }
300 }
301
302 /// A character set that only uses ASCII characters.
303 ///
304 /// This is useful if your terminal's font does not support box drawing
305 /// characters well and results in output that looks similar to rustc's
306 /// diagnostic output.
307 pub fn ascii() -> Chars {
308 Chars {
309 snippet_start: "-->".into(),
310 source_border_left: '|',
311 source_border_left_break: '.',
312
313 note_bullet: '=',
314
315 single_primary_caret: '^',
316 single_secondary_caret: '-',
317
318 multi_primary_caret_start: '^',
319 multi_primary_caret_end: '^',
320 multi_secondary_caret_start: '\'',
321 multi_secondary_caret_end: '\'',
322 multi_top_left: '/',
323 multi_top: '-',
324 multi_bottom_left: '\\',
325 multi_bottom: '-',
326 multi_left: '|',
327
328 pointer_left: '|',
329 }
330 }
331}