~/bend-docscommunity

bolt/lsp/report.bend source

bolt/lsp/report.bend on the hub · documented module

# lsp/report: the checker's text, as diagnostics. bend prints an error as#   Error:            (or `Error: a one-line message`)#   - expected : ..   the message lines, then `Context:` and its lines#   Location: name    the def; `../mod.name` when it sits in an import#   4>|   x           the source excerpt; `>|` marks the line, 1-based# There are no columns, so a diagnostic covers its whole line. An error in an# import cannot be placed in this file: it lands on line 0, naming its home.import Base# line is 0-based, as LSP countstype Diag is Data:  Diag{line: U32, msg: String}# a line of the checker's report, by what it starts withtype Line is Data:  KError{rest: String}  KLocation{place: String}  KOther{text: String}# msg and out are reversedtype St is Data:  St{seen: Bool, located: Bool, msg: List<&2, String>, line: U32, place: String, out: List<&2, Diag>}# a report line's kinddef classify(+s: String) -> Line:  Bool.pick(Line, String.starts_with(s, "Error:"), KError{String.drop(s, 6n)},    Bool.pick(Line, String.starts_with(s, "Location:"), KLocation{String.drop(s, 9n)}, KOther{s}))# not whitespace?def not_space(c: Char) -> Bool:  Bool.not(Char.is_space(c))# every whitespace char droppeddef strip(s: String) -> String:  String.from_list(List.filter(~Char, ~not_space, String.to_list(s)))# drops leading spacesdef lstrip.go(cs: List<&2, Char>) -> List<&2, Char>:  match cs:    case Con{' ', t}:      lstrip.go(t)    case other:      other# leading whitespace droppeddef lstrip(s: String) -> String:  String.from_list(lstrip.go(String.to_list(s)))# `12>|  code` marks line 12: the text before the first '>' is the number, and# the text after it starts with '|' (a '>' later in the code changes nothing)def marker.of(parts: List<&2, String>) -> Maybe<&2, U32>:  match parts:    case Con{a, Con{b, rest}}:      Bool.pick(Maybe<&2, U32>, String.starts_with(b, "|"), U32.read(strip(a)), None{})    case other:      None{}# the line number a source line of the report carries (`9>|`), when it doesdef marker(s: String) -> Maybe<&2, U32>:  marker.of(String.split(s, '>'))# a message line, without its `- `def clean(+s: String) -> String:  Bool.pick(String, String.starts_with(s, "- "), String.drop(s, 2n), s)# a non-empty line onto the messagedef push(+s: String, +msg: List<&2, String>) -> List<&2, String>:  Bool.pick(List<&2, String>, String.is_empty(s), msg, s <> msg)# the message lines (reversed) as text, headed by where it is when that is# another filedef message(+place: String, msg: List<&2, String>) -> String:  +body = String.join(List.reverse(&2, String, msg), "\n")  Bool.pick(String, String.contains(place, "/"), "in " ++ place ++ ":\n" ++ body, body)# the 0-based line for a diagnostic: line 0 for an error in an importdef line_of(place: String, +line: U32) -> U32:  Bool.pick(U32, Bool.or(String.contains(place, "/"), U32.is_eq(line, 0)), 0, (line - 1 : U32))# the error so far becomes a diagnostic; "no main to run" is a library, not an errordef flush(seen: Bool, msg: List<&2, String>, +line: U32, +place: String, +out: List<&2, Diag>) -> List<&2, Diag>:  +text = message(place, msg)  Bool.pick(List<&2, Diag>, Bool.and(seen, Bool.not(String.eq(text, "no main to run"))),    Diag{line_of(place, line), text} <> out, out)def step.go(k: Line, located: Bool, seen: Bool, msg: List<&2, String>, +line: U32, place: String,  out: List<&2, Diag>) -> St:  match k located seen:    case KError{rest} l s:      St{True{}, False{}, push(lstrip(rest), []), 0, "", flush(s, msg, line, place, out)}    case KLocation{p} l s:      St{s, True{}, msg, line, lstrip(p), out}    case KOther{text} False{} True{}:      St{True{}, False{}, push(clean(text), msg), line, place, out}    case KOther{text} True{} True{}:      St{True{}, True{}, msg, Maybe.default(&2, U32, marker(text), line), place, out}    case KOther{text} l False{}:      St{False{}, l, msg, line, place, out}# one report linedef step(s: String, st: St) -> St:  St{seen, located, msg, line, place, out} = st  step.go(classify(s), located, seen, msg, line, place, out)# every report line, in orderdef run(lines: List<&2, String>, st: St) -> St:  match lines:    case Nil{}:      st    case Con{l, t}:      run(t, step(l, st))# the diagnostics once the lines are over, a pending one flusheddef finish(st: St) -> List<&2, Diag>:  St{seen, located, msg, line, place, out} = st  List.reverse(&2, Diag, flush(seen, msg, line, place, out))# the checker's text as diagnosticsdef parse(text: String) -> List<&2, Diag>:  finish(run(String.lines(text), St{False{}, False{}, [], 0, "", []}))