codespan_reporting/term/
config.rs

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