basex-lsp/webapp/lsp/jsonrpc.xqm
2025-09-23 17:39:28 +01:00

145 lines
No EOL
3.1 KiB
Text

(:~
: jsonrpc
: @author andy bunce
:)
module namespace rpc = 'rpc';
import module namespace lsp-text = 'lsp-text' at "lsp-text.xqm";
(: map methods to functions :)
declare variable $rpc:Methods:=map:merge((
map{
"initialize" : rpc:method-initialize#1,
"initialized" : rpc:method-initialized#1,
"workspace/didChangeConfiguration" :rpc:method-unknown#1
},
$lsp-text:methods
));
(:~ return map if $msg is jsonrpc else empty :)
declare
function rpc:parse($msg as xs:string)
as map(*)?
{
try {
let $json:=parse-json($msg)
return if($json?jsonrpc="2.0" and exists($json?method))
then $json
else ()
} catch *{
()
}
};
(:~ send reply to $json message
get functions for methods
evaluate function with message
send any responses
:)
declare
function rpc:reply($json as map(*))
as empty-sequence() {
let $f :=(void(trace($json,"➡️")),$rpc:Methods?($json?method))
let $response := $f!.($json)
return $response!rpc:send(.)
};
(:~ send with logging :)
declare
function rpc:send($msg as map(*))
as empty-sequence()
{
ws:send($msg =>trace("⬅️"),ws:id())
};
(:~ canned initialize response :)
declare
function rpc:method-initialize($json as map(*))
as map(*)
{
ws:set(ws:id(),"client", $json?params),
rpc:result($json,
json:doc("etc/capabilities.json",{"format":"w3"}))
};
(:~ initialized response :)
declare
function rpc:method-initialized($json as map(*))
as empty-sequence()
{
ws:set(ws:id(),"initialized", true())
(: ,rpc:later(5000,"rpc:show-message","This is a test async message 5secs after initialized",ws:id()) :)
};
(:~ unknown method response :)
declare
function rpc:method-unknown($json as map(*))
as map(*)?
{
let $_:=trace($json?method,"unknown")
return ()
};
(:~ rpc log message :)
declare function rpc:log($msg as xs:string)
as map(*)
{
{"jsonrpc": "2.0",
"method":"window/logMessage",
"params":{"type":1, "message": $msg}
}
};
(:~ rpc response to $json msg :)
declare function rpc:result($json as map(*),$result)
as map(*)
{
map{
"jsonrpc": "2.0",
"id": $json?id,
"result": $result
}
};
(:~ response when error :)
declare function rpc:error($message as xs:string,$json as map(*))
{
{
"jsonrpc": "2.0",
"id": $json?id,
"error": {
"code": -32803,
"message": $message,
"data": { "documentUri": "file:///example.txt",
"reason": "Syntax block"
}
}
}
};
(:~ window/showMessage :)
declare function rpc:show-message($msg as xs:string)
as map(*)
{
{"jsonrpc": "2.0",
"method":"window/showMessage",
"params":{"type":3, "message": $msg}
}
};
(:~ send message after delay :)
declare function rpc:later($msdelay as xs:integer,$fn as xs:string ,$arg, $wsid as xs:string)
{
let $xq:=`import module namespace rpc = 'rpc' at 'jsonrpc.xqm';
declare variable $msdelay external;
declare variable $fn external;
declare variable $arg external;
declare variable $wsid external;
prof:sleep($msdelay),
ws:send(function-lookup(xs:QName($fn),1)($arg),$wsid)
`
let $bindings:={"msdelay": $msdelay,"wsid": $wsid, "fn": $fn,"arg":$arg}
let $opts:={"cache": true(),"base-uri": static-base-uri()}
return job:eval($xq, $bindings,$opts)=>void()
};