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}