|
1 | 1 | //! 'components.rs' defines all components with style `DiagnosticStyle` that builtin in compiler_base_error. |
| 2 | +use std::sync::Arc; |
| 3 | + |
2 | 4 | use super::{style::DiagnosticStyle, Component}; |
3 | 5 | use crate::errors::ComponentFormatError; |
4 | | -use rustc_errors::styled_buffer::StyledBuffer; |
| 6 | +use compiler_base_span::{span_to_filename_string, SourceMap, Span}; |
| 7 | +use rustc_errors::styled_buffer::{StyledBuffer, StyledString}; |
5 | 8 |
|
6 | 9 | /// `Label` can be considered as a component of diagnostic to display a short label message in `Diagnositc`. |
7 | 10 | /// `Label` provides "error", "warning", "note" and "Help" four kinds of labels. |
@@ -54,3 +57,333 @@ impl Component<DiagnosticStyle> for Label { |
54 | 57 | } |
55 | 58 | } |
56 | 59 | } |
| 60 | + |
| 61 | +// Make `StyledString` into a component of diagnostic to display a string with style. |
| 62 | +// For more information about `StyledString`, see doc in `/compiler_base/3rdparty/rustc_errors/src/styled_buffer.rs`. |
| 63 | +impl Component<DiagnosticStyle> for StyledString<DiagnosticStyle> { |
| 64 | + #[inline] |
| 65 | + fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, _: &mut Vec<ComponentFormatError>) { |
| 66 | + sb.appendl(&self.text, self.style); |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +/// `IndentWithPrefix` is a component of diagnostic to display an indent with prefix. |
| 71 | +/// An indent is a whitespace. |
| 72 | +/// ```ignore |
| 73 | +/// "| " is three indent with prefix "|". |
| 74 | +/// ``` |
| 75 | +pub struct IndentWithPrefix { |
| 76 | + indent: usize, |
| 77 | + prefix: StyledString<DiagnosticStyle>, |
| 78 | +} |
| 79 | + |
| 80 | +const DEFAULT_INDENT_PREFIX_LABEL: &str = "|"; |
| 81 | + |
| 82 | +impl IndentWithPrefix { |
| 83 | + /// Constructs a new `IndentWithPrefix` by default label with 0 indent. |
| 84 | + /// |
| 85 | + /// # Examples |
| 86 | + /// |
| 87 | + /// ```rust |
| 88 | + /// # use compiler_base_error::Component; |
| 89 | + /// # use compiler_base_error::components::IndentWithPrefix; |
| 90 | + /// # use compiler_base_error::DiagnosticStyle; |
| 91 | + /// # use rustc_errors::styled_buffer::StyledBuffer; |
| 92 | + /// |
| 93 | + /// let mut sb = StyledBuffer::<DiagnosticStyle>::new(); |
| 94 | + /// let mut errs = vec![]; |
| 95 | + /// |
| 96 | + /// // If you want to render default text: "|" |
| 97 | + /// let indent = IndentWithPrefix::default(); |
| 98 | + /// indent.format(&mut sb, &mut errs); |
| 99 | + /// ``` |
| 100 | + #[inline] |
| 101 | + pub fn default() -> Self { |
| 102 | + Self { |
| 103 | + indent: 0, |
| 104 | + prefix: StyledString::<DiagnosticStyle> { |
| 105 | + text: DEFAULT_INDENT_PREFIX_LABEL.to_string(), |
| 106 | + style: None, |
| 107 | + }, |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + /// Constructs a new `IndentWithPrefix` by default label with custom indents. |
| 112 | + /// |
| 113 | + /// # Examples |
| 114 | + /// |
| 115 | + /// ```rust |
| 116 | + /// # use compiler_base_error::Component; |
| 117 | + /// # use compiler_base_error::components::IndentWithPrefix; |
| 118 | + /// # use compiler_base_error::DiagnosticStyle; |
| 119 | + /// # use rustc_errors::styled_buffer::StyledBuffer; |
| 120 | + /// |
| 121 | + /// let mut sb = StyledBuffer::<DiagnosticStyle>::new(); |
| 122 | + /// let mut errs = vec![]; |
| 123 | + /// |
| 124 | + /// // If you want to add 3 indents and render text: " |" |
| 125 | + /// let indent = IndentWithPrefix::new_with_default_label(3, None); |
| 126 | + /// indent.format(&mut sb, &mut errs); |
| 127 | + /// ``` |
| 128 | + #[inline] |
| 129 | + pub fn new_with_default_label(indent: usize, style: Option<DiagnosticStyle>) -> Self { |
| 130 | + Self { |
| 131 | + indent, |
| 132 | + prefix: StyledString::<DiagnosticStyle>::new( |
| 133 | + DEFAULT_INDENT_PREFIX_LABEL.to_string(), |
| 134 | + style, |
| 135 | + ), |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + /// Constructs a new `IndentWithPrefix` by custom label with custom indents. |
| 140 | + /// |
| 141 | + /// # Examples |
| 142 | + /// |
| 143 | + /// ```rust |
| 144 | + /// # use compiler_base_error::Component; |
| 145 | + /// # use compiler_base_error::components::IndentWithPrefix; |
| 146 | + /// # use compiler_base_error::DiagnosticStyle; |
| 147 | + /// # use rustc_errors::styled_buffer::StyledBuffer; |
| 148 | + /// |
| 149 | + /// let mut sb = StyledBuffer::<DiagnosticStyle>::new(); |
| 150 | + /// let mut errs = vec![]; |
| 151 | + /// // If you want to add 3 indents and rendering text: " ^" |
| 152 | + /// let indent = IndentWithPrefix::new("^".to_string(), 3, None); |
| 153 | + /// indent.format(&mut sb, &mut errs); |
| 154 | + /// ``` |
| 155 | + #[inline] |
| 156 | + pub fn new(prefix: String, indent: usize, prefix_style: Option<DiagnosticStyle>) -> Self { |
| 157 | + Self { |
| 158 | + indent, |
| 159 | + prefix: StyledString::<DiagnosticStyle>::new(prefix, prefix_style), |
| 160 | + } |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +impl Component<DiagnosticStyle> for IndentWithPrefix { |
| 165 | + #[inline] |
| 166 | + fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, errs: &mut Vec<ComponentFormatError>) { |
| 167 | + let indent = self.indent; |
| 168 | + sb.appendl(&format!("{:>indent$}", ""), None); |
| 169 | + self.prefix.format(sb, errs) |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +/// `UnderLine` is a component of diagnostic to display an underline. |
| 174 | +/// |
| 175 | +/// ```ignore |
| 176 | +/// int test = 0; |
| 177 | +/// ^^^^ This is an underline under variable `test` |
| 178 | +/// ``` |
| 179 | +pub struct UnderLine { |
| 180 | + start: usize, |
| 181 | + end: usize, |
| 182 | + symbol: StyledString<DiagnosticStyle>, |
| 183 | +} |
| 184 | + |
| 185 | +const DEFAULT_UNDERLINE_LABEL: &str = "^"; |
| 186 | +impl UnderLine { |
| 187 | + /// Constructs a new `UnderLine` with a default label. |
| 188 | + /// |
| 189 | + /// # Examples |
| 190 | + /// |
| 191 | + /// ```rust |
| 192 | + /// # use compiler_base_error::Component; |
| 193 | + /// # use compiler_base_error::components::UnderLine; |
| 194 | + /// # use compiler_base_error::DiagnosticStyle; |
| 195 | + /// # use rustc_errors::styled_buffer::StyledBuffer; |
| 196 | + /// |
| 197 | + /// let mut sb = StyledBuffer::<DiagnosticStyle>::new(); |
| 198 | + /// let mut errs = vec![]; |
| 199 | + /// |
| 200 | + /// // rendering text: "^^^^^^^^^^" |
| 201 | + /// let ul = UnderLine::new_with_default_label(0, 10, None); |
| 202 | + /// ul.format(&mut sb, &mut errs); |
| 203 | + /// |
| 204 | + /// // rendering text: "^^^^^^^^^^" in `DiagnosticStyle::NeedFix`. |
| 205 | + /// let ul_need_fix = UnderLine::new_with_default_label(0, 10, Some(DiagnosticStyle::NeedFix)); |
| 206 | + /// ul_need_fix.format(&mut sb, &mut errs); |
| 207 | + /// ``` |
| 208 | + #[inline] |
| 209 | + pub fn new_with_default_label( |
| 210 | + start: usize, |
| 211 | + end: usize, |
| 212 | + style: Option<DiagnosticStyle>, |
| 213 | + ) -> Self { |
| 214 | + Self { |
| 215 | + start, |
| 216 | + end, |
| 217 | + symbol: StyledString::<DiagnosticStyle>::new( |
| 218 | + DEFAULT_UNDERLINE_LABEL.to_string(), |
| 219 | + style, |
| 220 | + ), |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + /// Constructs a new `UnderLine` with a custom label. |
| 225 | + /// |
| 226 | + /// # Examples |
| 227 | + /// |
| 228 | + /// ```rust |
| 229 | + /// # use compiler_base_error::Component; |
| 230 | + /// # use compiler_base_error::components::UnderLine; |
| 231 | + /// # use compiler_base_error::DiagnosticStyle; |
| 232 | + /// # use rustc_errors::styled_buffer::StyledBuffer; |
| 233 | + /// |
| 234 | + /// let mut sb = StyledBuffer::<DiagnosticStyle>::new(); |
| 235 | + /// let mut errs = vec![]; |
| 236 | + /// |
| 237 | + /// // rendering text: "__________" |
| 238 | + /// let ul = UnderLine::new(0, 10, "_".to_string(), None); |
| 239 | + /// ul.format(&mut sb, &mut errs); |
| 240 | + /// |
| 241 | + /// // rendering text: "~~" in `DiagnosticStyle::NeedFix`. |
| 242 | + /// let ul_need_fix = UnderLine::new(0, 2, "~".to_string(), Some(DiagnosticStyle::NeedFix)); |
| 243 | + /// ul_need_fix.format(&mut sb, &mut errs); |
| 244 | + /// ``` |
| 245 | + #[inline] |
| 246 | + pub fn new(start: usize, end: usize, label: String, style: Option<DiagnosticStyle>) -> Self { |
| 247 | + Self { |
| 248 | + start, |
| 249 | + end, |
| 250 | + symbol: StyledString::<DiagnosticStyle>::new(label.to_string(), style), |
| 251 | + } |
| 252 | + } |
| 253 | +} |
| 254 | + |
| 255 | +impl Component<DiagnosticStyle> for UnderLine { |
| 256 | + fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, errs: &mut Vec<ComponentFormatError>) { |
| 257 | + if self.start < self.end { |
| 258 | + IndentWithPrefix::new("".to_string(), self.start, None).format(sb, errs); |
| 259 | + for _ in self.start..self.end { |
| 260 | + self.symbol.format(sb, errs); |
| 261 | + } |
| 262 | + } else if self.start > self.end { |
| 263 | + errs.push(ComponentFormatError::new( |
| 264 | + "UnderLine", |
| 265 | + "Failed to Format UnderLine in One Line.", |
| 266 | + )) |
| 267 | + } |
| 268 | + } |
| 269 | +} |
| 270 | + |
| 271 | +/// `CodeSnippet` is a component of diagnostic to display code snippets. |
| 272 | +pub struct CodeSnippet { |
| 273 | + code_span: Span, |
| 274 | + source_map: Arc<SourceMap>, |
| 275 | +} |
| 276 | + |
| 277 | +impl CodeSnippet { |
| 278 | + /// # Examples |
| 279 | + /// |
| 280 | + /// If you want to get one line code snippet from 'compiler_base/error/src/diagnostic/test_datas/code_snippet' file |
| 281 | + /// ```ignore |
| 282 | + /// Line 1 Code Snippet. |
| 283 | + /// Line 2 Code Snippet. |
| 284 | + /// ``` |
| 285 | + /// |
| 286 | + /// ```rust |
| 287 | + /// # use compiler_base_error::{ |
| 288 | + /// # Component, |
| 289 | + /// # DiagnosticStyle, |
| 290 | + /// # }; |
| 291 | + /// # use compiler_base_span::{ |
| 292 | + /// # SourceMap, |
| 293 | + /// # FilePathMapping, |
| 294 | + /// # span_to_filename_string |
| 295 | + /// # }; |
| 296 | + /// # use rustc_errors::styled_buffer::StyledBuffer; |
| 297 | + /// # use compiler_base_span::{span::new_byte_pos, SpanData}; |
| 298 | + /// # use compiler_base_error::components::CodeSnippet; |
| 299 | + /// # use std::{path::PathBuf, sync::Arc, fs}; |
| 300 | + /// |
| 301 | + /// let mut sb = StyledBuffer::<DiagnosticStyle>::new(); |
| 302 | + /// let mut errs = vec![]; |
| 303 | + /// |
| 304 | + /// // 1. You shouled load the file and create the `SourceFile` |
| 305 | + /// let filename = fs::canonicalize(&PathBuf::from("./src/diagnostic/test_datas/code_snippet")) |
| 306 | + /// .unwrap() |
| 307 | + /// .display() |
| 308 | + /// .to_string(); |
| 309 | + /// |
| 310 | + /// let src = std::fs::read_to_string(filename.clone()).unwrap(); |
| 311 | + /// let sm = SourceMap::new(FilePathMapping::empty()); |
| 312 | + /// sm.new_source_file(PathBuf::from(filename.clone()).into(), src.to_string()); |
| 313 | + /// |
| 314 | + /// // 2. You should create a code span for the code snippet. |
| 315 | + /// let code_span = SpanData { |
| 316 | + /// lo: new_byte_pos(22), |
| 317 | + /// hi: new_byte_pos(42), |
| 318 | + /// }.span(); |
| 319 | + /// |
| 320 | + /// // 3. You can create the `CodeSnippet` by the `SourceFile`, |
| 321 | + /// // and render text "Line 2 Code Snippet.". |
| 322 | + /// let code_snippet = CodeSnippet::new(code_span, Arc::new(sm)); |
| 323 | + /// code_snippet.format(&mut sb, &mut errs); |
| 324 | + /// ``` |
| 325 | + #[inline] |
| 326 | + pub fn new(code_span: Span, source_map: Arc<SourceMap>) -> Self { |
| 327 | + Self { |
| 328 | + code_span, |
| 329 | + source_map, |
| 330 | + } |
| 331 | + } |
| 332 | +} |
| 333 | + |
| 334 | +const DEFAULT_FILE_PATH_PREFIX: &str = "---> File: "; |
| 335 | + |
| 336 | +impl Component<DiagnosticStyle> for CodeSnippet { |
| 337 | + fn format(&self, sb: &mut StyledBuffer<DiagnosticStyle>, errs: &mut Vec<ComponentFormatError>) { |
| 338 | + sb.pushs(DEFAULT_FILE_PATH_PREFIX, Some(DiagnosticStyle::Url)); |
| 339 | + let file_info = self.source_map.span_to_diagnostic_string(self.code_span); |
| 340 | + sb.appendl(&file_info, Some(DiagnosticStyle::Url)); |
| 341 | + sb.appendl("\n", None); |
| 342 | + match self.source_map.span_to_lines(self.code_span) { |
| 343 | + Ok(affected_lines) => { |
| 344 | + match self |
| 345 | + .source_map |
| 346 | + .source_file_by_filename(&span_to_filename_string( |
| 347 | + &self.code_span, |
| 348 | + &self.source_map, |
| 349 | + )) { |
| 350 | + Some(sf) => { |
| 351 | + for line in affected_lines.lines { |
| 352 | + let line_index = line.line_index.to_string(); |
| 353 | + let indent = line_index.len() + 1; |
| 354 | + IndentWithPrefix::new(line_index, indent, Some(DiagnosticStyle::Url)) |
| 355 | + .format(sb, errs); |
| 356 | + IndentWithPrefix::default().format(sb, errs); |
| 357 | + if let Some(line) = sf.get_line(line.line_index) { |
| 358 | + sb.appendl(&line.to_string(), None); |
| 359 | + } else { |
| 360 | + errs.push(ComponentFormatError::new( |
| 361 | + "CodeSnippet", |
| 362 | + "Failed to Display Code Snippet.", |
| 363 | + )) |
| 364 | + } |
| 365 | + sb.appendl("\n", None); |
| 366 | + IndentWithPrefix::new_with_default_label(indent + 1, None) |
| 367 | + .format(sb, errs); |
| 368 | + UnderLine::new_with_default_label( |
| 369 | + line.start_col.0, |
| 370 | + line.end_col.0, |
| 371 | + Some(DiagnosticStyle::NeedFix), |
| 372 | + ) |
| 373 | + .format(sb, errs); |
| 374 | + sb.appendl("\n", None); |
| 375 | + } |
| 376 | + } |
| 377 | + None => errs.push(ComponentFormatError::new( |
| 378 | + "CodeSnippet", |
| 379 | + "Failed to Load Source File", |
| 380 | + )), |
| 381 | + }; |
| 382 | + } |
| 383 | + Err(_) => errs.push(ComponentFormatError::new( |
| 384 | + "CodeSnippet", |
| 385 | + "Failed to Display Code Snippet Lines", |
| 386 | + )), |
| 387 | + }; |
| 388 | + } |
| 389 | +} |
0 commit comments