language-server: initial commit

A simple language server engine, for use with hoonls.py, which presents
the RPC interface expected by editors.  Features:

- Syntax error detection
- Rune snippets
- Autocomplete
This commit is contained in:
Philip Monk 2019-11-03 19:18:40 -08:00
parent ec3ab084c7
commit da71dac4ab
No known key found for this signature in database
GPG Key ID: B66E1F02604E44EC
5 changed files with 823 additions and 6 deletions

View File

@ -0,0 +1,179 @@
/+ *server, auto, easy-print, rune-snippet
|%
:: +move: output effect
::
+$ move [bone card]
:: +card: output effect payload
::
+$ card
$% [%connect wire binding:eyre term]
[%disconnect wire binding:eyre]
[%http-response =http-event:http]
==
::
+$ lsp-req
$% [%sync text=@t]
[%completion row=@ud col=@ud]
==
::
+$ state
[buf=@t cache=(tri @tD [hair hoon]) cache-size=@]
--
::
|_ [bow=bowl:gall state]
::
++ this .
++ tall-cached
(ifix [gay gay] tall:[%*(. vast fat cache)])
::
++ prep
|= old=(unit state)
^- (quip move _this)
~& > %lsp-prep
?~ old
:_ this
[ost.bow %connect / [~ /'~language-server-protocol'] %language-server]~
[~ this(+<+< -.u.old)]
::
:: alerts us that we were bound.
::
++ bound
|= [wir=wire success=? binding=binding:eyre]
^- (quip move _this)
[~ this]
::
:: +poke-handle-http-request: received on a new connection established
::
++ poke-handle-http-request
%- (require-authorization:app ost.bow move this)
|= =inbound-request:eyre
^- (quip move _this)
?> ?=(^ body.request.inbound-request)
=/ =lsp-req
%. (need (de-json:html q.u.body.request.inbound-request))
=, dejs:format
%- of
:~ sync+so
completion+(ot line+ni character+ni ~)
==
=^ out-jon +<+.this
?- -.lsp-req
%sync (handle-sync +.lsp-req)
%completion (handle-completion +.lsp-req)
==
[[ost.bow %http-response (json-response:app (json-to-octs out-jon))]~ this]
::
++ handle-sync
|= text=@t
=, enjs:format
[*json text cache cache-size]
::
::
++ ingest
=| count=@
|= =wall
^- (tri @tD [hair hoon])
?~ wall
[~ ~]
=. count +(count)
?: (gth count (sub (lent wall) cache-size))
cache
=. cache $(wall t.wall)
?: (lth count (sub (sub (lent wall) cache-size) 100))
cache
~? =(0 (mod (lent wall) 100))
[%ingest-round (lent wall)]
=/ =tape (zing (join "\0a" wall))
=/ mab (try tape tall-cached)
?~ mab
cache
:: ~& >>> [- +<]:u.mab
(~(put up cache) u.mab)
::
++ try
|* [los=tape sab=rule]
=+ vex=(sab [[1 1] los])
?~ q.vex
~
:: Finished with spaces remaining
::
=/ rows (sub p.p.q.u.q.vex 1)
=/ cols
?: =(0 rows)
(sub q.p.q.u.q.vex 1)
q.p.q.u.q.vex
=/ used-tape
(scag (sub (lent los) (lent q.q.u.q.vex)) los)
?. ?=([?(%32 %10) *] (flop used-tape))
:: ~& >>> no=used-tape
~
:- ~
^= u
:: ~& > [p.q.u.q.vex ppv=p.p.vex qpv=q.p.vex tol=(sub (lent los) (lent q.q.u.q.vex)) ll=(lent los) lq=(lent q.q.u.q.vex)]
:: ~& >> yes=used-tape
[used-tape [rows cols] p.u.q.vex]
::
++ safe-sub
|= [a=@ b=@]
?: (gth b a)
0
(sub a b)
::
++ handle-completion
|= [row=@ud col=@ud]
^- [json @t (tri @tD [hair hoon]) @]
=/ =wain (to-wain:format buf)
=/ =wall (turn wain trip)
:: =? cache (lth cache-size (lent wall)) (ingest wall)
:: =. cache-size (min (lent wall) (add cache-size 100))
=/ =tape (zing (join "\0a" wall))
=/ pos
|- ^- @ud
?~ wain
0
?: =(0 row)
col
%+ add +((met 3 i.wain)) :: +1 because newline
$(row (dec row), wain t.wain)
:_ [buf cache cache-size]
~& >>> bef=(swag [(safe-sub pos 2) 2] tape)
=/ rune (swag [(safe-sub pos 2) 2] tape)
?: (~(has by runes:rune-snippet) rune)
(rune-snippet rune)
::
=/ tl
(tab-list-tape:auto -:!>(..zuse) pos tape cache)
=, enjs:format
?: ?=(%| -.tl)
%- pairs
:~ good+b+|
:+ %diagnostics %a :_ ~
=/ loc (pairs line+(numb (dec row.p.tl)) character+(numb col.p.tl) ~)
%- pairs
:~ range+(pairs start+loc end+loc ~)
severity+n+'1'
message+s+'syntax error'
==
==
?~ p.tl
*json
%- pairs
:~ good+b+&
::
:- %result
%- pairs
:~ 'isIncomplete'^b+&
::
:- %items
:- %a
=/ lots (gth (lent u.p.tl) 10)
%- flop
%+ turn (scag 50 u.p.tl)
|= [=term =type]
?: lots
(frond label+s+term)
=/ detail (crip ~(ram re ~(duck easy-print type)))
(pairs label+s+term detail+s+detail ~)
==
==
--

