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