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