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}