-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
diagnostic_printer.rs
126 lines (112 loc) · 3.82 KB
/
diagnostic_printer.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
use std::fmt::Write;
use colored::Colorize;
use common::Diagnostic;
use common::Location;
use common::SourceLocationKey;
use common::TextSource;
use crate::SourcePrinter;
use crate::Style;
use crate::Styles;
pub struct DiagnosticPrinter<T: Sources> {
sources: T,
}
impl<TSources: Sources> DiagnosticPrinter<TSources> {
pub fn new(sources: TSources) -> Self {
Self { sources }
}
pub fn diagnostics_to_string(&self, diagnostics: &[Diagnostic]) -> String {
diagnostics
.iter()
.map(|d| self.diagnostic_to_string(d))
.collect::<Vec<String>>()
.join("\n")
}
pub fn diagnostic_to_string(&self, diagnostic: &Diagnostic) -> String {
let mut printed = String::new();
self.write_diagnostic(&mut printed, diagnostic).unwrap();
printed
}
pub fn write_diagnostic<W: Write>(
&self,
writer: &mut W,
diagnostic: &Diagnostic,
) -> std::fmt::Result {
let (message, text_color): (String, Style) = match diagnostic.severity() {
common::DiagnosticSeverity::ERROR => {
(format!("✖︎ {}", diagnostic.message()), Styles::red)
}
common::DiagnosticSeverity::WARNING => {
(format!("︎⚠ {}", diagnostic.message()), Styles::yellow)
}
common::DiagnosticSeverity::INFORMATION | common::DiagnosticSeverity::HINT => {
(format!("ℹ {}", diagnostic.message()), Styles::blue)
}
_ => (format!("ℹ {}", diagnostic.message()), Styles::blue),
};
writeln!(writer, "{}\n", text_color(message))?;
self.write_source(writer, diagnostic.location(), text_color)?;
for related_information in diagnostic.related_information() {
writeln!(
writer,
"\n{}\n",
text_color(format!(" ℹ︎ {}", related_information.message)),
)?;
self.write_source(writer, related_information.location, text_color)?;
}
Ok(())
}
/// Writes the file path and slice of the source code for the given location.
fn write_source<W: Write>(
&self,
writer: &mut W,
location: Location,
highlight_color: Style,
) -> std::fmt::Result {
let source_printer = SourcePrinter;
if let Some(source) = self.sources.get(location.source_location()) {
let range = source.to_span_range(location.span());
writeln!(
writer,
" {}{}",
normalize_path(location.source_location().path()).underline(),
format!(":{}:{}", range.start.line + 1, range.start.character + 1).dimmed()
)?;
source_printer.write_span_with_highlight_style(
writer,
location.span(),
&source.text,
source.line_index,
highlight_color,
)?;
} else {
writeln!(
writer,
"{}: <missing source>",
normalize_path(location.source_location().path())
)?;
}
Ok(())
}
}
pub trait Sources {
fn get(&self, source_location: SourceLocationKey) -> Option<TextSource>;
}
impl<F> Sources for F
where
F: Fn(SourceLocationKey) -> Option<TextSource>,
{
fn get(&self, source_location: SourceLocationKey) -> Option<TextSource> {
self(source_location)
}
}
/// Normalize Windows paths to Unix style. This is important for stable test
/// output across Mac/Windows/Linux.
fn normalize_path(path: &str) -> String {
path.replace("\\", "/")
}