Fork of daniellemaywood.uk/gleam — Wasm codegen work
3.7 kB
118 lines
1use camino::Utf8PathBuf;
2
3pub use codespan_reporting::diagnostic::{LabelStyle, Severity};
4use codespan_reporting::{diagnostic::Label as CodespanLabel, files::SimpleFile};
5use ecow::EcoString;
6use termcolor::Buffer;
7
8use crate::ast::SrcSpan;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Level {
12 Error,
13 Warning,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Label {
18 pub text: Option<String>,
19 pub span: SrcSpan,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct Location {
24 pub src: EcoString,
25 pub path: Utf8PathBuf,
26 pub label: Label,
27 pub extra_labels: Vec<Label>,
28}
29
30impl Location {
31 fn labels(&self) -> impl Iterator<Item = &Label> {
32 std::iter::once(&self.label).chain(self.extra_labels.iter())
33 }
34}
35
36// TODO: split this into locationed diagnostics and locationless diagnostics
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Diagnostic {
39 pub title: String,
40 pub text: String,
41 pub level: Level,
42 pub location: Option<Location>,
43 pub hint: Option<String>,
44}
45
46impl Diagnostic {
47 pub fn write(&self, buffer: &mut Buffer) {
48 use std::io::Write;
49 match &self.location {
50 Some(location) => self.write_span(location, buffer),
51 None => self.write_title(buffer),
52 };
53
54 if !self.text.is_empty() {
55 writeln!(buffer, "{}", self.text).expect("write text");
56 }
57
58 if let Some(hint) = &self.hint {
59 writeln!(buffer, "Hint: {hint}").expect("write hint");
60 }
61 }
62
63 fn write_span(&self, location: &Location, buffer: &mut Buffer) {
64 let file = SimpleFile::new(location.path.to_string(), location.src.as_str());
65 let labels = location
66 .labels()
67 .map(|l| {
68 let label = CodespanLabel::new(
69 LabelStyle::Primary,
70 (),
71 (l.span.start as usize)..(l.span.end as usize),
72 );
73 match &l.text {
74 None => label,
75 Some(text) => label.with_message(text.clone()),
76 }
77 })
78 .collect();
79
80 let severity = match self.level {
81 Level::Error => Severity::Error,
82 Level::Warning => Severity::Warning,
83 };
84
85 let diagnostic = codespan_reporting::diagnostic::Diagnostic::new(severity)
86 .with_message(&self.title)
87 .with_labels(labels);
88 let config = codespan_reporting::term::Config::default();
89 codespan_reporting::term::emit(buffer, &config, &file, &diagnostic)
90 .expect("write_diagnostic");
91 }
92
93 fn write_title(&self, buffer: &mut Buffer) {
94 use std::io::Write;
95 use termcolor::{Color, ColorSpec, WriteColor};
96 let (kind, colour) = match self.level {
97 Level::Error => ("error", Color::Red),
98 Level::Warning => ("warning", Color::Yellow),
99 };
100 buffer
101 .set_color(ColorSpec::new().set_bold(true).set_fg(Some(colour)))
102 .expect("write_title_color1");
103 write!(buffer, "{kind}").expect("write_title_kind");
104 buffer
105 .set_color(ColorSpec::new().set_bold(true))
106 .expect("write_title_color2");
107 write!(buffer, ": {}\n\n", self.title).expect("write_title_title");
108 buffer
109 .set_color(&ColorSpec::new())
110 .expect("write_title_reset");
111 }
112
113 pub fn pretty_string(&self) -> String {
114 let mut nocolor = Buffer::no_color();
115 self.write(&mut nocolor);
116 String::from_utf8(nocolor.into_inner()).expect("Error printing produced invalid utf8")
117 }
118}