/+  language-server-parser
::  Autocomplete for hoon.
::
=/  debug  |
|%
+*  option  [item]
  [term=cord detail=item]
::
::  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
  |=  ty=type
  %-  flop
  |-  ^-  (list (option type))
  ?-    ty
      %noun      ~
      %void      ~
      [%atom *]  ~
      [%cell *]
    %+  weld
      $(ty p.ty)
    $(ty q.ty)
  ::
      [%core *]
    %-  weld
    :_  ?.  ?=(%gold r.p.q.ty)
          ~
        $(ty p.ty)
    ^-  (list (option type))
    %-  zing
    %+  turn  ~(tap by q.r.q.ty)
    |=  [term =tome]
    %+  turn
      ~(tap by q.tome)
    |=  [name=term =hoon]
    ^-  (pair term type)
    ~|  term=term
    [name ~(play ~(et ut ty) ~[name] ~)]
  ::
      [%face *]
    ?^  p.ty
      ~
    [p.ty q.ty]~
  ::
      [%fork *]
    %=    $
        ty
      =/  tines  ~(tap in p.ty)
      ?~  tines
        %void
      |-  ^-  type
      ?~  t.tines
        i.tines
      (~(fuse ut $(tines t.tines)) i.tines)
    ==
  ::
      [%hint *]  $(ty q.ty)
      [%hold *]  $(ty ~(repo ut ty))
  ==
::
++  search-exact
  |*  [sid=term options=(list (option))]
  =/  match
    %+  skim  options
    |=  [id=cord *]
    =(sid id)
  ?~  match
    ~
  [~ i.match]
::
::  Get all the identifiers that start with sid.
::
++  search-prefix
  |*  [sid=cord ids=(list (option))]
  ^+  ids
  %+  skim  ids
  |=  [id=cord *]
  ^-  ?(%.y %.n)
  =(sid (new-end [3 (met 3 sid)] id))
::
::  Get the longest prefix of a list of identifiers.
::
++  longest-match
  |=  matches=(list (option))
  ^-  cord
  ?~  matches
    ''
  =/  n  1
  =/  last  (met 3 term.i.matches)
  |-  ^-  term
  ?:  (gth n last)
    term.i.matches
  =/  prefix  (new-end [3 n] term.i.matches)
  ?:  |-  ^-  ?
      ?|  ?=(~ t.matches)
          ?&  =(prefix (new-end [3 n] term.i.t.matches))
              $(t.matches t.t.matches)
      ==  ==
    $(n +(n))
  (new-end [3 (dec n)] term.i.matches)
::
::  Run +find-type safely, printing the first line of the stack trace on
::  error.
::
++  find-type-mule
  |=  [sut=type gen=hoon]
  ^-  (unit [term type])
  =/  res  (mule |.((find-type sut gen)))
  ?-  -.res
    %&  p.res
    %|  ((slog (flop (scag 10 p.res))) ~)
  ==
