codespan_reporting/term/
config.rs

1use alloc::string::String;
2
3/// Configures how a diagnostic is rendered.
4#[derive(Clone, Debug)]
5pub struct Config {
6    /// The display style to use when rendering diagnostics.
7    /// Defaults to: [`DisplayStyle::Rich`].
8    ///
9    /// [`DisplayStyle::Rich`]: DisplayStyle::Rich
10    pub display_style: DisplayStyle,
11    /// Column width of tabs.
12    /// Defaults to: `4`.
13    pub tab_width: usize,
14
15    /// Characters to use when rendering the diagnostic.
16    pub chars: Chars,
17    /// The minimum number of lines to be shown after the line on which a multiline [`Label`] begins.
18    ///
19    /// Defaults to: `3`.
20    ///
21    /// [`Label`]: crate::diagnostic::Label
22    pub start_context_lines: usize,
23    /// The minimum number of lines to be shown before the line on which a multiline [`Label`] ends.
24    ///
25    /// Defaults to: `1`.
26    ///
27    /// [`Label`]: crate::diagnostic::Label
28    pub end_context_lines: usize,
29    /// The minimum number of lines before a label that should be included for context.
30    ///
31    /// Defaults to: `0`.
32    pub before_label_lines: usize,
33    /// The minimum number of lines after a label that should be included for context.
34    ///
35    /// Defaults to: `0`.
36    pub after_label_lines: usize,
37}
38
39impl Default for Config {
40    fn default() -> Config {
41        Config {
42            display_style: DisplayStyle::Rich,
43            tab_width: 4,
44            chars: Chars::default(),
45            start_context_lines: 3,
46            end_context_lines: 1,
47            before_label_lines: 0,
48            after_label_lines: 0,
49        }
50    }
51}
52
53/// The display style to use when rendering diagnostics.
54#[derive(Clone, Debug)]
55pub enum DisplayStyle {
56    /// Output a richly formatted diagnostic, with source code previews.
57    ///
58    /// ```text
59    /// error[E0001]: unexpected type in `+` application
60    ///   ┌─ test:2:9
61    ///   │
62    /// 2 │ (+ test "")
63    ///   │         ^^ expected `Int` but found `String`
64    ///   │
65    ///   = expected type `Int`
66    ///        found type `String`
67    ///
68    /// error[E0002]: Bad config found
69    ///
70    /// ```
71    Rich,
72    /// Output a condensed diagnostic, with a line number, severity, message and notes (if any).
73    ///
74    /// ```text
75    /// test:2:9: error[E0001]: unexpected type in `+` application
76    /// = expected type `Int`
77    ///      found type `String`
78    ///
79    /// error[E0002]: Bad config found
80    /// ```
81    Medium,
82    /// Output a short diagnostic, with a line number, severity, and message.
83    ///
84    /// ```text
85    /// test:2:9: error[E0001]: unexpected type in `+` application
86    /// error[E0002]: Bad config found
87    /// ```
88    Short,
89}
90
91#[cfg(feature = "termcolor")]
92pub mod styles {
93    use super::super::renderer::{self, WriteStyle};
94
95    use crate::diagnostic::{LabelStyle, Severity};
96    use termcolor::{Color, ColorSpec, WriteColor};
97
98    // re-export
99    pub use termcolor;
100
101    /// Styles to use when rendering the diagnostic.
102    #[derive(Clone, Debug)]
103    pub struct Styles {
104        /// The style to use when rendering bug headers.
105        /// Defaults to `fg:red bold intense`.
106        pub header_bug: ColorSpec,
107        /// The style to use when rendering error headers.
108        /// Defaults to `fg:red bold intense`.
109        pub header_error: ColorSpec,
110        /// The style to use when rendering warning headers.
111        /// Defaults to `fg:yellow bold intense`.
112        pub header_warning: ColorSpec,
113        /// The style to use when rendering note headers.
114        /// Defaults to `fg:green bold intense`.
115        pub header_note: ColorSpec,
116        /// The style to use when rendering help headers.
117        /// Defaults to `fg:cyan bold intense`.
118        pub header_help: ColorSpec,
119        /// The style to use when the main diagnostic message.
120        /// Defaults to `bold intense`.
121        pub header_message: ColorSpec,
122
123        /// The style to use when rendering bug labels.
124        /// Defaults to `fg:red`.
125        pub primary_label_bug: ColorSpec,
126        /// The style to use when rendering error labels.
127        /// Defaults to `fg:red`.
128        pub primary_label_error: ColorSpec,
129        /// The style to use when rendering warning labels.
130        /// Defaults to `fg:yellow`.
131        pub primary_label_warning: ColorSpec,
132        /// The style to use when rendering note labels.
133        /// Defaults to `fg:green`.
134        pub primary_label_note: ColorSpec,
135        /// The style to use when rendering help labels.
136        /// Defaults to `fg:cyan`.
137        pub primary_label_help: ColorSpec,
138        /// The style to use when rendering secondary labels.
139        /// Defaults `fg:blue` (or `fg:cyan` on windows).
140        pub secondary_label: ColorSpec,
141
142        /// The style to use when rendering the line numbers.
143        /// Defaults `fg:blue` (or `fg:cyan` on windows).
144        pub line_number: ColorSpec,
145        /// The style to use when rendering the source code borders.
146        /// Defaults `fg:blue` (or `fg:cyan` on windows).
147        pub source_border: ColorSpec,
148        /// The style to use when rendering the note bullets.
149        /// Defaults `fg:blue` (or `fg:cyan` on windows).
150        pub note_bullet: ColorSpec,
151    }
152
153    impl Styles {
154        /// The style used to mark a header at a given severity.
155        #[must_use]
156        pub fn header(&self, severity: Severity) -> &ColorSpec {
157            match severity {
158                Severity::Bug => &self.header_bug,
159                Severity::Error => &self.header_error,
160                Severity::Warning => &self.header_warning,
161                Severity::Note => &self.header_note,
162                Severity::Help => &self.header_help,
163            }
164        }
165
166        #[must_use]
167        pub fn header_message(&self) -> &ColorSpec {
168            &self.header_message
169        }
170
171        #[must_use]
172        pub fn line_number(&self) -> &ColorSpec {
173            &self.line_number
174        }
175
176        #[must_use]
177        pub fn note_bullet(&self) -> &ColorSpec {
178            &self.note_bullet
179        }
180
181        #[must_use]
182        pub fn source_border(&self) -> &ColorSpec {
183            &self.source_border
184        }
185
186        /// The style used to mark a primary or secondary label at a given severity.
187        #[must_use]
188        pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec {
189            match (label_style, severity) {
190                (LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug,
191                (LabelStyle::Primary, Severity::Error) => &self.primary_label_error,
192                (LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning,
193                (LabelStyle::Primary, Severity::Note) => &self.primary_label_note,
194                (LabelStyle::Primary, Severity::Help) => &self.primary_label_help,
195                (LabelStyle::Secondary, _) => &self.secondary_label,
196            }
197        }
198    }
199
200    impl Styles {
201        #[must_use]
202        pub fn no_color() -> Styles {
203            Styles {
204                header_bug: ColorSpec::new(),
205                header_error: ColorSpec::new(),
206                header_warning: ColorSpec::new(),
207                header_note: ColorSpec::new(),
208                header_help: ColorSpec::new(),
209                header_message: ColorSpec::new(),
210
211                primary_label_bug: ColorSpec::new(),
212                primary_label_error: ColorSpec::new(),
213                primary_label_warning: ColorSpec::new(),
214                primary_label_note: ColorSpec::new(),
215                primary_label_help: ColorSpec::new(),
216                secondary_label: ColorSpec::new(),
217
218                line_number: ColorSpec::new(),
219                source_border: ColorSpec::new(),
220                note_bullet: ColorSpec::new(),
221            }
222        }
223    }
224
225    impl Default for Styles {
226        fn default() -> Styles {
227            // Default style
228            let header = ColorSpec::new().set_bold(true).set_intense(true).clone();
229
230            Styles {
231                header_bug: header.clone().set_fg(Some(Color::Red)).clone(),
232                header_error: header.clone().set_fg(Some(Color::Red)).clone(),
233                header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(),
234                header_note: header.clone().set_fg(Some(Color::Green)).clone(),
235                header_help: header.clone().set_fg(Some(Color::Cyan)).clone(),
236                header_message: header,
237
238                primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
239                primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
240                primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(),
241                primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(),
242                primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
243                secondary_label: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
244
245                line_number: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
246                source_border: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
247                note_bullet: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
248            }
249        }
250    }
251
252    /// A [`WriteStyle`](crate::term::renderer::WriteStyle) implementation that applies custom [`Styles`]
253    /// using termcolor.
254    ///
255    /// Use this to render diagnostics with custom colors
256    pub struct StylesWriter<'a, W> {
257        writer: W,
258        style: &'a Styles,
259    }
260
261    impl<'a, W> StylesWriter<'a, W> {
262        /// Creates a new `StylesWriter` with the given writer and styles.
263        pub fn new(writer: W, style: &'a Styles) -> Self {
264            Self { writer, style }
265        }
266    }
267
268    // Always true here #[cfg(feature = "std")]
269    impl<'a, W: WriteColor> std::io::Write for StylesWriter<'a, W> {
270        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
271            self.writer.write(buf)
272        }
273
274        fn flush(&mut self) -> std::io::Result<()> {
275            self.writer.flush()
276        }
277    }
278
279    impl<W: WriteColor> WriteStyle for StylesWriter<'_, W> {
280        fn set_header(&mut self, severity: Severity) -> renderer::GeneralWriteResult {
281            self.writer.set_color(self.style.header(severity))
282        }
283
284        fn set_header_message(&mut self) -> renderer::GeneralWriteResult {
285            self.writer.set_color(&self.style.header_message)
286        }
287
288        fn set_line_number(&mut self) -> renderer::GeneralWriteResult {
289            self.writer.set_color(&self.style.line_number)
290        }
291
292        fn set_note_bullet(&mut self) -> renderer::GeneralWriteResult {
293            self.writer.set_color(&self.style.note_bullet)
294        }
295
296        fn set_source_border(&mut self) -> renderer::GeneralWriteResult {
297            self.writer.set_color(&self.style.source_border)
298        }
299
300        fn set_label(
301            &mut self,
302            severity: Severity,
303            label_style: LabelStyle,
304        ) -> renderer::GeneralWriteResult {
305            let spec = self.style.label(severity, label_style);
306            self.writer.set_color(spec)
307        }
308
309        fn reset(&mut self) -> renderer::GeneralWriteResult {
310            self.writer.reset()
311        }
312    }
313}
314
315#[cfg(feature = "termcolor")]
316impl<T> super::renderer::WriteStyle for T
317where
318    T: termcolor::WriteColor + ?Sized,
319{
320    fn set_header(
321        &mut self,
322        severity: crate::diagnostic::Severity,
323    ) -> super::renderer::GeneralWriteResult {
324        self.set_color(styles::Styles::default().header(severity))
325    }
326
327    fn set_header_message(&mut self) -> super::renderer::GeneralWriteResult {
328        self.set_color(&styles::Styles::default().header_message)
329    }
330
331    fn set_line_number(&mut self) -> super::renderer::GeneralWriteResult {
332        self.set_color(&styles::Styles::default().line_number)
333    }
334
335    fn set_note_bullet(&mut self) -> super::renderer::GeneralWriteResult {
336        self.set_color(&styles::Styles::default().note_bullet)
337    }
338
339    fn set_source_border(&mut self) -> super::renderer::GeneralWriteResult {
340        self.set_color(&styles::Styles::default().source_border)
341    }
342
343    fn set_label(
344        &mut self,
345        severity: crate::diagnostic::Severity,
346        label_style: crate::diagnostic::LabelStyle,
347    ) -> super::renderer::GeneralWriteResult {
348        let styles = styles::Styles::default();
349        let spec = styles.label(severity, label_style);
350        self.set_color(spec)
351    }
352
353    fn reset(&mut self) -> super::renderer::GeneralWriteResult {
354        self.reset()
355    }
356}
357
358/// Characters to use when rendering the diagnostic.
359///
360/// By using [`Chars::ascii()`] you can switch to an ASCII-only format suitable
361/// for rendering on terminals that do not support box drawing characters.
362#[derive(Clone, Debug)]
363pub struct Chars {
364    /// The characters to use for the top-left border of the snippet.
365    /// Defaults to: `"┌─"` or `"-->"` with [`Chars::ascii()`].
366    pub snippet_start: String,
367    /// The character to use for the left border of the source.
368    /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
369    pub source_border_left: char,
370    /// The character to use for the left border break of the source.
371    /// Defaults to: `'·'` or `'.'` with [`Chars::ascii()`].
372    pub source_border_left_break: char,
373
374    /// The character to use for the note bullet.
375    /// Defaults to: `'='`.
376    pub note_bullet: char,
377
378    /// The character to use for marking a single-line primary label.
379    /// Defaults to: `'^'`.
380    pub single_primary_caret: char,
381    /// The character to use for marking a single-line secondary label.
382    /// Defaults to: `'-'`.
383    pub single_secondary_caret: char,
384
385    /// The character to use for marking the start of a multi-line primary label.
386    /// Defaults to: `'^'`.
387    pub multi_primary_caret_start: char,
388    /// The character to use for marking the end of a multi-line primary label.
389    /// Defaults to: `'^'`.
390    pub multi_primary_caret_end: char,
391    /// The character to use for marking the start of a multi-line secondary label.
392    /// Defaults to: `'\''`.
393    pub multi_secondary_caret_start: char,
394    /// The character to use for marking the end of a multi-line secondary label.
395    /// Defaults to: `'\''`.
396    pub multi_secondary_caret_end: char,
397    /// The character to use for the top-left corner of a multi-line label.
398    /// Defaults to: `'╭'` or `'/'` with [`Chars::ascii()`].
399    pub multi_top_left: char,
400    /// The character to use for the top of a multi-line label.
401    /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`].
402    pub multi_top: char,
403    /// The character to use for the bottom-left corner of a multi-line label.
404    /// Defaults to: `'╰'` or `'\'` with [`Chars::ascii()`].
405    pub multi_bottom_left: char,
406    /// The character to use when marking the bottom of a multi-line label.
407    /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`].
408    pub multi_bottom: char,
409    /// The character to use for the left of a multi-line label.
410    /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
411    pub multi_left: char,
412
413    /// The character to use for the left of a pointer underneath a caret.
414    /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`].
415    pub pointer_left: char,
416}
417
418impl Default for Chars {
419    fn default() -> Chars {
420        Chars::box_drawing()
421    }
422}
423
424impl Chars {
425    /// A character set that uses Unicode box drawing characters.
426    #[must_use]
427    pub fn box_drawing() -> Chars {
428        Chars {
429            snippet_start: "┌─".into(),
430            source_border_left: '│',
431            source_border_left_break: '·',
432
433            note_bullet: '=',
434
435            single_primary_caret: '^',
436            single_secondary_caret: '-',
437
438            multi_primary_caret_start: '^',
439            multi_primary_caret_end: '^',
440            multi_secondary_caret_start: '\'',
441            multi_secondary_caret_end: '\'',
442            multi_top_left: '╭',
443            multi_top: '─',
444            multi_bottom_left: '╰',
445            multi_bottom: '─',
446            multi_left: '│',
447
448            pointer_left: '│',
449        }
450    }
451
452    /// A character set that only uses ASCII characters.
453    ///
454    /// This is useful if your terminal's font does not support box drawing
455    /// characters well and results in output that looks similar to rustc's
456    /// diagnostic output.
457    #[must_use]
458    pub fn ascii() -> Chars {
459        Chars {
460            snippet_start: "-->".into(),
461            source_border_left: '|',
462            source_border_left_break: '.',
463
464            note_bullet: '=',
465
466            single_primary_caret: '^',
467            single_secondary_caret: '-',
468
469            multi_primary_caret_start: '^',
470            multi_primary_caret_end: '^',
471            multi_secondary_caret_start: '\'',
472            multi_secondary_caret_end: '\'',
473            multi_top_left: '/',
474            multi_top: '-',
475            multi_bottom_left: '\\',
476            multi_bottom: '-',
477            multi_left: '|',
478
479            pointer_left: '|',
480        }
481    }
482}