View File

@ -3,6 +3,17 @@
|%
+$ ids (list [=term =type])
::
:: Like +rose except also produces line number
::
++ lily
|* {los/tape sab/rule}
=+ vex=(sab [[1 1] los])
?~ q.vex
[%| p=p.vex(q (dec q.p.vex))]
?. =(~ q.q.u.q.vex)
[%| p=p.vex(q (dec q.p.vex))]
[%& p=p.u.q.vex]
::
:: Get all the identifiers accessible if this type is your subject.
::
++ get-identifiers
@ -94,7 +105,7 @@
=/ res (mule |.((find-type sut gen)))
?- -.res
%& p.res
%| ((slog (flop (scag 1 p.res))) ~)
%| ((slog (flop (scag 10 p.res))) ~)
==
::
:: Get the subject type of the wing where you've put the "magic-spoon".
@ -219,7 +230,7 @@
++ find-type-in-spec
|= [sut=type pec=spec]
^- (unit [term type])
!!
~
::
:: Insert magic marker in hoon source at the given position.
::
@ -299,6 +310,18 @@
:: Same as +advance-hoon, but takes a position and text directly.
::
++ tab-list-tape
|= [sut=type pos=@ud code=tape]
(tab-list-hoon sut (scan txt:(insert-magic pos code) vest))
|= [sut=type pos=@ud code=tape cache=(tri @tD [hair hoon])]
^- (each (unit ids) [row=@ col=@])
~& > %start-magick
=/ magicked txt:(insert-magic pos code)
~& > %start-parsing
=/ parser
(ifix [gay gay] tall:[%*(. vast fat cache)])
=/ res (lily magicked parser)
?: ?=(%| -.res)
~& > [%parsing-error p.res]
[%| p.res]
:- %&
~& > %parsed-good
((cury tab-list-hoon sut) p.res)
--

View File