::
::  Get the subject type of the wing where you've put the "magic-spoon".
::
++  find-type
  |=  [sut=type gen=hoon]
  =*  loop  $
  |^
  ^-  (unit [term type])
  ?-    gen
      [%cnts [%magic-spoon ~] *]    `['' sut]
      [%cnts [%magic-spoon @ ~] *]  `[i.t.p.gen sut]
      [%cnts [%magic-spoon @ *] *]
    %=  $
      sut      (~(play ut sut) wing+t.t.p.gen)
      t.p.gen  t.p.gen(t ~)
    ==
  ::
      [%cnts [%magic-fork @ ~] *]
    `['' (~(play ut sut) wing+t.p.gen)]
  ::
      [^ *]      (both p.gen q.gen)
      [%brcn *]  (grow q.gen)
      [%brpt *]  (grow q.gen)
      [%cnts *]
    |-  ^-  (unit [term type])
    =*  inner-loop  $
    ?~  q.gen
      ~
    %+  replace
      loop(gen q.i.q.gen)
    |.  inner-loop(q.gen t.q.gen)
  ::
      [%dtkt *]  (spec-and-hoon p.gen q.gen)
      [%dtls *]  loop(gen p.gen)
      [%rock *]  ~
      [%sand *]  ~
      [%tune *]  ~
      [%dttr *]  (both p.gen q.gen)
      [%dtts *]  (both p.gen q.gen)
      [%dtwt *]  loop(gen p.gen)
      [%hand *]  ~
      [%ktbr *]  loop(gen p.gen)
      [%ktls *]  (both p.gen q.gen)
      [%ktpm *]  loop(gen p.gen)
      [%ktsg *]  loop(gen p.gen)
      [%ktwt *]  loop(gen p.gen)
      [%note *]  loop(gen q.gen)
      [%sgzp *]  (both p.gen q.gen)
      [%sggr *]  loop(gen q.gen)  ::  should check for hoon in p.gen
      [%tsgr *]  (change p.gen q.gen)
      [%tscm *]
    %+  replace
      loop(gen p.gen)
    |.(loop(gen q.gen, sut (~(busk ut sut) p.gen)))
  ::
      [%wtcl *]  (bell p.gen q.gen r.gen)
      [%fits *]  (both p.gen wing+q.gen)
      [%wthx *]  loop(gen wing+q.gen)
      [%dbug *]  loop(gen q.gen)
      [%zpcm *]  (both p.gen q.gen)
      [%lost *]  loop(gen p.gen)
      [%zpmc *]  (both p.gen q.gen)
      [%zpts *]  loop(gen p.gen)
      [%zppt *]  (both q.gen r.gen)
      [%zpzp *]  ~
      *
    =+  doz=~(open ap gen)
    ?:  =(doz gen)
      ~_  (show [%c 'hoon'] [%q gen])
      ~>  %mean.'play-open'
      !!
    loop(gen doz)
  ==
  ::
  ++  replace
    |=  [a=(unit [term type]) b=(trap (unit [term type]))]
    ^-  (unit [term type])
    ?~(a $:b a)
  ::
  ++  both
    |=  [a=hoon b=hoon]
    (replace loop(gen a) |.(loop(gen b)))
  ::
  ++  bell
    |=  [a=hoon b=hoon c=hoon]
    %+  replace  loop(gen a)
    |.  %+  replace  loop(gen b, sut (~(gain ut sut) a))
    |.  loop(gen c, sut (~(lose ut sut) a))
  ::
  ++  spec-and-hoon
    |=  [a=spec b=hoon]
    (replace (find-type-in-spec sut a) |.(loop(gen b)))
  ::
  ++  change
    |=  [a=hoon b=hoon]
    (replace loop(gen a) |.(loop(gen b, sut (~(play ut sut) a))))
  ::
  ++  grow
    |=  m=(map term tome)
    =/  tomes  ~(tap by m)
    |-  ^-  (unit [term type])
    =*  outer-loop  $
    ?~  tomes
      ~
    =/  arms  ~(tap by q.q.i.tomes)
    |-  ^-  (unit [term type])
    =*  inner-loop  $
    ?~  arms
      outer-loop(tomes t.tomes)
    %+  replace
      loop(gen q.i.arms, sut (~(play ut sut) gen))
    |.  inner-loop(arms t.arms)
  --
::
::  Not implemented yet.  I wonder whether we should modify types found
::  in spec mode such that if it's a mold that produces a type, it
::  should just display the type and not that it's technically a
::  function.
::
++  find-type-in-spec
  |=  [sut=type pec=spec]
  ^-  (unit [term type])
  ~
::
++  get-id
  |=  [pos=@ud txt=tape]
  ^-  [forward=(unit @t) backward=(unit @t) id=(unit @t)]
  =/  seek
    ;~(sfix (punt (cook crip (star prn))) (star ;~(pose prn (just `@`10))))
  =/  forward=(unit @t)
    (scan (slag pos txt) seek)
  =/  backward=(unit @t)
    %-  (lift |=(t=@tas (swp 3 t)))
    (scan (flop (scag pos txt)) seek)
  =/  id=(unit @t)
    ?~  forward
      ?~  backward
        ~
      `u.backward
    ?~  backward
      `u.forward
    `(cat 3 u.backward u.forward)
  [forward backward id]
