src/lsp/server.bend
bolt@1.27.0.0 · 738 lines · 66 definitions
1
2
3
4
5
6
7
8
9
10import Base
11import emerging-ezjson@1.2.0.0/main.bend as Ezjson
12import emerging-ezjson@1.2.0.0/src/value.bend as J
13import bolt@1.27.0.0/src/lsp/proto.bend as Proto
14import bolt@1.27.0.0/src/lsp/docs.bend as Docs
15import bolt@1.27.0.0/src/lsp/nav.bend as Nav
16import bolt@1.27.0.0/src/lsp/semantic.bend as Semantic
17import bolt@1.27.0.0/src/rules.bend as Lint
18import bolt@1.27.0.0/src/finding.bend as Finding
19import bolt@1.27.0.0/src/config.bend as Config
20import bolt@1.27.0.0/src/lint/plan.bend as Plan
21import bolt@1.27.0.0/src/lint/world.bend as W
22import bolt@1.27.0.0/src/role.bend as Role
23import bolt@1.27.0.0/src/src.bend as Src
24import bolt@1.27.0.0/src/noqa.bend as Noqa
25import bolt@1.27.0.0/src/lsp/transport/service.bend as T
26import bolt@1.27.0.0/src/lsp/checker/answer.bend as C
27import bolt@1.27.0.0/src/lsp/files/service.bend as F
28import bolt@1.27.0.0/src/lsp/report.bend as Rep
29import bolt@1.27.0.0/src/lsp/enc.bend as Enc
30import bolt@1.27.0.0/src/syntax/outline.bend as Outline
31import bolt@1.27.0.0/src/syntax/lex.bend as Lex
32import bolt@1.27.0.0/src/syntax/word.bend as Word
33
34
35type Method is Data:
36 MInitialize{}
37 MShutdown{}
38 MExit{}
39 MOpen{}
40 MChange{}
41 MSave{}
42 MClose{}
43 MHover{}
44 MDefinition{}
45 MSymbols{}
46 MCompletion{}
47 MReferences{}
48 MRename{}
49 MSemantic{}
50 MOther{}
51
52
53
54type Reply is Data:
55 RNote{body: String}
56 RResult{id: J.Json, result: J.Json}
57 RError{id: J.Json, code: String, message: String}
58
59
60type Answer is Data:
61 Answer{result: J.Json}
62 Refusal{code: String, message: String}
63
64
65
66type Work is Data:
67 Work{notes: List<&2, String>, answer: Answer, docs: List<&2, Docs.Doc>}
68
69
70type Out is Data:
71 Out{replies: List<&2, Reply>, quit: Bool, docs: List<&2, Docs.Doc>}
72
73
74
75type Role is Data:
76 RRequest{}
77 RNotice{}
78 RExit{}
79 RUnknown{}
80
81
82def method(+name: String) -> Method:
83 Bool.pick(Method, String.eq(name, "initialize"), MInitialize{},
84 Bool.pick(Method, String.eq(name, "shutdown"), MShutdown{},
85 Bool.pick(Method, String.eq(name, "exit"), MExit{},
86 Bool.pick(Method, String.eq(name, "textDocument/didOpen"), MOpen{},
87 Bool.pick(Method, String.eq(name, "textDocument/didChange"), MChange{},
88 Bool.pick(Method, String.eq(name, "textDocument/didSave"), MSave{},
89 Bool.pick(Method, String.eq(name, "textDocument/didClose"), MClose{},
90 Bool.pick(Method, String.eq(name, "textDocument/hover"), MHover{},
91 Bool.pick(Method, String.eq(name, "textDocument/definition"), MDefinition{},
92 Bool.pick(Method, String.eq(name, "textDocument/documentSymbol"), MSymbols{},
93 Bool.pick(Method, String.eq(name, "textDocument/completion"), MCompletion{},
94 Bool.pick(Method, String.eq(name, "textDocument/references"), MReferences{},
95 Bool.pick(Method, String.eq(name, "textDocument/rename"), MRename{},
96 Bool.pick(Method, String.eq(name, "textDocument/semanticTokens/full"), MSemantic{},
97 MOther{}))))))))))))))
98
99
100def role(mm: Method) -> Role:
101 match mm:
102 case MInitialize{}:
103 RRequest{}
104 case MShutdown{}:
105 RRequest{}
106 case MExit{}:
107 RExit{}
108 case MOpen{}:
109 RNotice{}
110 case MChange{}:
111 RNotice{}
112 case MSave{}:
113 RNotice{}
114 case MClose{}:
115 RNotice{}
116 case MHover{}:
117 RRequest{}
118 case MDefinition{}:
119 RRequest{}
120 case MSymbols{}:
121 RRequest{}
122 case MCompletion{}:
123 RRequest{}
124 case MReferences{}:
125 RRequest{}
126 case MRename{}:
127 RRequest{}
128 case MSemantic{}:
129 RRequest{}
130 case MOther{}:
131 RUnknown{}
132
133
134def name_of(msg: J.Json) -> String:
135 Maybe.default(&2, String, Ezjson.as_str(Ezjson.get(msg, "method")), "")
136
137
138def id_of(msg: J.Json) -> J.Json:
139 Ezjson.get(msg, "id")
140
141
142def uri_of(params: J.Json) -> String:
143 Maybe.default(&2, String, Ezjson.as_str(Ezjson.get(Ezjson.get(params, "textDocument"), "uri")), "")
144
145
146def line_of(params: J.Json) -> U32:
147 Maybe.default(&2, U32, Ezjson.as_u32(Ezjson.get(Ezjson.get(params, "position"), "line")), 0)
148
149
150def col_of(params: J.Json) -> U32:
151 Maybe.default(&2, U32, Ezjson.as_u32(Ezjson.get(Ezjson.get(params, "position"), "character")), 0)
152
153
154def text(rr: Reply) -> String:
155 match rr:
156 case RNote{body}:
157 body
158 case RResult{id, result}:
159 Ezjson.print(Proto.response(id, result))
160 case RError{id, code, message}:
161 Ezjson.print(Proto.failure(id, code, message))
162
163
164def notices(ns: List<&2, String>) -> List<&2, Reply>:
165 match ns:
166 case Nil{}:
167 Nil{}
168 case Con{n, t}:
169 RNote{n} <> notices(t)
170
171
172def respond(id: J.Json, aa: Answer) -> Reply:
173 match aa:
174 case Answer{result}:
175 RResult{id, result}
176 case Refusal{code, message}:
177 RError{id, code, message}
178
179
180def unknown(id: J.Json) -> List<&2, Reply>:
181 match id:
182 case J.JNull{}:
183 []
184 case other:
185 [RError{other, "-32601", "method not found"}]
186
187
188def answered(result: J.Json, docs: List<&2, Docs.Doc>) -> Work:
189 Work{[], Answer{result}, docs}
190
191
192def noted(notes: List<&2, String>, docs: List<&2, Docs.Doc>) -> Work:
193 Work{notes, Answer{J.JNull{}}, docs}
194
195def step.go(rr: Role, id: J.Json, ww: Work) -> Out:
196 match rr:
197 case RRequest{}:
198 Work{notes, answer, docs} = ww
199 Out{[respond(id, answer)], False{}, docs}
200 case RNotice{}:
201 Work{notes, answer, docs} = ww
202 Out{notices(notes), False{}, docs}
203 case RExit{}:
204 Work{notes, answer, docs} = ww
205 Out{[], True{}, docs}
206 case RUnknown{}:
207 Work{notes, answer, docs} = ww
208 Out{unknown(id), False{}, docs}
209
210
211
212
213def step(mm: Method, id: J.Json, ww: Work) -> Out:
214 step.go(role(mm), id, ww)
215
216
217
218
219
220
221
222
223
224def pub.note(cc: Enc.Cols, uri: String, ds: List<&2, Rep.Diag>, gs: List<&2, Finding.Graded>) -> String:
225 Ezjson.print(Proto.publish(cc, uri, ds, gs))
226
227
228
229def pub.cands(cwd: String, path: String) -> List<&2, String>:
230 Config.chain(Config.home(cwd, Config.dir_of(path)))
231
232
233
234
235
236
237
238def pub.lint(
239 +path: String,
240 +text: String,
241 rs: List<&2, Maybe<&2, String>>,
242 ms: List<&2, Role.Mark>
243) -> List<&2, Finding.Graded>:
244 +cfg = Config.nearest(rs)
245 Bool.pick(List<&2, Finding.Graded>, Plan.is_config(path), [],
246 Plan.after([Noqa.file(path, Lex.tokens(text))], False{},
247 Lint.graded(cfg, Lint.on.role(Role.of(ms, path), Src.of(path, text))), fs => Lint.graded(cfg, fs)))
248
249
250
251def pub.marks(+world: W.World) -> List<&2, Role.Mark>:
252 Plan.marks.named(world)
253
254
255
256def pub.checked(
257 ee: Enc.Enc,
258 +uri: String,
259 +text: String,
260 +ds: List<&2, Rep.Diag>,
261 rs: List<&2, Maybe<&2, String>>,
262 +ms: List<&2, Role.Mark>,
263 +docs: List<&2, Docs.Doc>
264) -> Work:
265 noted([pub.note(Enc.cols(ee, text), uri, ds, pub.lint(Proto.path(uri), text, rs, ms))],
266 Docs.set_checked(docs, uri, ds, ms))
267
268
269def pub.open(
270 ee: Enc.Enc,
271 +uri: String,
272 +text: String,
273 ds: List<&2, Rep.Diag>,
274 rs: List<&2, Maybe<&2, String>>,
275 +ms: List<&2, Role.Mark>,
276 +docs: List<&2, Docs.Doc>
277) -> Work:
278 pub.checked(ee, uri, text, ds, rs, ms, Docs.set(docs, uri, text))
279
280
281def pub.save(
282 ee: Enc.Enc,
283 +uri: String,
284 ds: List<&2, Rep.Diag>,
285 rs: List<&2, Maybe<&2, String>>,
286 +ms: List<&2, Role.Mark>,
287 +docs: List<&2, Docs.Doc>
288) -> Work:
289 pub.checked(ee, uri, Maybe.default(&2, String, Docs.get(docs, uri), ""), ds, rs, ms, docs)
290
291
292
293def pub.change(
294 ee: Enc.Enc,
295 +uri: String,
296 +text: String,
297 rs: List<&2, Maybe<&2, String>>,
298 +docs: List<&2, Docs.Doc>
299) -> Work:
300 noted([pub.note(Enc.cols(ee, text), uri, Docs.errors(docs, uri),
301 pub.lint(Proto.path(uri), text, rs, Docs.marks(docs, uri)))], Docs.set(docs, uri, text))
302
303
304def pub.close(ee: Enc.Enc, +uri: String, docs: List<&2, Docs.Doc>) -> Work:
305 noted([pub.note(Enc.cols(ee, ""), uri, [], [])], Docs.del(docs, uri))
306
307
308def pub.configs(~fs: F.Files, +uri: String) -> IO(List<&2, Maybe<&2, String>>):
309 do IO<List<&2, Maybe<&2, String>>>:
310 cwd : String <- F.cwd(fs)
311 F.read_all(~fs, pub.cands(cwd, Proto.path(uri)))
312
313
314def pub.read.answer(mm: Maybe<&2, String>) -> W.Answer:
315 match mm:
316 case None{}:
317 W.Unread{}
318 case Some{text}:
319 W.Read{text}
320
321
322
323def pub.answer(~fs: F.Files, ask: W.Ask) -> IO(W.Answer):
324 match ask:
325 case W.Entries{dir}:
326 do IO<W.Answer>:
327 es : List<&2, String> <- F.entries(fs, dir)
328 return W.Listed{es}
329 case W.Contents{path}:
330 do IO<W.Answer>:
331 mm : Maybe<&2, String> <- F.read(fs, path)
332 return pub.read.answer(mm)
333
334
335def pub.answers(~fs: F.Files, asks: List<&2, W.Ask>) -> IO(List<&2, W.Reply>):
336 match asks:
337 case Nil{}:
338 IO.pure(List<&2, W.Reply>, [])
339 case Con{+a, rest}:
340 do IO<List<&2, W.Reply>>:
341 r : W.Answer <- pub.answer(~fs, a)
342 more : List<&2, W.Reply> <- pub.answers(~fs, rest)
343 return W.Reply{a, r} <> more
344
345
346def pub.world.more(world: W.World, rs: List<&2, W.Reply>) -> W.World:
347 W.World{paths, cwd, replies} = world
348 W.World{paths, cwd, List.append(&2, W.Reply, replies, rs)}
349
350
351
352def pub.world.step(
353 ~fs: F.Files,
354 asks: List<&2, W.Ask>,
355 +world: W.World,
356 go: W.World -> IO(W.World)
357) -> IO(W.World):
358 match asks:
359 case Nil{}:
360 IO.pure(W.World, world)
361 case Con{a, rest}:
362 do IO<W.World>:
363 rs : List<&2, W.Reply> <- pub.answers(~fs, a <> rest)
364 go(pub.world.more(world, rs))
365
366
367
368def pub.world.go(~fs: F.Files, fuel: Nat, +world: W.World) -> IO(W.World):
369 match fuel:
370 case 0n:
371 IO.pure(W.World, world)
372 case 1n+f:
373 pub.world.step(~fs, Plan.wants(world), world, ww => pub.world.go(~fs, f, ww))
374
375
376
377def pub.seen(~fs: F.Files, +uri: String) -> IO(List<&2, Role.Mark>):
378 do IO<List<&2, Role.Mark>>:
379 cwd : String <- F.cwd(fs)
380 ww : W.World <- pub.world.go(~fs, Nat.add(Plan.limit(), 2n), W.World{[Proto.path(uri)], cwd, []})
381 return pub.marks(ww)
382
383
384def pub.opened(
385 ~fs: F.Files,
386 ~ck: C.Checker,
387 ee: Enc.Enc,
388 +uri: String,
389 text: String,
390 docs: List<&2, Docs.Doc>
391) -> IO(Work):
392 do IO<Work>:
393 aa : C.Answer <- C.check(ck, Proto.path(uri))
394 rs : List<&2, Maybe<&2, String>> <- pub.configs(~fs, uri)
395 ms : List<&2, Role.Mark> <- pub.seen(~fs, uri)
396 return pub.open(ee, uri, text, C.errors(aa), rs, ms, docs)
397
398
399def pub.saved(~fs: F.Files, ~ck: C.Checker, ee: Enc.Enc, +uri: String, docs: List<&2, Docs.Doc>) -> IO(Work):
400 do IO<Work>:
401 aa : C.Answer <- C.check(ck, Proto.path(uri))
402 rs : List<&2, Maybe<&2, String>> <- pub.configs(~fs, uri)
403 ms : List<&2, Role.Mark> <- pub.seen(~fs, uri)
404 return pub.save(ee, uri, C.errors(aa), rs, ms, docs)
405
406
407def pub.changed(~fs: F.Files, ee: Enc.Enc, +uri: String, text: String, docs: List<&2, Docs.Doc>) -> IO(Work):
408 do IO<Work>:
409 rs : List<&2, Maybe<&2, String>> <- pub.configs(~fs, uri)
410 return pub.change(ee, uri, text, rs, docs)
411
412def hover.of(mm: Maybe<&2, Nav.Found>) -> J.Json:
413 match mm:
414 case None{}:
415 J.JNull{}
416 case Some{Nav.Found{uri, item}}:
417 Proto.hover(item)
418 case Some{Nav.Here{uri, b}}:
419 Proto.hover.local(b)
420
421def definition.of(cc: Enc.Cols, mm: Maybe<&2, Nav.Found>) -> J.Json:
422 match mm:
423 case None{}:
424 J.JNull{}
425 case Some{Nav.Found{uri, item}}:
426 Proto.location(cc, uri, item)
427 case Some{Nav.Here{uri, b}}:
428 Proto.location.local(cc, uri, b)
429
430
431
432def col_in(cc: Enc.Cols, +params: J.Json) -> U32:
433 Enc.into(cc, line_of(params), col_of(params))
434
435
436def hovered(
437 ~fs: F.Files,
438 ee: Enc.Enc,
439 +docs: List<&2, Docs.Doc>,
440 +uri: String,
441 +params: J.Json
442) -> IO(Work):
443 do IO<Work>:
444 text : String <- Nav.text_of(~fs, docs, uri)
445 f : Maybe<&2, Nav.Found> <- Nav.hover(~fs, docs, uri, line_of(params), col_in(Enc.cols(ee, text), params))
446 return answered(hover.of(f), docs)
447
448
449def defined(
450 ~fs: F.Files,
451 ee: Enc.Enc,
452 +docs: List<&2, Docs.Doc>,
453 +uri: String,
454 +params: J.Json
455) -> IO(Work):
456 do IO<Work>:
457 +text : String <- Nav.text_of(~fs, docs, uri)
458 +cc : Enc.Cols = Enc.cols(ee, text)
459 f : Maybe<&2, Nav.Found> <- Nav.at(~fs, docs, uri, line_of(params), col_in(cc, params))
460 return answered(definition.of(cc, f), docs)
461
462def complete.with(
463 ~fs: F.Files,
464 cc: Enc.Cols,
465 +prefix: String,
466 +docs: List<&2, Docs.Doc>,
467 uri: String,
468 +line: U32,
469 +col: U32
470) -> IO(Work):
471 do IO<Work>:
472 items : List<&2, Outline.Item> <- Nav.candidates(~fs, docs, uri, prefix, line)
473 return answered(Proto.completions(cc, items, line, (col - U32.from_nat(String.length(prefix)) : U32), col), docs)
474
475
476def complete(
477 ~fs: F.Files,
478 ee: Enc.Enc,
479 +docs: List<&2, Docs.Doc>,
480 +uri: String,
481 +params: J.Json
482) -> IO(Work):
483 do IO<Work>:
484 +text : String <- Nav.text_of(~fs, docs, uri)
485 +cc : Enc.Cols = Enc.cols(ee, text)
486 +line : U32 = line_of(params)
487 +col : U32 = col_in(cc, params)
488 complete.with(~fs, cc, Word.before(text, line, col), docs, uri, line, col)
489
490def references.of(cc: Enc.Cols, ss: Nav.Sites, uri: String, name: String) -> J.Json:
491 Nav.Sites{mine, at} = ss
492 Proto.locations(cc, uri, name, at)
493
494
495def rename.of(cc: Enc.Cols, ss: Nav.Sites, uri: String, name: String, new: String) -> Answer:
496 Nav.Sites{mine, at} = ss
497 match mine:
498 case True{}:
499 Answer{Proto.rename(cc, uri, name, new, at)}
500 case False{}:
501 Refusal{"-32803", "only a name bound in this file can be renamed"}
502
503
504def references(
505 ~fs: F.Files,
506 ee: Enc.Enc,
507 +docs: List<&2, Docs.Doc>,
508 +uri: String,
509 +params: J.Json
510) -> IO(Work):
511 do IO<Work>:
512 +text : String <- Nav.text_of(~fs, docs, uri)
513 +cc : Enc.Cols = Enc.cols(ee, text)
514 +line : U32 = line_of(params)
515 +col : U32 = col_in(cc, params)
516 s : Nav.Sites <- Nav.sites(~fs, docs, uri, line, col)
517 return answered(references.of(cc, s, uri, Word.at(text, line, col)), docs)
518
519
520def renamed(
521 ~fs: F.Files,
522 ee: Enc.Enc,
523 +docs: List<&2, Docs.Doc>,
524 +uri: String,
525 +params: J.Json
526) -> IO(Work):
527 do IO<Work>:
528 +text : String <- Nav.text_of(~fs, docs, uri)
529 +cc : Enc.Cols = Enc.cols(ee, text)
530 +line : U32 = line_of(params)
531 +col : U32 = col_in(cc, params)
532 s : Nav.Sites <- Nav.sites(~fs, docs, uri, line, col)
533 return Work{[],
534 rename.of(cc, s, uri, Word.at(text, line, col),
535 Maybe.default(&2, String, Ezjson.as_str(Ezjson.get(params, "newName")), "")),
536 docs}
537
538
539def semantic(~fs: F.Files, ee: Enc.Enc, +docs: List<&2, Docs.Doc>, uri: String) -> IO(Work):
540 do IO<Work>:
541 text : String <- Nav.text_of(~fs, docs, uri)
542 return answered(Proto.semantic(Semantic.data(ee, text)), docs)
543
544
545def symbols(~fs: F.Files, ee: Enc.Enc, +docs: List<&2, Docs.Doc>, +uri: String) -> IO(Work):
546 do IO<Work>:
547 text : String <- Nav.text_of(~fs, docs, uri)
548 items : List<&2, Outline.Item> <- Nav.outline(~fs, docs, uri)
549 return answered(Proto.symbols(Enc.cols(ee, text), items), docs)
550
551
552
553def work(
554 ~fs: F.Files,
555 ~ck: C.Checker,
556 ee: Enc.Enc,
557 mm: Method,
558 +params: J.Json,
559 +docs: List<&2, Docs.Doc>
560) -> IO(Work):
561 match mm:
562 case MInitialize{}:
563 IO.pure(Work, answered(Proto.capabilities(ee), docs))
564 case MShutdown{}:
565 IO.pure(Work, answered(J.JNull{}, docs))
566 case MExit{}:
567 IO.pure(Work, noted([], docs))
568 case MOpen{}:
569 pub.opened(~fs, ~ck, ee, uri_of(params),
570 Maybe.default(&2, String, Ezjson.as_str(Ezjson.get(Ezjson.get(params, "textDocument"), "text")), ""), docs)
571 case MChange{}:
572 pub.changed(~fs, ee, uri_of(params),
573 Maybe.default(&2, String, Ezjson.as_str(Ezjson.get(Ezjson.at(Ezjson.get(params, "contentChanges"), 0), "text")), ""), docs)
574 case MSave{}:
575 pub.saved(~fs, ~ck, ee, uri_of(params), docs)
576 case MClose{}:
577 IO.pure(Work, pub.close(ee, uri_of(params), docs))
578 case MHover{}:
579 hovered(~fs, ee, docs, uri_of(params), params)
580 case MDefinition{}:
581 defined(~fs, ee, docs, uri_of(params), params)
582 case MSymbols{}:
583 symbols(~fs, ee, docs, uri_of(params))
584 case MCompletion{}:
585 complete(~fs, ee, docs, uri_of(params), params)
586 case MReferences{}:
587 references(~fs, ee, docs, uri_of(params), params)
588 case MRename{}:
589 renamed(~fs, ee, docs, uri_of(params), params)
590 case MSemantic{}:
591 semantic(~fs, ee, docs, uri_of(params))
592 case MOther{}:
593 IO.pure(Work, noted([], docs))
594
595
596
597def encoding(mm: Method, params: J.Json, ee: Enc.Enc) -> Enc.Enc:
598 match mm:
599 case MInitialize{}:
600 Enc.negotiate(params)
601 case _other:
602 ee
603
604
605def handle(
606 ~fs: F.Files,
607 ~ck: C.Checker,
608 ee: Enc.Enc,
609 +msg: J.Json,
610 docs: List<&2, Docs.Doc>
611) -> IO(Out):
612 +mm = method(name_of(msg))
613 do IO<Out>:
614 w : Work <- work(~fs, ~ck, ee, mm, Ezjson.get(msg, "params"), docs)
615 return step(mm, id_of(msg), w)
616
617
618def decoded(mm: Maybe<&2, J.Json>) -> J.Json:
619 Maybe.default(&2, J.Json, mm, J.JNull{})
620
621
622def send_all(~H: Type, ~tr: T.Transport<H>, replies: List<&2, Reply>, h3: H) -> IO(H):
623 match replies:
624 case Nil{}:
625 IO.pure(H, h3)
626 case Con{body, t}:
627 do IO<H>:
628 h2 : H <- T.send(H, tr, h3, text(body))
629 send_all(~H, ~tr, t, h2)
630
631
632def next(
633 -H: Type,
634 quit: Bool,
635 h3: H,
636 ee: Enc.Enc,
637 docs: List<&2, Docs.Doc>,
638 rest: H -> Enc.Enc -> List<&2, Docs.Doc> -> IO(Unit)
639) -> IO(Unit):
640 match quit:
641 case True{}:
642 IO.pure(Unit, Unit{})
643 case False{}:
644 rest(h3, ee, docs)
645
646
647def answer(
648 ~H: Type,
649 ~tr: T.Transport<H>,
650 out: Out,
651 h3: H,
652 ee: Enc.Enc,
653 rest: H -> Enc.Enc -> List<&2, Docs.Doc> -> IO(Unit)
654) -> IO(Unit):
655 Out{replies, quit, docs} = out
656 do IO<Unit>:
657 h2 : H <- send_all(~H, ~tr, replies, h3)
658 next(H, quit, h2, ee, docs, rest)
659
660
661def turn.msg(
662 ~H: Type,
663 ~tr: T.Transport<H>,
664 ~fs: F.Files,
665 ~ck: C.Checker,
666 +msg: J.Json,
667 h3: H,
668 ee: Enc.Enc,
669 docs: List<&2, Docs.Doc>,
670 rest: H -> Enc.Enc -> List<&2, Docs.Doc> -> IO(Unit)
671) -> IO(Unit):
672 +e2 = encoding(method(name_of(msg)), Ezjson.get(msg, "params"), ee)
673 do IO<Unit>:
674 out : Out <- handle(~fs, ~ck, e2, msg, docs)
675 answer(~H, ~tr, out, h3, e2, rest)
676
677
678def turn(
679 ~H: Type,
680 ~tr: T.Transport<H>,
681 ~fs: F.Files,
682 ~ck: C.Checker,
683 mm: Maybe<&2, String>,
684 h3: H,
685 ee: Enc.Enc,
686 docs: List<&2, Docs.Doc>,
687 rest: H -> Enc.Enc -> List<&2, Docs.Doc> -> IO(Unit)
688) -> IO(Unit):
689 match mm:
690 case None{}:
691 IO.pure(Unit, Unit{})
692 case Some{body}:
693 turn.msg(~H, ~tr, ~fs, ~ck, decoded(Ezjson.parse(body)), h3, ee, docs, rest)
694
695
696def got(
697 ~H: Type,
698 ~tr: T.Transport<H>,
699 ~fs: F.Files,
700 ~ck: C.Checker,
701 rr: H & Maybe<&2, String>,
702 ee: Enc.Enc,
703 docs: List<&2, Docs.Doc>,
704 rest: H -> Enc.Enc -> List<&2, Docs.Doc> -> IO(Unit)
705) -> IO(Unit):
706 (h, m) = rr
707 turn(~H, ~tr, ~fs, ~ck, m, h, ee, docs, rest)
708
709def serve.go(
710 ~H: Type,
711 ~tr: T.Transport<H>,
712 ~fs: F.Files,
713 ~ck: C.Checker,
714 fuel: Nat,
715 h3: H,
716 ee: Enc.Enc,
717 docs: List<&2, Docs.Doc>
718) -> IO(Unit):
719 match fuel:
720 case 0n:
721 IO.pure(Unit, Unit{})
722 case 1n+f:
723 do IO<Unit>:
724 r : H & Maybe<&2, String> <- T.recv(H, tr, h3)
725 got(~H, ~tr, ~fs, ~ck, r, ee, docs, hh => e2 => dd => serve.go(~H, ~tr, ~fs, ~ck, f, hh, e2, dd))
726
727
728
729def serve(
730 ~H: Type,
731 ~tr: T.Transport<H>,
732 ~fs: F.Files,
733 ~ck: C.Checker,
734 fuel: Nat,
735 h3: H,
736 docs: List<&2, Docs.Doc>
737) -> IO(Unit):
738 serve.go(~H, ~tr, ~fs, ~ck, fuel, h3, Enc.Utf16{}, docs)