Merge branch 'philip/language-server' (#1910)

* philip/language-server:
  language-server: address review comments
  language-server: fix rune typos
  language-server: multiple files and ford
  language-server: namespace libraries
  language-server: cleanup and incremental text sync
  language-server: initial commit

Signed-off-by: Jared Tobin <jared@tlon.io>
This commit is contained in:
Jared Tobin 2019-11-06 16:12:26 +08:00
commit 0e6542afd5
No known key found for this signature in database
GPG Key ID: 0E4647D58F8A69E4
7 changed files with 994 additions and 6 deletions

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:27983a88317d0a15ff14104e38b54ca592f390ed904f7805c68a477c3e5cdfd7
size 8884127
oid sha256:85ed537f104bbefc1c14327b2a0c32d79c0fdc4f9767a738d81046415c5c1ee6
size 8917927

View File

@ -3,7 +3,9 @@
:: :: ::
/? 309 :: arvo kelvin
/- *sole, lens ::
/+ sole, pprint, auto, easy-print ::
/+ sole, pprint, ::
auto=language-server-complete, ::
easy-print=language-server-easy-print ::
:: :: ::
:::: :: ::::
:: :: ::

View File

@ -0,0 +1,217 @@
/+ *server,
auto=language-server-complete,
easy-print=language-server-easy-print,
rune-snippet=language-server-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
$: uri=@t
$% [%sync changes=(list change)]
[%completion position]
==
==
::
+$ change
$: range=(unit range)
range-length=(unit @ud)
text=@t
==
::
+$ range
$: start=position
end=position
==
::
+$ position
[row=@ud col=@ud]
::
+$ state bufs=(map uri=@t buf=wall)
--
::
|_ [bow=bowl:gall state]
::
++ this .
++ prep
|= old=(unit state)
^- (quip move _this)
~& > %lsp-prep
?~ old
:_ this
[ost.bow %connect / [~ /'~language-server-protocol'] %language-server]~
[~ this(bufs 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
::
++ parser
=, dejs:format
|^
%: ot
uri+so
:- %data
%- of
:~ sync+sync
completion+position
==
~
==
::
++ sync
%- ar
%: ou
range+(uf ~ (pe ~ range))
'rangeLength'^(uf ~ (pe ~ ni))
text+(un so)
~
==
::
++ range
%: ot
start+position
end+position
~
==
::
++ position
%: ot
line+ni
character+ni
~
==
--
++ poke-handle-http-request
%- (require-authorization:app ost.bow move this)
|= =inbound-request:eyre
^- (quip move _this)
?> ?=(^ body.request.inbound-request)
=/ =lsp-req
%- parser
(need (de-json:html q.u.body.request.inbound-request))
=/ buf (~(gut by bufs) uri.lsp-req *wall)
=^ out-jon buf
?- +<.lsp-req
%sync (handle-sync buf +>.lsp-req)
%completion (handle-completion buf +>.lsp-req)
==
=. bufs
(~(put by bufs) uri.lsp-req buf)
[[ost.bow %http-response (json-response:app (json-to-octs out-jon))]~ this]
::
++ handle-sync
|= [buf=wall changes=(list change)]
:- *json
|- ^- wall
?~ changes
buf
?: ?|(?=(~ range.i.changes) ?=(~ range-length.i.changes))
=/ =wain (to-wain:format text.i.changes)
=. buf (turn wain trip)
$(changes t.changes)
=/ =tape (zing (join "\0a" buf))
=/ start-pos (get-pos buf start.u.range.i.changes)
=/ end-pos (get-pos buf end.u.range.i.changes)
=. tape
;: weld
(scag start-pos tape)
(trip text.i.changes)
(slag end-pos tape)
==
=. buf (to-wall tape)
$(changes t.changes)
::
++ to-wall
|= =tape
^- wall
%+ roll (flop tape)
|= [char=@tD =wall]
?~ wall
[[char ~] ~]
?: =('\0a' char)
[~ wall]
[[char i.wall] t.wall]
::
++ get-pos
|= [buf=wall position]
^- @ud
?~ buf
0
?: =(0 row)
col
%+ add +((lent i.buf)) :: +1 because newline
$(row (dec row), buf t.buf)
::
++ safe-sub
|= [a=@ b=@]
?: (gth b a)
0
(sub a b)
::
++ handle-completion
|= [buf=wall row=@ud col=@ud]
^- [json wall]
=/ =tape (zing (join "\0a" buf))
=/ pos (get-pos buf row col)
:_ buf
:: Check if we're on a rune
::
=/ rune (swag [(safe-sub pos 2) 2] tape)
?: (~(has by runes:rune-snippet) rune)
(rune-snippet rune)
:: Don't run on large files because it's slow
::
?: (gth (lent buf) 1.000)
=, enjs:format
(pairs good+b+& result+~ ~)
::
=/ tl
(tab-list-tape:auto -:!>(..zuse) pos tape)
=, 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

@ -1,8 +1,21 @@
/+ language-server-parser
:: Autocomplete for hoon.
::
=/ debug |
|%
+$ 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 +107,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 +232,7 @@
++ find-type-in-spec
|= [sut=type pec=spec]
^- (unit [term type])
!!
~
::
:: Insert magic marker in hoon source at the given position.
::
@ -300,5 +313,15 @@
::
++ tab-list-tape
|= [sut=type pos=@ud code=tape]
(tab-list-hoon sut (scan txt:(insert-magic pos code) vest))
^- (each (unit ids) [row=@ col=@])
~? > debug %start-magick
=/ magicked txt:(insert-magic pos code)
~? > debug %start-parsing
=/ res (lily magicked (language-server-parser *beam))
?: ?=(%| -.res)
~? > debug [%parsing-error p.res]
[%| p.res]
:- %&
~? > debug %parsed-good
((cury tab-list-hoon sut) tssg+sources.p.res)
--

View File

@ -0,0 +1,204 @@
:: lifted directly from ford, should probably be in zuse
=< parse-scaffold
=, ford
|%
++ parse-scaffold
|= src-beam=beam
::
=/ hoon-parser (vang & (en-beam:format src-beam))
|^ ::
%+ cook
|= a=[@ud (list ^cable) (list ^cable) (list ^crane) (list hoon)]
^- scaffold
[[[p q] s]:src-beam a]
::
%+ ifix [gay gay]
;~ plug
:: parses the zuse version, eg "/? 309"
::
;~ pose
(ifix [;~(plug net wut gap) gap] dem)
(easy zuse)
==
:: pareses the structures, eg "/- types"
::
;~ pose
(ifix [;~(plug net hep gap) gap] (most ;~(plug com gaw) cable))
(easy ~)
==
:: parses the libraries, eg "/+ lib1, lib2"
::
;~ pose
(ifix [;~(plug net lus gap) gap] (most ;~(plug com gaw) cable))
(easy ~)
==
::
(star ;~(sfix crane gap))
::
(most gap tall:hoon-parser)
==
:: +beam: parses a hood path and converts it to a beam
::
++ beam
%+ sear de-beam:format
;~ pfix
net
(sear plex (stag %clsg poor)):hoon-parser
==
:: +cable: parses a +^cable, a reference to something on the filesystem
::
:: This parses:
::
:: `library` -> wraps `library` around the library `library`
:: `face=library` -> wraps `face` around the library `library`
:: `*library` -> exposes `library` directly to the subject
::
++ cable
%+ cook |=(a=^cable a)
;~ pose
(stag ~ ;~(pfix tar sym))
(cook |=([face=term tis=@ file=term] [`face file]) ;~(plug sym tis sym))
(cook |=(a=term [`a a]) sym)
==
:: +crane: all runes that start with / which aren't /?, /-, /+ or //.
::
++ crane
=< apex
:: whether we allow tall form
=| allow-tall-form=?
::
|%
++ apex
%+ knee *^crane |. ~+
;~ pfix net
;~ pose
:: `/~` hoon literal
::
(stag %fssg ;~(pfix sig hoon))
:: `/$` process query string
::
(stag %fsbc ;~(pfix bus hoon))
:: `/|` first of many options that succeeds
::
(stag %fsbr ;~(pfix bar parse-alts))
:: `/=` wrap a face around a crane
::
(stag %fsts ;~(pfix tis parse-face))
:: `/.` null terminated list
::
(stag %fsdt ;~(pfix dot parse-list))
:: `/,` switch by path
::
(stag %fscm ;~(pfix com parse-switch))
:: `/&` pass through a series of mark
::
(stag %fspm ;~(pfix pad parse-pipe))
:: `/_` run a crane on each file in the current directory
::
(stag %fscb ;~(pfix cab subcrane))
:: `/;` passes date through a gate
::
(stag %fssm ;~(pfix mic parse-gate))
:: `/:` evaluate at path
::
(stag %fscl ;~(pfix col parse-at-path))
:: `/^` cast
::
(stag %fskt ;~(pfix ket parse-cast))
:: `/*` run a crane on each file with current path as prefix
::
(stag %fstr ;~(pfix tar subcrane))
:: `/!mark/ evaluate as hoon, then pass through mark
::
(stag %fszp ;~(pfix zap ;~(sfix sym net)))
:: `/mark/` passes current path through :mark
::
(stag %fszy ;~(sfix sym net))
==
==
:: +parse-alts: parse a set of alternatives
::
++ parse-alts
%+ wide-or-tall
(ifix [lit rit] (most ace subcrane))
;~(sfix (star subcrane) gap duz)
:: +parse-face: parse a face around a subcrane
::
++ parse-face
%+ wide-or-tall
;~(plug sym ;~(pfix tis subcrane))
;~(pfix gap ;~(plug sym subcrane))
:: +parse-list: parse a null terminated list of cranes
::
++ parse-list
%+ wide-or-tall
fail
;~(sfix (star subcrane) gap duz)
:: +parse-switch: parses a list of [path crane]
::
++ parse-switch
%+ wide-or-tall
fail
=- ;~(sfix (star -) gap duz)
;~(pfix gap net ;~(plug static-path subcrane))
:: +parse-pipe: parses a pipe of mark conversions
::
++ parse-pipe
%+ wide-or-tall
;~(plug (plus ;~(sfix sym pad)) subcrane)
=+ (cook |=(a=term [a ~]) sym)
;~(pfix gap ;~(plug - subcrane))
:: +parse-gate: parses a gate applied to a crane
::
++ parse-gate
%+ wide-or-tall
;~(plug ;~(sfix wide:hoon-parser mic) subcrane)
;~(pfix gap ;~(plug tall:hoon-parser subcrane))
:: +parse-at-path: parses a late bound bath
::
++ parse-at-path
%+ wide-or-tall
;~(plug ;~(sfix late-bound-path col) subcrane)
;~(pfix gap ;~(plug late-bound-path subcrane))
:: +parse-cast: parses a mold and then the subcrane to apply that mold to
::
++ parse-cast
%+ wide-or-tall
;~(plug ;~(sfix wyde:hoon-parser ket) subcrane)
;~(pfix gap ;~(plug till:hoon-parser subcrane))
:: +subcrane: parses a subcrane
::
++ subcrane
%+ wide-or-tall
apex(allow-tall-form |)
;~(pfix gap apex)
:: +wide-or-tall: parses tall form hoon if :allow-tall-form is %.y
::
++ wide-or-tall
|* [wide=rule tall=rule]
?. allow-tall-form wide
;~(pose wide tall)
:: +hoon: parses hoon as an argument to a crane
::
++ hoon
%+ wide-or-tall
(ifix [lac rac] (stag %cltr (most ace wide:hoon-parser)))
;~(pfix gap tall:hoon-parser)
--
:: +static-path: parses a path
::
++ static-path
(sear plex (stag %clsg (more net hasp))):hoon-parser
:: +late-bound-path: a path whose time varies
::
++ late-bound-path
;~ pfix net
%+ cook |=(a=truss a)
=> hoon-parser
;~ plug
(stag ~ gash)
;~(pose (stag ~ ;~(pfix cen porc)) (easy ~))
==
==
--
--

View File

@ -0,0 +1,542 @@
=>
|%
++ 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)
== ==