compose_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    /// The style to use when rendering suggested additions.
147    /// Defaults `fg:green`.
148    pub suggest_add: ColorSpec,
149
150    /// The style to use when rendering suggested removals.
151    /// Defaults `fg:red`.
152    pub suggest_remove: ColorSpec,
153
154    /// The style to use when rendering suggested replacement messages.
155    /// Defaults `fg:yellow`.
156    pub suggest_replace: ColorSpec,
157}
158
159impl Styles {
160    /// The style used to mark a header at a given severity.
161    pub fn header(&self, severity: Severity) -> &ColorSpec {
162        match severity {
163            Severity::Bug => &self.header_bug,
164            Severity::Error => &self.header_error,
165            Severity::Warning => &self.header_warning,
166            Severity::Note => &self.header_note,
167            Severity::Help => &self.header_help,
168        }
169    }
170
171    /// The style used to mark a primary or secondary label at a given severity.
172    pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec {
173        match (label_style, severity) {
174            (LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug,
175            (LabelStyle::Primary, Severity::Error) => &self.primary_label_error,
176            (LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning,
177            (LabelStyle::Primary, Severity::Note) => &self.primary_label_note,
178            (LabelStyle::Primary, Severity::Help) => &self.primary_label_help,
179            (LabelStyle::Secondary, _) => &self.secondary_label,
180        }
181    }
182
183    #[doc(hidden)]
184    pub fn with_blue(blue: Color) -> Styles {
185        let header = ColorSpec::new().set_bold(true).set_intense(true).clone();
186
187        Styles {
188            header_bug: header.clone().set_fg(Some(Color::Red)).clone(),
189            header_error: header.clone().set_fg(Some(Color::Red)).clone(),
190            header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(),
191            header_note: header.clone().set_fg(Some(Color::Green)).clone(),
192            header_help: header.clone().set_fg(Some(Color::Cyan)).clone(),
193            header_message: header,
194
195            primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
196            primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
197            primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(),
198            primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(),
199            primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
200            secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(),
201
202            line_number: ColorSpec::new().set_fg(Some(blue)).clone(),
203            source_border: ColorSpec::new().set_fg(Some(blue)).clone(),
204            note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(),
205
206            suggest_add: ColorSpec::new().set_fg(Some(Color::Green)).clone(),
207            suggest_remove: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
208            suggest_replace: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(),
209        }
210    }
211}
212
213impl Default for Styles {
214    fn default() -> Styles {
215        // Blue is really difficult to see on the standard windows command line
216        #[cfg(windows)]
217        const BLUE: Color = Color::Cyan;
218        #[cfg(not(windows))]
219        const BLUE: Color = Color::Blue;
220
221        Self::with_blue(BLUE)
222    }
223}
224
225/// Characters to use when rendering the diagnostic.
226///
227/// By using [`Chars::ascii()`] you can switch to an ASCII-only format suitable
228/// for rendering on terminals that do not support box drawing characters.
229#[derive(Clone, Debug)]
230pub struct Chars {
231    /// The characters to use for the top-left border of the snippet.
232    /// Defaults to: `"┌─"` or `"-->"` with [`Chars::ascii()`].
233    pub snippet_start: String,
234    /// The character to use for the left border of the source.
235    /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
236    pub source_border_left: char,
237    /// The character to use for the left border break of the source.
238    /// Defaults to: `'·'` or `'.'` with [`Chars::ascii()`].
239    pub source_border_left_break: char,
240
241    /// The character to use for the note bullet.
242    /// Defaults to: `'='`.
243    pub note_bullet: char,
244
245    /// The character to use for marking a single-line primary label.
246    /// Defaults to: `'^'`.
247    pub single_primary_caret: char,
248    /// The character to use for marking a single-line secondary label.
249    /// Defaults to: `'-'`.
250    pub single_secondary_caret: char,
251
252    /// The character to use for marking the start of a multi-line primary label.
253    /// Defaults to: `'^'`.
254    pub multi_primary_caret_start: char,
255    /// The character to use for marking the end of a multi-line primary label.
256    /// Defaults to: `'^'`.
257    pub multi_primary_caret_end: char,
258    /// The character to use for marking the start of a multi-line secondary label.
259    /// Defaults to: `'\''`.
260    pub multi_secondary_caret_start: char,
261    /// The character to use for marking the end of a multi-line secondary label.
262    /// Defaults to: `'\''`.
263    pub multi_secondary_caret_end: char,
264    /// The character to use for the top-left corner of a multi-line label.
265    /// Defaults to: `'╭'` or `'/'` with [`Chars::ascii()`].
266    pub multi_top_left: char,
267    /// The character to use for the top of a multi-line label.
268    /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`].
269    pub multi_top: char,
270    /// The character to use for the bottom-left corner of a multi-line label.
271    /// Defaults to: `'╰'` or `'\'` with [`Chars::ascii()`].
272    pub multi_bottom_left: char,
273    /// The character to use when marking the bottom of a multi-line label.
274    /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`].
275    pub multi_bottom: char,
276    /// The character to use for the left of a multi-line label.
277    /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
278    pub multi_left: char,
279
280    /// The character to use for the left of a pointer underneath a caret.
281    /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
282    pub pointer_left: char,
283}
284
285impl Default for Chars {
286    fn default() -> Chars {
287        Chars::box_drawing()
288    }
289}
290
291impl Chars {
292    /// A character set that uses Unicode box drawing characters.
293    pub fn box_drawing() -> Chars {
294        Chars {
295            snippet_start: "┌─".into(),
296            source_border_left: '│',
297            source_border_left_break: '·',
298
299            note_bullet: '=',
300
301            single_primary_caret: '^',
302            single_secondary_caret: '-',
303
304            multi_primary_caret_start: '^',
305            multi_primary_caret_end: '^',
306            multi_secondary_caret_start: '\'',
307            multi_secondary_caret_end: '\'',
308            multi_top_left: '╭',
309            multi_top: '─',
310            multi_bottom_left: '╰',
311            multi_bottom: '─',
312            multi_left: '│',
313
314            pointer_left: '│',
315        }
316    }
317
318    /// A character set that only uses ASCII characters.
319    ///
320    /// This is useful if your terminal's font does not support box drawing
321    /// characters well and results in output that looks similar to rustc's
322    /// diagnostic output.
323    pub fn ascii() -> Chars {
324        Chars {
325            snippet_start: "-->".into(),
326            source_border_left: '|',
327            source_border_left_break: '.',
328
329            note_bullet: '=',
330
331            single_primary_caret: '^',
332            single_secondary_caret: '-',
333
334            multi_primary_caret_start: '^',
335            multi_primary_caret_end: '^',
336            multi_secondary_caret_start: '\'',
337            multi_secondary_caret_end: '\'',
338            multi_top_left: '/',
339            multi_top: '-',
340            multi_bottom_left: '\\',
341            multi_bottom: '-',
342            multi_left: '|',
343
344            pointer_left: '|',
345        }
346    }
347}