///|
fn json_escape(value : String) -> String {
let builder = StringBuilder()
for character in value {
match character {
'"' => builder.write_string("\\\"")
'\\' => builder.write_string("\\\\")
'\n' => builder.write_string("\\n")
'\r' => builder.write_string("\\r")
'\t' => builder.write_string("\\t")
_ => builder.write_char(character)
}
}
builder.to_string()
}
///|
fn diagnostic_file(diagnostic : Diagnostic, fallback : String) -> String {
if diagnostic.file.is_empty() {
fallback
} else {
diagnostic.file
}
}
///|
/// Render diagnostics in a compiler-friendly `file:line:column` format.
pub fn render_text(
diagnostics : Array[Diagnostic],
source : @source.SourceFile,
) -> String {
let builder = StringBuilder()
for diagnostic in diagnostics {
let (line, column) = diagnostic.at_line_col(source)
builder.write_string(diagnostic_file(diagnostic, source.name()))
builder.write_char(':')
builder.write_object(line + 1)
builder.write_char(':')
builder.write_object(column + 1)
builder.write_string(": ")
builder.write_string(diagnostic.severity.label())
builder.write_char('[')
builder.write_string(diagnostic.code)
builder.write_string("]: ")
builder.write_string(diagnostic.message)
builder.write_char('\n')
for note in diagnostic.notes {
builder.write_string(" note: ")
builder.write_string(note)
builder.write_char('\n')
}
}
builder.to_string()
}
///|
/// Render one JSON object per diagnostic for CI and editor integrations.
pub fn render_json(diagnostics : Array[Diagnostic]) -> String {
let builder = StringBuilder()
builder.write_char('[')
for index, diagnostic in diagnostics {
if index > 0 {
builder.write_char(',')
}
builder.write_string("{\"code\":\"")
builder.write_string(json_escape(diagnostic.code))
builder.write_string("\",\"severity\":\"")
builder.write_string(diagnostic.severity.label())
builder.write_string("\",\"message\":\"")
builder.write_string(json_escape(diagnostic.message))
builder.write_string("\",\"file\":\"")
builder.write_string(json_escape(diagnostic.file))
builder.write_string("\",\"start\":")
builder.write_object(diagnostic.span.start)
builder.write_string(",\"end\":")
builder.write_object(diagnostic.span.end)
builder.write_string("}")
}
builder.write_string("]\n")
builder.to_string()
}
///|
/// Render a semantic dependency index as a Graphviz DOT document.
pub fn render_graph(index : @semantic.SemanticIndex) -> String {
let builder = StringBuilder()
builder.write_string("digraph rtl {\n")
builder.write_string(" rankdir=LR;\n")
if index.modules.length() > 0 {
for mod in index.modules {
builder.write_string(" \"")
builder.write_string(mod.name)
builder.write_string("\";\n")
}
}
for edge in index.graph.edges() {
let (parent, child) = edge
builder.write_string(" \"")
builder.write_string(parent)
builder.write_string("\" -> \"")
builder.write_string(child)
builder.write_string("\";\n")
}
builder.write_string("}\n")
builder.to_string()
}