::
::  Insert magic marker in hoon source at the given position.
::
++  insert-magic
  |=  [pos=@ud txt=tape]
  ^-  [back-pos=@ud fore-pos=@ud txt=tape]
  ::  Find beg-pos by searching backward to where the current term
  ::  begins
  =+  (get-id pos txt)
  =/  back-pos
    ?~  backward
      pos
    (sub pos (met 3 u.backward))
  =/  fore-pos
    ?~  forward
      pos
    (add pos (met 3 u.forward))
  :+  back-pos  fore-pos
  ::  Insert "magic-spoon" marker so +find-type can identify where to
  ::  stop.
  ::
  ;:  weld
    (scag back-pos txt)
    ?:  &(?=(~ id) ?=([%'.' *] (slag pos txt)))
      "magic-fork"
    "magic-spoon"
    ?~  id
      ""
    "."
    (slag back-pos txt)
    "\0a"
  ==
::
::  Produce the longest possible advance without choosing between
::  matches.
::
::    Takes a +hoon which has already has a magic-spoon marker.  Useful if
::    you want to handle your own parsing.
::
++  advance-hoon
  |=  [sut=type gen=hoon]
  %+  bind  (find-type-mule sut gen)
  |=  [id=term typ=type]
  =/  matches=(list (option type))
    (search-prefix id (get-identifiers typ))
  (longest-match matches)
::
::  Same as +advance-hoon, but takes a position and text directly.
::
++  advance-tape
  |=  [sut=type pos=@ud code=tape]
  (advance-hoon sut (scan txt:(insert-magic pos code) vest))
::
::  Produce a list of matches.
::
::    Takes a +hoon which has already has a magic-spoon marker.  Useful if
::    you want to handle your own parsing.
::
++  tab-list-hoon
  |=  [sut=type gen=hoon]
  ^-  (unit (list (option type)))
  %+  bind  (find-type-mule sut gen)
  |=  [id=term typ=type]
  (search-prefix id (get-identifiers typ))
::
::  Same as +advance-hoon, but takes a position and text directly.
::
++  tab-list-tape
  |=  [sut=type pos=@ud code=tape]
  ^-  (each (unit (list (option type))) [row=@ col=@])
  ~?  >  debug  %start-magick
  =/  magicked  txt:(insert-magic pos code)
  ~?  >  debug  %start-parsing
  =/  res  (lily magicked (language-server-parser *path))
  ?:  ?=(%| -.res)
    ~?  >  debug  [%parsing-error p.res]
    [%| p.res]
  :-  %&
  ~?  >  debug  %parsed-good
  ((cury tab-list-hoon sut) hoon.p.res)
::
:: Generators
++  tab-generators
  |=  [pfix=path app=(unit term) gens=(list term)]
  ^-  (list (option tank))
  %+  turn  gens
  |=  gen=term
  ^-  (option tank)
  =/  pax=path
    (weld pfix ~[gen %hoon])
  =/  file
    .^(@t %cx pax)
  :_  (render-help file)
  ?~  app
    (cat 3 '+' gen)
  ?:  =(%hood u.app)
    (cat 3 '|' gen)
  :((cury cat 3) ':' u.app '|' gen)
::  Stolen from +help
++  render-help
  |=  a=@t
  ^-  tank
  :-  %leaf
  =/  c  (to-wain:format a)
  ?~  c  "~"
  ?.  =('::  ' (new-end [3 4] i.c))
    "<undocumented>"
  (trip i.c)
--