From fc56a3d505676c020f773c8a47f8bf16e57ce9c8 Mon Sep 17 00:00:00 2001 From: Andy Bunce Date: Thu, 30 Oct 2025 15:36:39 +0000 Subject: [PATCH] [mod] symbol ranges --- webapp/lsp/handlers.xqm | 18 ++++---- webapp/lsp/lsp-ws.xqm | 2 +- webapp/lsp/providers/diagnostics.xqm | 11 +++-- webapp/lsp/providers/documentSymbols.xqm | 49 +++++++++++++--------- webapp/static/clients/codemirror/script.js | 11 ++--- 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/webapp/lsp/handlers.xqm b/webapp/lsp/handlers.xqm index 74dcd8d..3200397 100644 --- a/webapp/lsp/handlers.xqm +++ b/webapp/lsp/handlers.xqm @@ -5,7 +5,7 @@ module namespace hnd="lsp/handlers"; (:~ structure returned by tree walkers :) -declare record hnd:Result( +declare record hnd:State( result as item()*, skipchildren? as xs:boolean:=false(), extras? @@ -13,23 +13,23 @@ declare record hnd:Result( declare type hnd:actionFn as fn( $parse as element(), - $state as hnd:Result -) as hnd:Result; + $state as hnd:State +) as hnd:State; declare type hnd:actionMap as map(xs:string,hnd:actionFn); declare function hnd:walk($parse as element(), $actions as hnd:actionMap, - $state as hnd:Result ) -as hnd:Result + $state as hnd:State ) +as hnd:State { let $action:=$actions(name($parse)) - let $result:= if(exists($action)) + let $state:= if(exists($action)) then $action($parse,$state) - else hnd:Result($state) + else $state - return if($result?skipchildren) - then $result + return if($state?skipchildren) + then $state else fold-left( $parse/*,$state, fn($state,$this){hnd:walk($this, $actions, $state)} diff --git a/webapp/lsp/lsp-ws.xqm b/webapp/lsp/lsp-ws.xqm index 312e6bc..378af2d 100644 --- a/webapp/lsp/lsp-ws.xqm +++ b/webapp/lsp/lsp-ws.xqm @@ -35,7 +35,7 @@ function lsp-ws:message( $message as xs:string ) as empty-sequence() { - let $json := rpc:parse($message=>trace("IN: ")) + let $json := rpc:parse($message) return if(exists($json)) then rpc:reply($json) else message($message,"bad RPC: ") diff --git a/webapp/lsp/providers/diagnostics.xqm b/webapp/lsp/providers/diagnostics.xqm index 6c3d906..1e51617 100644 --- a/webapp/lsp/providers/diagnostics.xqm +++ b/webapp/lsp/providers/diagnostics.xqm @@ -66,13 +66,13 @@ as lspt:Diagnostic* (: test data :) declare function lsp-diags:parse-xquery($text as xs:string, $parse as element(Module)) as map(*)*{ - let $state:= hnd:Result(()) + let $state:= hnd:State(()) let $result:= hnd:walk($parse,$lsp-diags:actions,$state) return $result?result }; -declare function lsp-diags:Module($parse as element(Module),$state as hnd:Result ) -as hnd:Result{ +declare function lsp-diags:Module($parse as element(Module),$state as hnd:State ) +as hnd:State{ let $new:=if(exists($parse/VersionDecl)=>trace("has ver: ")) then () else let $text:="dddjjjjjjjjjjjjjjjj" @@ -86,7 +86,10 @@ as hnd:Result{ "No XQuery version declaration present", "XQLT???") return ( - hnd:Result(($state?result,$new=>trace(" VER ")),true()) + $state + =>map:put("result",($state?result,$new=>trace(" VER "))) + =>map:put("skipchildren",true()) + ,message(name($parse),"Module: ") ) }; \ No newline at end of file diff --git a/webapp/lsp/providers/documentSymbols.xqm b/webapp/lsp/providers/documentSymbols.xqm index 1430bd4..f7a83dc 100644 --- a/webapp/lsp/providers/documentSymbols.xqm +++ b/webapp/lsp/providers/documentSymbols.xqm @@ -15,9 +15,9 @@ declare variable $syms:actions as hnd:actionMap :={ declare function syms:list($parse as element(),$text as xs:string) as lspt:DocumentSymbol*{ - let $state:= hnd:Result(()) - let $result:= hnd:walk($parse,$syms:actions,$state) - return $result?result + let $state:= hnd:State((),false(),{"text":$text}) + let $state:= hnd:walk($parse,$syms:actions,$state) + return $state?result }; (: for testing only:) @@ -33,37 +33,46 @@ as lspt:DocumentSymbol*{ }; (:~ just trace :) -declare function syms:action($parse as element(),$state as hnd:Result ) -as hnd:Result{ - hnd:Result($state?result,true()) ,message(name($parse),"ACTION: ") +declare function syms:action($parse as element(),$state as hnd:State ) +as hnd:State{ + $state ,message(name($parse),"ACTION: ") }; -declare function syms:VarDecl($parse as element(VarDecl),$state as hnd:Result ) -as hnd:Result{ +declare function syms:VarDecl($parse as element(VarDecl),$state as hnd:State ) +as hnd:State{ let $name:=syms:localName($parse/VarNameAndType/EQName) let $length:=string($parse)=>string-length() - let $range:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3)) - let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Variable'),$range,$range,"VAR") - return ($state?result,$sym)=>hnd:Result(true()) + let $range:=pos:range-from-ast($parse,$state?extras?text)=>trace("POSSSS") + let $full-range:=$range + let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Variable'),$range,$full-range,"VAR") + return $state + =>map:put("result",($state?result,$sym)) + =>map:put("skipchildren",true()) + }; -declare function syms:FunctionDecl($parse as element(FunctionDecl),$state as hnd:Result ) -as hnd:Result{ +declare function syms:FunctionDecl($parse as element(FunctionDecl),$state as hnd:State ) +as hnd:State{ let $name:=syms:localName($parse/UnreservedFunctionEQName) let $prev:=$state?result[$name eq ?name] - let $range:=pos:range-from-ast($parse,$state?context?text)=>trace("POSSSS") + let $range:=pos:range-from-ast($parse,$state?extras?text)=>trace("POSSSS") let $full-range:=$range let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Method'),$range,$full-range,"FUN") - return ($state?result,$sym)=>hnd:Result(true()) + return $state + =>map:put("result",($state?result,$sym)) + =>map:put("skipchildren",true()) }; -declare function syms:NamedRecordTypeDecl($parse as element(NamedRecordTypeDecl), $state as hnd:Result ) -as hnd:Result{ +declare function syms:NamedRecordTypeDecl($parse as element(NamedRecordTypeDecl), $state as hnd:State ) +as hnd:State{ let $name:=syms:localName($parse/EQName) - let $range:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3)) - let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('TypeParameter'),$range,$range,"--RECORD") +let $range:=pos:range-from-ast($parse,$state?extras?text)=>trace("POSSSS") + let $full-range:=$range + let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('TypeParameter'),$range,$full-range,"--RECORD") =>trace("RECORD") - return ($state?result,$sym)=>hnd:Result(true()) + return $state + =>map:put("result",($state?result,$sym)) + =>map:put("skipchildren",true()) }; declare function syms:localName($name as xs:string ) diff --git a/webapp/static/clients/codemirror/script.js b/webapp/static/clients/codemirror/script.js index c3361f1..3f1d3ae 100644 --- a/webapp/static/clients/codemirror/script.js +++ b/webapp/static/clients/codemirror/script.js @@ -3,8 +3,8 @@ const view = new lsp.EditorView({ parent: document.getElementById("editor") }); let doc = "xquery version '3.1';\n(:~ comment:)\nmodule namespace pdfbox='ns';\n"; -var client; -var extLint; +var client; // https://codemirror.net/docs/ref/#lsp-client +var extLsp; // https://codemirror.net/docs/ref/#lsp-client.LSPPlugin var workspace = { "file:///some/file.xqm": null }; @@ -74,8 +74,9 @@ $("cmdList").onclick = e => { $("symList").addEventListener("itemSelected", e => { const sel = e.detail.selectionRange; console.log("SYM selection range", sel); - const an = 17 * e.detail.kind - view.dispatch({ selection: { anchor: an, head: an + 34 }, scrollIntoView: true }); + const an = extLsp.fromPosition(sel.start) + const hd = extLsp.fromPosition(sel.end) + view.dispatch({ selection: { anchor: an, head: hd}, scrollIntoView: true }); }); $("lint").onclick = async e => { @@ -158,7 +159,7 @@ function connect() { client.connect(transport); $("popConnect").hidePopover(); $("tConnect").checked=true; - let extLsp = client.plugin(file, "xquery"); + extLsp = client.plugin(file, "xquery"); view.dispatch({ effects: lsp.StateEffect.appendConfig.of(