[fix] sending messages

This commit is contained in:
Andy Bunce 2025-07-26 14:21:07 +01:00
parent 0dfcbf17d8
commit c59edb71a2
6 changed files with 147 additions and 13 deletions

View file

@ -7,6 +7,8 @@ An attempt to write a language protocol server using BaseX features...
* https://github.com/mkslanc/ace-linters https://mkslanc.github.io/ace-linters/
* I needed `set NODE_OPTIONS=--max_old_space_size=8192` for build to complete
* or `node --max-old-space-size=8192 node_modules/webpack-dev-serve
r/bin/webpack-dev-server.js`
## notes
Using https://github.com/mkslanc/ace-linters https://mkslanc.github.io/ace-linters/

59
webapp/lsp/jsonrpc.xqm Normal file
View file

@ -0,0 +1,59 @@
(:~
: jsonrpc
: @author andy bunce
:)
module namespace lsprpc = 'lsprpc';
declare variable $lsprpc:methods:=map{
"initialize" : lsprpc:method-initialize#1,
"initialized" : lsprpc:method-unknown#1,
"workspace/didChangeConfiguration" :lsprpc:method-unknown#1,
"textDocument/didOpen": lsprpc:method-unknown#1,
"textDocument/didClose" : lsprpc:method-unknown#1
};
(:~ return map if $msg is jsonrpc else empty :)
declare
function lsprpc: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 replay to $json :)
declare
function lsprpc:reply($json as map(*))
as empty-sequence() {
let $method := $json?method
let $f :=$lsprpc:methods?($method)
let $response := $f!.($json)
return if(exists($response)) then ws:send($response=>trace("REPLY: "),ws:id())
};
(:~ canned initialize response :)
declare
function lsprpc:method-initialize($json as map(*))
as map(*)?
{
``[{"jsonrpc":"2.0","id":0,
"result":{"capabilities":{"textDocumentSync":2,"completionProvider":{"resolveProvider":false,"triggerCharacters":["\"",":"]},"hoverProvider":true,"documentSymbolProvider":true,"documentRangeFormattingProvider":false,"colorProvider":{},"foldingRangeProvider":true,"selectionRangeProvider":true,"documentLinkProvider":{}}}}]``
=>parse-json()
};
(:~ unknown method response :)
declare
function lsprpc:method-unknown($json as map(*))
as map(*)?
{
let $_:=trace($json?method,"unknown")
return ()
};

View file

@ -1,9 +1,10 @@
(:~
: Simple WebSocket chat. WebSocket functions.
: @author BaseX Team, BSD License
: WebSocket LSP
: @author Andy Bunce
:)
module namespace lsp-ws = 'lsp-ws';
import module namespace lsprpc = 'lsprpc' at 'jsonrpc.xqm';
import module namespace chat-util = 'chat/util' at 'lsp-util.xqm';
declare
@ -18,9 +19,8 @@ function lsp-ws:error($error) {
declare
%ws:connect('/lsp')
function lsp-ws:connect() as empty-sequence() {
let $_:=ws:id()=>trace("CONNECT2: ")
let $_:=session:get($chat-util:id)=>trace("CONNECTx: ")
return ws:set(ws:id()=>trace("CONNECT: "), $chat-util:id, "session:get($chat-util:id)")
ws:set(ws:id()=>trace("CONNECT: "), $chat-util:id, "session:get($chat-util:id)")
(: ,chat-util:users() :)
};
@ -33,15 +33,14 @@ declare
function lsp-ws:message(
$message as xs:string
) as empty-sequence() {
let $json := parse-json($message=>trace("MSG "))
let $type := $json?type
return if($type = 'message') then (
chat-util:message($json?text, $json?to)
) else if($type = 'ping') then(
(: do nothing :)
) else error()
let $json := lsprpc:parse($message=>trace("MSG "))
return if(exists($json))
then lsprpc:reply($json)
else message($message,"bad RPC: ")
};
(:~
: Closes a WebSocket connection. Unregisters the user and notifies all clients.
:)

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width,height=device-height" />
<title>BaseX LSP</title>
</head>
<body>
<div>something<button onclick="foo()">send</button><a href="/dba/logs" target="_blank">dba</a></div>
<div id="editor" style="height: 100px">some text</div>
<script type="module">
import * as ace from "https://www.unpkg.com/ace-code@latest/src-noconflict/ace.js";
import { Mode as JSONMode } from "ace-code/src/mode/json"; //any mode you want
import { AceLanguageClient } from "ace-linters/build/ace-language-client";
// Create a web socket
const serverData = {
module: () => import("ace-linters/build/language-client"),
modes: "json|json5",
type: "socket",
socket: new WebSocket("ws://127.0.0.1:3000/exampleServer"), // address of your websocket server
}
// Create an Ace editor
let editor = ace.edit("container", {
mode: new JSONMode() // Set the mode of the editor to JSON
});
// Create a language provider for web socket
let languageProvider = AceLanguageClient.for(serverData);
// Register the editor with the language provider
languageProvider.registerEditor(editor);
</script>
</body>
</html>

View file

@ -32,7 +32,7 @@ const serverData = {
module: () => import("https://www.unpkg.com/ace-linters@latest/build/language-client"),
modes: "json|json5",
type: "socket",
socket: new WebSocket("ws://127.0.0.1:3000/exampleServer"), // your websocket server address
socket: new WebSocket("ws://127.0.0.1:3000/ws/lsp"), // your websocket server address
}
function opts(editor) {

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width,height=device-height" />
<title>BaseX LSP</title>
</head>
<body>
<div>Socket <button onclick="foo()">send</button><a href="/dba/logs" target="_blank">dba</a></div>
<script>
var socket = new WebSocket("ws://127.0.0.1:3000/ws/lsp") // address of your websocket server
// Listen for possible errors
socket.addEventListener("error", (event) => {
console.log("WebSocket error: ", event);
});
socket.addEventListener("close", (event) => {
console.log("closed", event.code, event.reason, event.wasClean);
});
socket.addEventListener("open", (event) => {
setInterval(function ping() { socket.send('{"type":"ping","msg":"staying alive"}'); }, 100000);
socket.send('{"type":"ping","msg":"Hello Server!"}');
});
function foo() {
socket.send('{"type":"ping","msg":"foo!"}');
};
</script>
</body>
</html>