149 lines
No EOL
3.5 KiB
Text
149 lines
No EOL
3.5 KiB
Text
(:~
|
|
: jsonrpc
|
|
: @author andy bunce
|
|
:)
|
|
module namespace rpc = 'rpc';
|
|
import module namespace lsp-text = 'lsp-text' at "lsp-text.xqm";
|
|
|
|
(:~ build a notification :)
|
|
declare function rpc:build-notification ($method as xs:string, $params as map(*))
|
|
as map(*){
|
|
{"jsonrpc": "2.0", "method": $method, "params": $params }
|
|
};
|
|
|
|
(:~ response to $json msg, if result empty just acknowledge :)
|
|
declare function rpc:build-response($json as map(*),$result:=())
|
|
as map(*){
|
|
{ "jsonrpc": "2.0", "id": $json?id, "result": $result}
|
|
};
|
|
|
|
(:~ response when error :)
|
|
declare function rpc:build-error($info as map(*),$json as map(*))
|
|
as map(*){
|
|
{ "jsonrpc": "2.0", "id": $json?id, "error": $info }
|
|
};
|
|
|
|
(: 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 eq "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 :=(
|
|
rpc:admin-log($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()
|
|
{
|
|
rpc:admin-log($msg,"⬅️"),
|
|
ws:send($msg ,ws:id())
|
|
};
|
|
|
|
(:~ canned initialize response :)
|
|
declare
|
|
function rpc:method-initialize($json as map(*))
|
|
as map(*)
|
|
{
|
|
ws:set(ws:id(),"client", $json?params),
|
|
rpc:build-response($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(*)
|
|
{
|
|
rpc:build-notification(
|
|
"window/logMessage",
|
|
{"type":1, "message": $msg}
|
|
)
|
|
};
|
|
|
|
(:~ write to admin:log :)
|
|
declare function rpc:admin-log($data as item()*,$emoji as xs:string)
|
|
as empty-sequence()
|
|
{
|
|
let $msg:= json:serialize($data,{ 'format': 'w3', 'indent': 'no' })
|
|
return admin:write-log($emoji || $msg,"LSP")
|
|
};
|
|
|
|
|
|
(:~ 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()
|
|
}; |