(:~ handle textDocument messages
 : @author andy bunce
 :)
module namespace lsp-text = 'lsp-text';

import module namespace docs="lsp/docs" at "docs.xqm";
import module namespace rpc = 'rpc' at 'jsonrpc.xqm';
import module namespace lspt = 'lsp-typedefs' at 'lsp-typedefs.xqm';
import module namespace pos="lsp/position" at "position.xqm";
import module namespace syms="lsp/symbols" at "providers/documentSymbols.xqm";
import module namespace comp = 'lsp-completions' at "providers/completions.xqm";
import module namespace hov = 'lsp-hover' at "providers/hover.xqm";

declare variable $lsp-text:methods:=map{
  "textDocument/didOpen": lsp-text:didOpen#1,
  "textDocument/didChange": lsp-text:didChange#1,
  "textDocument/didClose" : lsp-text:method-unknown#1,

  "textDocument/hover":  lsp-text:hover#1,
  "textDocument/completion": lsp-text:completion#1,
  "textDocument/formatting" : lsp-text:format#1,
  "textDocument/documentSymbol" : lsp-text:symbols#1
};

declare variable $lsp-text:word-reg:="[A-Z]|_|[a-z]|[À-Ö]|[Ø-ö]|[ø-˿]|[Ͱ-ͽ]|[Ϳ-῿]|[‌-‍]|[⁰-↏]|[Ⰰ-⿯]|[、-퟿]|[豈-﷏]|[ﷰ-�]";

(:~  hover :)
declare
function lsp-text:hover($json as map(*)) 
as map(*)
{
  let $pos:=$json?params?position
  let $uri:= $json?params?textDocument?uri
  let $r:=hov:list($uri,$pos)
return rpc:build-response($json,{"contents":array:build($r)})
};

(:~  symbols :)
declare
function lsp-text:symbols($json as map(*)) 
as map(*)?
{
 let $uri:=$json?params?textDocument?uri
  let $xml:=docs:get(ws:id(), $uri, "parse")
 let $syms:=syms:list($xml,string($xml))
 return rpc:build-response($json,array:build($syms))
};

declare
function lsp-text:completion($json as map(*)) 
as map(*)?
{
 let $uri:=$json?params?textDocument?uri
 let $context:=$json?params?context (:{"triggerCharacter":":","triggerKind":2.0e0}:)
 let $result:=comp:list($context)=>prof:time("⏱️ completions " || $uri)
 return  rpc:build-response($json,array:build($result))
};

declare
function lsp-text:format($json as map(*)) 
as map(*)?
{
 let $uri:=$json?params?textDocument?uri
 let $text:=docs:get(ws:id(), $uri, "textDocument")?text
 let $xml:=docs:get(ws:id(), $uri, "parse")
 return if($xml/self::ERROR)
        then rpc:build-error(
            { "code": -32803, "message": "Syntax errors found.",
              "data": { "documentUri": $uri, "reason": "Syntax errors found." }
            }
            ,$json)
        else 
              let $xml:=$xml update {lsp-text:tidy(.)}
              let $result:=[{
                    "range":pos:full-range($text),
                    "newText": string($xml)
                  }]
              return rpc:build-response($json,$result)
};

(:~  didOpen method response :)
declare
function lsp-text:didOpen($json as map(*)) 
as map(*)?
{
  let $uri:=docs:open(ws:id(),$json?params)=>prof:time("⏱️ doc:save ")
  return docs:parse(ws:id(),$uri)
};

(:~  didChange method response :)
declare
function lsp-text:didChange($json as map(*)) 
as map(*)?
{
 let $uri:=docs:change(ws:id(),$json?params)=>prof:time("⏱️ doc:change ")
 return docs:parse(ws:id(),$uri)
 
};


(:~  unknown method response :)
declare
function lsp-text:method-unknown($json as map(*)) 
as map(*)?
{
 let $_:=trace($json?method,"unknown")
 return ()
};

(:~ ensure spaces around := :)
declare %updating function lsp-text:tidy($doc){
  for $a in $doc//TOKEN[.=":="]
  let $before:= $a/preceding-sibling::node()[1]
  let $after:= $a/following-sibling::node()[1]

  return (
       if($before instance of element(*))
       then insert node " " before $a else (),
       
       if($after instance of element(*))
       then insert node " " after $a else ()
     )
};