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