src/lsp/nav.bend
bolt@1.27.0.0 · 453 lines · 40 definitions
1
2
3
4
5
6
7
8import Base
9import bolt@1.27.0.0/src/syntax/outline.bend as Outline
10import bolt@1.27.0.0/src/syntax/word.bend as Word
11import bolt@1.27.0.0/src/syntax/bind.bend as Bind
12import bolt@1.27.0.0/src/lsp/files/service.bend as F
13import bolt@1.27.0.0/src/lsp/docs.bend as Docs
14import bolt@1.27.0.0/src/lsp/path.bend as Path
15import bolt@1.27.0.0/src/lsp/proto.bend as Proto
16
17
18
19type Found is Data:
20 Found{uri: String, item: Outline.Item}
21 Here{uri: String, bind: Bind.Bind}
22
23
24def found(mm: Maybe<&2, Outline.Item>, uri: String) -> Maybe<&2, Found>:
25 match mm:
26 case None{}:
27 None{}
28 case Some{item}:
29 Some{Found{uri, item}}
30
31def load.miss(~fs: F.Files, open: Maybe<&2, String>, path: String) -> IO(String):
32 match open:
33 case Some{text}:
34 IO.pure(String, text)
35 case None{}:
36 do IO<String>:
37 r : Maybe<&2, String> <- F.read(fs, path)
38 return Maybe.default(&2, String, r, "")
39
40
41def load(~fs: F.Files, docs: List<&2, Docs.Doc>, uri: String, path: String) -> IO(String):
42 load.miss(~fs, Docs.get(docs, uri), path)
43
44
45def in_file(~fs: F.Files, docs: List<&2, Docs.Doc>, +path: String, name: String) -> IO(Maybe<&2, Found>):
46 do IO<Maybe<&2, Found>>:
47 text : String <- load(~fs, docs, "file://" ++ path, path)
48 return found(Outline.find(Outline.items(text), name), "file://" ++ path)
49
50
51def in_base(~fs: F.Files, docs: List<&2, Docs.Doc>, name: String) -> IO(Maybe<&2, Found>):
52 do IO<Maybe<&2, Found>>:
53 path : String <- F.base_path(fs)
54 in_file(~fs, docs, path, name)
55
56
57def own(
58 ~fs: F.Files,
59 mm: Maybe<&2, Outline.Item>,
60 docs: List<&2, Docs.Doc>,
61 uri: String,
62 name: String
63) -> IO(Maybe<&2, Found>):
64 match mm:
65 case Some{item}:
66 IO.pure(Maybe<&2, Found>, Some{Found{uri, item}})
67 case None{}:
68 in_base(~fs, docs, name)
69
70
71def via(
72 ~fs: F.Files,
73 imp: Maybe<&2, String>,
74 docs: List<&2, Docs.Doc>,
75 +uri: String,
76 +name: String,
77 items: List<&2, Outline.Item>
78) -> IO(Maybe<&2, Found>):
79 match imp:
80 case Some{rel}:
81 in_file(~fs, docs, Path.join(Path.dir(Proto.path(uri)), rel), Word.rest(name))
82 case None{}:
83 own(~fs, Outline.find(items, name), docs, uri, name)
84
85
86def among(
87 ~fs: F.Files,
88 +items: List<&2, Outline.Item>,
89 docs: List<&2, Docs.Doc>,
90 uri: String,
91 +name: String
92) -> IO(Maybe<&2, Found>):
93 via(~fs, Outline.import_path(items, Word.head(name)), docs, uri, name, items)
94
95
96def resolve(~fs: F.Files, +docs: List<&2, Docs.Doc>, +uri: String, name: String) -> IO(Maybe<&2, Found>):
97 do IO<Maybe<&2, Found>>:
98 text : String <- load(~fs, docs, uri, Proto.path(uri))
99 among(~fs, Outline.items(text), docs, uri, name)
100
101
102def here(mm: Maybe<&2, Bind.Bind>, uri: String) -> Maybe<&2, Found>:
103 match mm:
104 case None{}:
105 None{}
106 case Some{b}:
107 Some{Here{uri, b}}
108
109
110
111
112def at.qual(
113 ~fs: F.Files,
114 imp: Maybe<&2, String>,
115 rest: String,
116 items: List<&2, Outline.Item>,
117 docs: List<&2, Docs.Doc>,
118 +uri: String,
119 +name: String
120) -> IO(Maybe<&2, Found>):
121 match imp:
122 case Some{rel}:
123 in_file(~fs, docs, Path.join(Path.dir(Proto.path(uri)), rel), rest)
124 case None{}:
125 own(~fs, Outline.find(items, name), docs, uri, name)
126
127
128
129
130
131def at.target(
132 ~fs: F.Files,
133 tg: Bind.Target,
134 +name: String,
135 binds: List<&2, Bind.Bind>,
136 +items: List<&2, Outline.Item>,
137 docs: List<&2, Docs.Doc>,
138 +uri: String
139) -> IO(Maybe<&2, Found>):
140 match tg:
141 case Bind.TLocal{tl, tc}:
142 IO.pure(Maybe<&2, Found>, here(Bind.binder(binds, tl, tc), uri))
143 case Bind.TItem{+item}:
144 own(~fs, Outline.find(items, item), docs, uri, item)
145 case Bind.TQual{alias, rest}:
146 at.qual(~fs, Outline.import_path(items, alias), rest, items, docs, uri, name)
147 case Bind.TFree{}:
148 own(~fs, Outline.find(items, name), docs, uri, name)
149
150
151
152
153def at.site(
154 ~fs: F.Files,
155 site: Maybe<&2, Bind.Site>,
156 binds: List<&2, Bind.Bind>,
157 items: List<&2, Outline.Item>,
158 docs: List<&2, Docs.Doc>,
159 +uri: String,
160 name: String
161) -> IO(Maybe<&2, Found>):
162 match site:
163 case Some{Bind.SBind{b}}:
164 IO.pure(Maybe<&2, Found>, Some{Here{uri, b}})
165 case Some{Bind.SUse{Bind.Use{n, _l, _c, tg}}}:
166 at.target(~fs, tg, n, binds, items, docs, uri)
167 case None{}:
168 resolve(~fs, docs, uri, name)
169
170def at.bound(
171 ~fs: F.Files,
172 bb: Bind.Bound,
173 items: List<&2, Outline.Item>,
174 docs: List<&2, Docs.Doc>,
175 uri: String,
176 name: String,
177 +line: U32,
178 +col: U32
179) -> IO(Maybe<&2, Found>):
180 Bind.Bound{+binds, uses, scopes} = bb
181 at.site(~fs, Bind.at(Bind.Bound{binds, uses, scopes}, line, col), binds, items, docs, uri, name)
182
183def at.in(
184 ~fs: F.Files,
185 +text: String,
186 docs: List<&2, Docs.Doc>,
187 uri: String,
188 +line: U32,
189 +col: U32
190) -> IO(Maybe<&2, Found>):
191 at.bound(~fs, Bind.bound(text), Outline.items(text), docs, uri, Word.at(text, line, col), line, col)
192
193
194def at(~fs: F.Files, +docs: List<&2, Docs.Doc>, +uri: String, line: U32, col: U32) -> IO(Maybe<&2, Found>):
195 do IO<Maybe<&2, Found>>:
196 text : String <- load(~fs, docs, uri, Proto.path(uri))
197 at.in(~fs, text, docs, uri, line, col)
198
199
200def hover.binder(mm: Maybe<&2, Found>, uri: String, bb: Bind.Bind) -> Maybe<&2, Found>:
201 match mm:
202 case Some{f}:
203 Some{f}
204 case None{}:
205 Some{Here{uri, bb}}
206
207
208
209
210def hover.pick(mm: Maybe<&2, Found>, local: Maybe<&2, Found>, uri: String, bb: Bind.Bind) -> Maybe<&2, Found>:
211 match mm:
212 case Some{f}:
213 Some{f}
214 case None{}:
215 hover.binder(local, uri, bb)
216
217
218
219def hover.item(
220 ~fs: F.Files,
221 +name: String,
222 items: List<&2, Outline.Item>,
223 docs: List<&2, Docs.Doc>,
224 +uri: String,
225 bb: Bind.Bind
226) -> IO(Maybe<&2, Found>):
227 do IO<Maybe<&2, Found>>:
228 m : Maybe<&2, Found> <- resolve(~fs, docs, uri, name)
229 return hover.pick(m, found(Outline.find(items, name), uri), uri, bb)
230
231
232def hover.kind(
233 ~fs: F.Files,
234 kind: Bind.BindKind,
235 name: String,
236 items: List<&2, Outline.Item>,
237 docs: List<&2, Docs.Doc>,
238 uri: String,
239 bb: Bind.Bind
240) -> IO(Maybe<&2, Found>):
241 match kind:
242 case Bind.KItem{}:
243 hover.item(~fs, name, items, docs, uri, bb)
244 case other:
245 IO.pure(Maybe<&2, Found>, Some{Here{uri, bb}})
246
247
248def hover.bind(
249 ~fs: F.Files,
250 bb: Bind.Bind,
251 items: List<&2, Outline.Item>,
252 docs: List<&2, Docs.Doc>,
253 uri: String
254) -> IO(Maybe<&2, Found>):
255 Bind.Bind{+name, line, col, +kind, note} = bb
256 hover.kind(~fs, kind, name, items, docs, uri, Bind.Bind{name, line, col, kind, note})
257
258
259
260
261def hover.site(
262 ~fs: F.Files,
263 site: Maybe<&2, Bind.Site>,
264 items: List<&2, Outline.Item>,
265 +binds: List<&2, Bind.Bind>,
266 docs: List<&2, Docs.Doc>,
267 uri: String,
268 +name: String
269) -> IO(Maybe<&2, Found>):
270 match site:
271 case Some{Bind.SBind{b}}:
272 hover.bind(~fs, b, items, docs, uri)
273 case other:
274 at.site(~fs, site, binds, items, docs, uri, name)
275
276
277def hover.bound(
278 ~fs: F.Files,
279 bb: Bind.Bound,
280 items: List<&2, Outline.Item>,
281 docs: List<&2, Docs.Doc>,
282 uri: String,
283 name: String,
284 +line: U32,
285 +col: U32
286) -> IO(Maybe<&2, Found>):
287 Bind.Bound{+binds, uses, scopes} = bb
288 hover.site(~fs, Bind.at(Bind.Bound{binds, uses, scopes}, line, col), items, binds, docs, uri, name)
289
290
291def hover.in(
292 ~fs: F.Files,
293 +text: String,
294 docs: List<&2, Docs.Doc>,
295 uri: String,
296 +line: U32,
297 +col: U32
298) -> IO(Maybe<&2, Found>):
299 hover.bound(~fs, Bind.bound(text), Outline.items(text), docs, uri, Word.at(text, line, col), line, col)
300
301
302
303def hover(~fs: F.Files, +docs: List<&2, Docs.Doc>, +uri: String, line: U32, col: U32) -> IO(Maybe<&2, Found>):
304 do IO<Maybe<&2, Found>>:
305 text : String <- load(~fs, docs, uri, Proto.path(uri))
306 hover.in(~fs, text, docs, uri, line, col)
307
308
309
310
311
312
313type Sites is Data:
314 Sites{mine: Bool, at: List<&2, Bind.Pos>}
315
316def sites.of(site: Maybe<&2, Bind.Site>, +bb: Bind.Bound) -> Sites:
317 match site:
318 case None{}:
319 Sites{False{}, []}
320 case Some{Bind.SBind{Bind.Bind{name, l, c, Bind.KItem{}, note}}}:
321 Sites{True{}, Bind.item_sites(bb, name)}
322 case Some{Bind.SBind{Bind.Bind{name, l, c, Bind.KCtor{}, note}}}:
323 Sites{True{}, Bind.item_sites(bb, name)}
324 case Some{Bind.SBind{Bind.Bind{name, l, c, k, note}}}:
325 Sites{True{}, Bind.sites(bb, l, c)}
326 case Some{Bind.SUse{Bind.Use{n, l, c, Bind.TLocal{tl, tc}}}}:
327 Sites{True{}, Bind.sites(bb, tl, tc)}
328 case Some{Bind.SUse{Bind.Use{n, l, c, Bind.TItem{name}}}}:
329 Sites{True{}, Bind.item_sites(bb, name)}
330 case Some{Bind.SUse{Bind.Use{n, l, c, other}}}:
331 Sites{False{}, Bind.named_sites(bb, n)}
332
333def sites.in(+text: String, +line: U32, +col: U32) -> Sites:
334 +b = Bind.bound(text)
335 sites.of(Bind.at(b, line, col), b)
336
337
338def sites(~fs: F.Files, docs: List<&2, Docs.Doc>, +uri: String, line: U32, col: U32) -> IO(Sites):
339 do IO<Sites>:
340 text : String <- load(~fs, docs, uri, Proto.path(uri))
341 return sites.in(text, line, col)
342
343
344def outline(
345 ~fs: F.Files,
346 docs: List<&2, Docs.Doc>,
347 +uri: String
348) -> IO(List<&2, Outline.Item>):
349 do IO<List<&2, Outline.Item>>:
350 text : String <- load(~fs, docs, uri, Proto.path(uri))
351 return Outline.items(text)
352
353
354
355
356
357def starting_in(
358 ~fs: F.Files,
359 docs: List<&2, Docs.Doc>,
360 +path: String,
361 prefix: String,
362 qual: String
363) -> IO(List<&2, Outline.Item>):
364 do IO<List<&2, Outline.Item>>:
365 text : String <- load(~fs, docs, "file://" ++ path, path)
366 return Outline.starting(Outline.items(text), prefix, qual)
367
368
369def from_base(~fs: F.Files, typed: Bool, docs: List<&2, Docs.Doc>, prefix: String) -> IO(List<&2, Outline.Item>):
370 match typed:
371 case False{}:
372 IO.pure(List<&2, Outline.Item>, [])
373 case True{}:
374 do IO<List<&2, Outline.Item>>:
375 path : String <- F.base_path(fs)
376 starting_in(~fs, docs, path, prefix, "")
377
378
379def as_items(ls: List<&2, Bind.Bind>) -> List<&2, Outline.Item>:
380 match ls:
381 case Nil{}:
382 Nil{}
383 case Con{Bind.Bind{name, line, col, kind, note}, t}:
384 Outline.Item{Outline.ILocal{}, name, line, note, Proto.kind_name(kind), ""} <> as_items(t)
385
386
387
388def unqualified(
389 ~fs: F.Files,
390 +items: List<&2, Outline.Item>,
391 docs: List<&2, Docs.Doc>,
392 +prefix: String,
393 locals: List<&2, Outline.Item>
394) -> IO(List<&2, Outline.Item>):
395 do IO<List<&2, Outline.Item>>:
396 base : List<&2, Outline.Item> <- from_base(~fs, Bool.not(String.is_empty(prefix)), docs, prefix)
397 return List.append(&2, Outline.Item, Outline.starting(locals, prefix, ""),
398 List.append(&2, Outline.Item, Outline.starting(items, prefix, ""),
399 List.append(&2, Outline.Item, Outline.aliases(items, prefix), base)))
400
401
402
403def candidates.via(
404 ~fs: F.Files,
405 imp: Maybe<&2, String>,
406 items: List<&2, Outline.Item>,
407 docs: List<&2, Docs.Doc>,
408 uri: String,
409 +prefix: String,
410 locals: List<&2, Outline.Item>
411) -> IO(List<&2, Outline.Item>):
412 match imp:
413 case Some{rel}:
414 starting_in(~fs, docs, Path.join(Path.dir(Proto.path(uri)), rel), Word.rest(prefix), Word.head(prefix) ++ ".")
415 case None{}:
416 unqualified(~fs, items, docs, prefix, locals)
417
418def candidates.among(
419 ~fs: F.Files,
420 +items: List<&2, Outline.Item>,
421 docs: List<&2, Docs.Doc>,
422 uri: String,
423 +prefix: String,
424 locals: List<&2, Outline.Item>
425) -> IO(List<&2, Outline.Item>):
426 candidates.via(~fs, Outline.import_path(items, Word.head(prefix)), items, docs, uri, prefix, locals)
427
428def candidates.in(
429 ~fs: F.Files,
430 +text: String,
431 docs: List<&2, Docs.Doc>,
432 uri: String,
433 prefix: String,
434 line: U32
435) -> IO(List<&2, Outline.Item>):
436 candidates.among(~fs, Outline.items(text), docs, uri, prefix, as_items(Bind.visible(Bind.bound(text), line)))
437
438
439
440def candidates(
441 ~fs: F.Files,
442 +docs: List<&2, Docs.Doc>,
443 +uri: String,
444 prefix: String,
445 line: U32
446) -> IO(List<&2, Outline.Item>):
447 do IO<List<&2, Outline.Item>>:
448 text : String <- load(~fs, docs, uri, Proto.path(uri))
449 candidates.in(~fs, text, docs, uri, prefix, line)
450
451
452def text_of(~fs: F.Files, docs: List<&2, Docs.Doc>, +uri: String) -> IO(String):
453 load(~fs, docs, uri, Proto.path(uri))