@ -0,0 +1,541 @@
=>
|%
++ snippet
|= [rune=tape text=tape]
^- json
=, enjs:format
%- pairs
:~ 'label'^(tape rune)
'insertTextFormat'^(numb 2)
'insertText'^(tape text)
==
::
++ runes
%- malt
:~ :- "|$"
"""
$\{1:sample}
$\{2:body}
"""
:- "|_"
"""
$\{1:sample}
++ $\{2:arm}
$\{3:body}
--
"""
:- "|:"
"""
$\{1:sample}
$\{2:body}
"""
:- "|%"
"""
++ $\{1:arm}
$\{2:body}
--
"""
:- "|."
"""
$\{1:body}
"""
:- "|^"
"""
$\{1:body}
::
++ $\{2:arm}
$\{3:body}
--
"""
:- "|-"
"""
$\{1:body}
"""
:- "|~"
"""
$\{1:sample}
$\{2:body}
"""
:- "|*"
"""
$\{1:sample}
$\{2:body}
"""
:- "|="
"""
$\{1:sample}
$\{2:body}
"""
:- "|@"
"""
++ $\{1:arm}
$\{2:body}
--
"""
:- "|?"
"""
$\{1:sample}
"""
::
:- ":_"
"""
$\{1:tail}
$\{2:head}
"""
:- ":^"
"""
$\{1:car}
$\{2:cadr}
$\{3:caddr}
$\{4:cddr}
"""
:- ":-"
"""
$\{1:tail}
$\{2:head}
"""
:- ":+"
"""
$\{1:car}
$\{2:cadr}
$\{3:cddr}
"""
:- ":~"
"""
$\{1:item}
==
"""
:- ":*"
"""
$\{1:item}
==
"""
::
:- "%_"
"""
$\{1:target}
$\{2:wing} $\{3:new-value}
==
"""
:- "%."
"""
$\{1:arg}
$\{2:gate}
"""
:- "%-"
"""
$\{1:gate}
$\{2:arg}
"""
:- "%:"
"""
$\{1:gate}
$\{2:args}
==
"""
:- "%*"
"""
$\{1:target-wing} $\{2:from}
$\{3:wing} $\{4:new-value}
==
"""
:- "%^"
"""
$\{1:gate}
$\{2:arg1}
$\{3:arg2}
$\{4:arg3}
"""
:- "%+"
"""
$\{1:gate}
$\{2:arg1}
$\{3:arg2}
"""
:- "%~"
"""
$\{1:arm}
$\{2:core}
$\{3:arg}
"""
:- "%="
"""
$\{1:target}
$\{2:wing} $\{3:new-value}
==
"""
::
:- ".^"
"""
$\{1:mold}
$\{2:path}
"""
:- ".+"
"""
$\{1:atom}
"""
:- ".*"
"""
$\{1:subject}
$\{2:formula}
"""
:- ".="
"""
$\{1:a}
$\{2:b}
"""
:- ".?"
"""
$\{1:noun}
"""
::
:- "^|"
"""
$\{1:iron-core}
"""
:- "^%"
"""
$\{1:body}
"""
:- "^."
"""
$\{1:a}
$\{2:b}
"""
:- "^+"
"""
$\{1:like}
$\{2:body}
"""
:- "^-"
"""
$\{1:type}
$\{2:body}
"""
:- "^&"
"""
$\{1:zinc-core}
"""
:- "^~"
"""
$\{1:constant}
"""
:- "^="
"""
$\{1:face}
$\{2:body}
"""
:- "^?"
"""
$\{1:lead-core}
"""
:- "^*"
"""
$\{1:type}
"""
:- "^:"
"""
$\{1:type}
"""
::
:- "~|"
"""
$\{1:trace}
$\{2:body}
"""
:- "~_"
"""
$\{1:tank}
$\{2:body}
"""
:- "~%"
"""
$\{1:name}
$\{2:parent}
~
$\{3:body}
"""
:- "~/"
"""
$\{1:name}
$\{2:body}
"""
:- "~<"
"""
$\{1:hint}
$\{2:body}
"""
:- "~>"
"""
$\{1:hint}
$\{2:body}
"""
:- "~$"
"""
$\{1:name}
$\{2:body}
"""
:- "~+"
"""
$\{1:body}
"""
:- "~&"
"""
$\{1:printf}
$\{2:body}
"""
:- "~="
"""
$\{1:a}
$\{2:b}
"""
:- "~?"
"""
$\{1:condition}
$\{2:printf}
$\{3:body}
"""
:- "~!"
"""
$\{1:type}
$\{2:body}
"""
::
:- ";="
"""
$\{1:manx}
==
"""
:- ";:"
"""
$\{1:gate}
$\{2:args}
==
"""
:- ";/"
"""
$\{1:tape}
"""
:- ";<"
"""
$\{1:type} bind:m $\{2:body1}
$\{3:body2}
"""
:- ";~"
"""
$\{1:gate}
$\{2:args}
==
"""
:- ";;"
"""
$\{1:type}
$\{2:body}
"""
::
:- "=|"
"""
$\{1:type}
$\{2:body}
"""
:- ";~"
"""
$\{1:wing} $\{2:value}
==
$\{3:body}
"""
:- "=/"
"""
$\{1:face}
$\{2:value}
$\{3:body}
"""
:- "=;"
"""
$\{1:face}
$\{2:body}
$\{3:value}
"""
:- "=."
"""
$\{1:wing}
$\{2:value}
$\{3:body}
"""
:- "%^"
"""
$\{1:wing} $\{2:condition}
$\{3:value}
$\{4:body}
"""
:- "=<"
"""
$\{1:formula}
$\{2:subject}
"""
:- "=-"
"""
$\{1:body}
$\{2:value}
"""
:- "=>"
"""
$\{1:subject}
$\{2:formula}
"""
:- "=^"
"""
$\{1:face}
$\{2:wing}
$\{3:computation}
$\{4:body}
"""
:- "=+"
"""
$\{1:value}
$\{2:body}
"""
:- "=~"
"""
$\{1:body}
"""
:- "=*"
"""
$\{1:alias} $\{2:value}
$\{3:body}
"""
:- "=,"
"""
$\{1:alias}
$\{3:body}
"""
::
:- "?|"
"""
$\{1:condition}
==
"""
:- "?-"
"""
$\{1:case}
$\{2:type} $\{3:value}
==
"""
:- "?:"
"""
$\{1:if}
$\{2:then}
$\{3:else}
"""
:- "?."
"""
$\{1:if}
$\{2:else}
$\{3:then}
"""
:- "?^"
"""
$\{1:value}
$\{2:if-cell}
$\{3:if-atom}
"""
:- "?<"
"""
$\{1:assertion}
$\{2:body}
"""
:- "?>"
"""
$\{1:assertion}
$\{2:body}
"""
:- "?-"
"""
$\{1:case} $\{2:else}
$\{3:type} $\{4:value}
==
"""
:- "?&"
"""
$\{1:condition}
==
"""
:- "?@"
"""
$\{1:value}
$\{2:if-atom}
$\{3:if-cell}
"""
:- "?~"
"""
$\{1:value}
$\{2:if-null}
$\{3:if-nonnull}
"""
:- "?#"
"""
$\{1:skin}
$\{2:wing}
"""
:- "?="
"""
$\{1:type}
$\{2:wing}
"""
:- "?!"
"""
$\{1:loobean}
"""
::
:- "!,"
"""
*hoon
$\{1:ast}
"""
:- "!>"
"""
$\{1:value}
"""
:- "!;"
"""
$\{1:type}
$\{2:body}
"""
:- "!="
"""
$\{1:body}
"""
:- "!@"
"""
$\{1:wing}
$\{2:if-exists}
$\{3:if-not-exists}
"""
:- "!?"
"""
$\{1:version}
$\{2:body}
"""
:- "!!"
""
==
--
|= rune=tape
=, enjs:format
^- json
%- pairs
:~ good+b+&
:- %result
%- pairs
:~ 'isIncomplete'^b+&
:- %items
:- %a :_ ~
~| [%unrecognized-rune rune]
%+ snippet
rune
(~(got by runes) rune)
== ==

