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