src/lsp/report.bend
bolt@1.27.0.0 · 170 lines · 22 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import Base
16import bolt@1.27.0.0/src/lazy/lazy.bend as Lazy
17
18
19type Diag is Data:
20 Diag{line: U32, msg: String}
21
22
23type Line is Data:
24 KError{rest: String}
25 KLocation{place: String}
26 KOther{text: String}
27
28
29
30
31type Raw is Data:
32 Raw{place: String, line: U32, body: String}
33
34
35type St is Data:
36 St{seen: Bool, located: Bool, msg: List<&2, String>, line: U32, place: String, out: List<&2, Raw>}
37
38
39def classify(+ss: String) -> Line:
40 Bool.pick(Line, String.starts_with(ss, "Error:"), KError{String.drop(ss, 6n)},
41 Bool.pick(Line, String.starts_with(ss, "Location:"), KLocation{String.drop(ss, 9n)}, KOther{ss}))
42
43
44
45def marker.go(ss: String, +whole: String, +count: Nat) -> Maybe<&2, U32>:
46 match ss:
47 case SNil{}:
48 None{}
49 case SCon{+hh, +tt}:
50 Lazy.stop(Maybe<&2, U32>, Bool.not(Char.is_digit(hh)),
51 Bool.pick(Maybe<&2, U32>, String.starts_with(SCon{hh, tt}, ">|"), U32.read(String.take(whole, count)), None{}),
52 _u => marker.go(tt, whole, 1n+count))
53
54
55
56
57def marker(ss: String) -> Maybe<&2, U32>:
58 +tt = String.trim_start(ss)
59 marker.go(tt, tt, 0n)
60
61
62def clean(+ss: String) -> String:
63 Bool.pick(String, String.starts_with(ss, "- "), String.drop(ss, 2n), ss)
64
65
66def is_nag(+ss: String) -> Bool:
67 String.contains(ss, "is available: run bend update")
68
69
70def push(+ss: String, +msg: List<&2, String>) -> List<&2, String>:
71 Bool.pick(List<&2, String>, Bool.or(String.is_empty(ss), is_nag(ss)), msg, ss <> msg)
72
73
74
75def flush(seen: Bool, msg: List<&2, String>, +line: U32, +place: String, +out: List<&2, Raw>) -> List<&2, Raw>:
76 +body = String.join(List.reverse(&2, String, msg), "\n")
77 Bool.pick(List<&2, Raw>, Bool.and(seen, Bool.not(String.eq(body, "no main to run"))),
78 Raw{place, line, body} <> out, out)
79
80def step.go(
81 kk: Line,
82 located: Bool,
83 seen: Bool,
84 msg: List<&2, String>,
85 +line: U32,
86 place: String,
87 out: List<&2, Raw>
88) -> St:
89 match kk located seen:
90 case KError{rest} l s:
91 St{True{}, False{}, push(String.trim_start(rest), []), 0, "", flush(s, msg, line, place, out)}
92 case KLocation{p} l s:
93 St{s, True{}, msg, line, String.trim_start(p), out}
94 case KOther{text} False{} True{}:
95 St{True{}, False{}, push(clean(text), msg), line, place, out}
96 case KOther{text} True{} True{}:
97 St{True{}, True{}, msg, Maybe.default(&2, U32, marker(text), line), place, out}
98 case KOther{text} l False{}:
99 St{False{}, l, msg, line, place, out}
100
101
102def step(ss: String, st: St) -> St:
103 St{seen, located, msg, line, place, out} = st
104 step.go(classify(ss), located, seen, msg, line, place, out)
105
106
107def run(lines: List<&2, String>, st: St) -> St:
108 match lines:
109 case Nil{}:
110 st
111 case Con{l, t}:
112 run(t, step(l, st))
113
114
115def finish(st: St) -> List<&2, Raw>:
116 St{seen, located, msg, line, place, out} = st
117 List.reverse(&2, Raw, flush(seen, msg, line, place, out))
118
119
120
121def stem(ss: String) -> String:
122 match ss:
123 case SNil{}:
124 SNil{}
125 case SCon{+hh, tt}:
126 Lazy.stop(String, Char.is_eq(hh, '~'), SNil{}, _u => SCon{hh, stem(tt)})
127
128
129def owns(names: List<&2, String>, +name: String) -> Bool:
130 match names:
131 case Nil{}:
132 False{}
133 case Con{hh, tt}:
134 Lazy.or_else(String.eq(hh, name), _u => owns(tt, name))
135
136
137
138
139
140
141def mine(names: Maybe<&2, List<&2, String>>, +place: String) -> Bool:
142 match names:
143 case None{}:
144 Bool.not(String.contains(place, "/"))
145 case Some{ks}:
146 Bool.or(String.is_empty(place), owns(ks, stem(place)))
147
148
149
150def diag.at(+own: Bool, +place: String, +line: U32, +body: String) -> Diag:
151 Diag{Bool.pick(U32, Bool.or(Bool.not(own), U32.is_eq(line, 0)), 0, (line - 1 : U32)),
152 Bool.pick(String, own, body, "in " ++ place ++ ":\n" ++ body)}
153
154
155def diag(names: Maybe<&2, List<&2, String>>, rr: Raw) -> Diag:
156 Raw{+place, line, body} = rr
157 diag.at(mine(names, place), place, line, body)
158
159
160def diags(rs: List<&2, Raw>, +names: Maybe<&2, List<&2, String>>) -> List<&2, Diag>:
161 match rs:
162 case Nil{}:
163 []
164 case Con{rr, rest}:
165 diag(names, rr) <> diags(rest, names)
166
167
168
169def parse_in(+names: Maybe<&2, List<&2, String>>, text: String) -> List<&2, Diag>:
170 diags(finish(run(String.lines(text), St{False{}, False{}, [], 0, "", []})), names)