29
pkg/arvo/lib/trie.hoon Normal file
View File

@ -0,0 +1,29 @@
|%
++ trie
|$ [key-t val-t]
[val=(unit val-t) kid=(map key-t (trie key-t val-t))]
--
::
=| a=(trie * *)
=* val-t ?>(?=(^ val.a) val.a)
|@
++ put
|* [b=(list *) c=*]
=> .(b (homo b))
|- ^+ a
?~ b
a(val `c)
=/ son (~(gut by kid.a) i.b [~ ~])
a(kid (~(put by kid.a) i.b $(a son, b t.b)))
::
++ get
|* b=(list *)
=> .(b (homo b))
|-
?~ b
[~ val.a]
=/ son (~(get by kid.a) i.b)
?~ son
[b val.a]
$(a u.son, b t.b)
--

View File

@ -1817,6 +1817,32 @@
?~(r.a [~ n.a] $(a r.a))
--
::
++ up
!:
=| a=(tri * *)
=* val-t ?>(?=(^ val.a) val.a)
|@
++ put
|* [b=(list *) c=*]
=> .(b (homo b))
|- ^+ a
?~ b
a(val `c)
=/ son (~(gut by kid.a) i.b [~ ~])
a(kid (~(put by kid.a) i.b $(a son, b t.b)))
::
++ get
|* b=(list *)
=> .(b (homo b))
|-
?~ b
[~ val.a]
=/ son (~(get by kid.a) i.b)
?~ son
[b val.a]
$(a u.son, b t.b)
--
::
:::: 2o: containers ::
:: ::
::
@ -1838,6 +1864,11 @@
$| (tree item)
|=(a=(tree) ~(apt in a))
::
++ tri :: trie
|$ [key-t val-t]
$~ [~ ~]
[val=(unit val-t) kid=(map key-t (tri key-t val-t))]
::
:::: 2l: container from container ::
:: ::
::
@ -11936,7 +11967,7 @@
%*(. vast bug bug, wer wer) :: wer: where we are
::
++ vast :: main parsing core
=+ [bug=`?`| wer=*path]
=+ [bug=`?`| wer=*path fat=*(tri @tD [hair hoon])]
|%
++ gash %+ cook :: parse path
|= a/(list tyke) ^- tyke
@ -14100,7 +14131,21 @@
(punt ;~(pfix ;~(pose net tis) wyde))
==
==
++ tall :: full tall form
++ tall
|= tub=nail
=+ ^- [t=tape h=(unit [=hair =hoon])]
(~(get up fat) q.tub)
?^ h
~& > [%tall-cache-hit len=(lent t) hair=hair.u.h]
~? (lth (lent t) 200) t=t
=/ =hair
?: =(0 p.hair.u.h)
[p=p.p.tub q=(add q.p.tub q.hair.u.h)]
[p=(add p.p.tub p.hair.u.h) q=q.hair.u.h]
[p=hair q=`u=[p=hoon.u.h q=[p=hair q=t]]]
(tale tub)
::
++ tale :: full tall form
%+ knee *hoon
|.(~+((wart ;~(pose expression:(norm &) long lute apex:(sail &)))))
++ till :: mold tall form