dada_ir_ast/diagnostic/
render.rs1use crate::{diagnostic::Diagnostic, span::AbsoluteSpan};
2use annotate_snippets::{Message, Renderer, Snippet};
3use dada_util::arena::Arena;
4
5use super::RenderOptions;
6
7pub(super) fn render(db: &dyn crate::Db, opts: &RenderOptions, diagnostic: &Diagnostic) -> String {
8 let arena = Arena::new();
9 let message = to_message(db, diagnostic, &arena);
10 renderer(opts).render(message).to_string()
11}
12
13fn renderer(opts: &RenderOptions) -> Renderer {
14 if opts.no_color {
15 Renderer::plain()
16 } else {
17 Renderer::styled()
18 }
19}
20
21fn to_level(level: crate::diagnostic::Level) -> annotate_snippets::Level {
22 match level {
23 crate::diagnostic::Level::Note => annotate_snippets::Level::Note,
24 crate::diagnostic::Level::Warning => annotate_snippets::Level::Warning,
25 crate::diagnostic::Level::Info => annotate_snippets::Level::Info,
26 crate::diagnostic::Level::Help => annotate_snippets::Level::Help,
27 crate::diagnostic::Level::Error => annotate_snippets::Level::Error,
28 }
29}
30
31fn to_message<'a>(
32 db: &'a dyn crate::Db,
33 diagnostic: &'a Diagnostic,
34 arena: &'a Arena,
35) -> Message<'a> {
36 to_level(diagnostic.level)
37 .title(&diagnostic.message)
38 .snippet(to_snippet(db, diagnostic, arena))
39 .footers(diagnostic.children.iter().map(|d| to_message(db, d, arena)))
40}
41
42fn to_snippet<'a>(
43 db: &'a dyn crate::Db,
44 diagnostic: &'a Diagnostic,
45 arena: &'a Arena,
46) -> Snippet<'a> {
47 let source_file = diagnostic.span.source_file;
48
49 let default_label = if !diagnostic.labels.is_empty() {
50 None
51 } else {
52 Some(
53 to_level(diagnostic.level)
54 .span(to_span(diagnostic.span))
55 .label("here"),
56 )
57 };
58
59 let url = source_file.url(db);
60 let origin = arena.insert(db.url_display(url));
61
62 Snippet::source(source_file.contents_if_ok(db))
63 .line_start(1)
64 .origin(origin)
65 .fold(true)
66 .annotations(
67 diagnostic
68 .labels
69 .iter()
70 .map(|label| {
71 assert!(label.span.source_file == source_file);
72 to_level(label.level)
73 .span(to_span(label.span))
74 .label(&label.message)
75 })
76 .chain(default_label),
77 )
78}
79
80fn to_span(span: AbsoluteSpan) -> std::ops::Range<usize> {
81 span.start.as_usize()..span.end.as_usize()
82}