[mod] symbol ranges

This commit is contained in:
Andy Bunce 2025-10-30 15:36:39 +00:00
parent 2d0ae19026
commit fc56a3d505
5 changed files with 52 additions and 39 deletions

View file

@ -5,7 +5,7 @@ module namespace hnd="lsp/handlers";
(:~ structure returned by tree walkers :) (:~ structure returned by tree walkers :)
declare record hnd:Result( declare record hnd:State(
result as item()*, result as item()*,
skipchildren? as xs:boolean:=false(), skipchildren? as xs:boolean:=false(),
extras? extras?
@ -13,23 +13,23 @@ declare record hnd:Result(
declare type hnd:actionFn as fn( declare type hnd:actionFn as fn(
$parse as element(), $parse as element(),
$state as hnd:Result $state as hnd:State
) as hnd:Result; ) as hnd:State;
declare type hnd:actionMap as map(xs:string,hnd:actionFn); declare type hnd:actionMap as map(xs:string,hnd:actionFn);
declare function hnd:walk($parse as element(), declare function hnd:walk($parse as element(),
$actions as hnd:actionMap, $actions as hnd:actionMap,
$state as hnd:Result ) $state as hnd:State )
as hnd:Result as hnd:State
{ {
let $action:=$actions(name($parse)) let $action:=$actions(name($parse))
let $result:= if(exists($action)) let $state:= if(exists($action))
then $action($parse,$state) then $action($parse,$state)
else hnd:Result($state) else $state
return if($result?skipchildren) return if($state?skipchildren)
then $result then $state
else fold-left( else fold-left(
$parse/*,$state, $parse/*,$state,
fn($state,$this){hnd:walk($this, $actions, $state)} fn($state,$this){hnd:walk($this, $actions, $state)}

View file

@ -35,7 +35,7 @@ function lsp-ws:message(
$message as xs:string $message as xs:string
) as empty-sequence() { ) as empty-sequence() {
let $json := rpc:parse($message=>trace("IN: ")) let $json := rpc:parse($message)
return if(exists($json)) return if(exists($json))
then rpc:reply($json) then rpc:reply($json)
else message($message,"bad RPC: ") else message($message,"bad RPC: ")

View file

@ -66,13 +66,13 @@ as lspt:Diagnostic*
(: test data :) (: test data :)
declare function lsp-diags:parse-xquery($text as xs:string, $parse as element(Module)) declare function lsp-diags:parse-xquery($text as xs:string, $parse as element(Module))
as map(*)*{ as map(*)*{
let $state:= hnd:Result(()) let $state:= hnd:State(())
let $result:= hnd:walk($parse,$lsp-diags:actions,$state) let $result:= hnd:walk($parse,$lsp-diags:actions,$state)
return $result?result return $result?result
}; };
declare function lsp-diags:Module($parse as element(Module),$state as hnd:Result ) declare function lsp-diags:Module($parse as element(Module),$state as hnd:State )
as hnd:Result{ as hnd:State{
let $new:=if(exists($parse/VersionDecl)=>trace("has ver: ")) let $new:=if(exists($parse/VersionDecl)=>trace("has ver: "))
then () then ()
else let $text:="dddjjjjjjjjjjjjjjjj" else let $text:="dddjjjjjjjjjjjjjjjj"
@ -86,7 +86,10 @@ as hnd:Result{
"No XQuery version declaration present", "No XQuery version declaration present",
"XQLT???") "XQLT???")
return ( 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: ") ,message(name($parse),"Module: ")
) )
}; };

View file

@ -15,9 +15,9 @@ declare variable $syms:actions as hnd:actionMap :={
declare function syms:list($parse as element(),$text as xs:string) declare function syms:list($parse as element(),$text as xs:string)
as lspt:DocumentSymbol*{ as lspt:DocumentSymbol*{
let $state:= hnd:Result(()) let $state:= hnd:State((),false(),{"text":$text})
let $result:= hnd:walk($parse,$syms:actions,$state) let $state:= hnd:walk($parse,$syms:actions,$state)
return $result?result return $state?result
}; };
(: for testing only:) (: for testing only:)
@ -33,37 +33,46 @@ as lspt:DocumentSymbol*{
}; };
(:~ just trace :) (:~ just trace :)
declare function syms:action($parse as element(),$state as hnd:Result ) declare function syms:action($parse as element(),$state as hnd:State )
as hnd:Result{ as hnd:State{
hnd:Result($state?result,true()) ,message(name($parse),"ACTION: ") $state ,message(name($parse),"ACTION: ")
}; };
declare function syms:VarDecl($parse as element(VarDecl),$state as hnd:Result ) declare function syms:VarDecl($parse as element(VarDecl),$state as hnd:State )
as hnd:Result{ as hnd:State{
let $name:=syms:localName($parse/VarNameAndType/EQName) let $name:=syms:localName($parse/VarNameAndType/EQName)
let $length:=string($parse)=>string-length() let $length:=string($parse)=>string-length()
let $range:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3)) let $range:=pos:range-from-ast($parse,$state?extras?text)=>trace("POSSSS")
let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Variable'),$range,$range,"VAR") let $full-range:=$range
return ($state?result,$sym)=>hnd:Result(true()) 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 ) declare function syms:FunctionDecl($parse as element(FunctionDecl),$state as hnd:State )
as hnd:Result{ as hnd:State{
let $name:=syms:localName($parse/UnreservedFunctionEQName) let $name:=syms:localName($parse/UnreservedFunctionEQName)
let $prev:=$state?result[$name eq ?name] 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 $full-range:=$range
let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Method'),$range,$full-range,"FUN") 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 ) declare function syms:NamedRecordTypeDecl($parse as element(NamedRecordTypeDecl), $state as hnd:State )
as hnd:Result{ as hnd:State{
let $name:=syms:localName($parse/EQName) let $name:=syms:localName($parse/EQName)
let $range:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3)) let $range:=pos:range-from-ast($parse,$state?extras?text)=>trace("POSSSS")
let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('TypeParameter'),$range,$range,"--RECORD") let $full-range:=$range
let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('TypeParameter'),$range,$full-range,"--RECORD")
=>trace("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 ) declare function syms:localName($name as xs:string )

View file

@ -3,8 +3,8 @@ const view = new lsp.EditorView({
parent: document.getElementById("editor") parent: document.getElementById("editor")
}); });
let doc = "xquery version '3.1';\n(:~ comment:)\nmodule namespace pdfbox='ns';\n"; let doc = "xquery version '3.1';\n(:~ comment:)\nmodule namespace pdfbox='ns';\n";
var client; var client; // https://codemirror.net/docs/ref/#lsp-client
var extLint; var extLsp; // https://codemirror.net/docs/ref/#lsp-client.LSPPlugin
var workspace = { var workspace = {
"file:///some/file.xqm": null "file:///some/file.xqm": null
}; };
@ -74,8 +74,9 @@ $("cmdList").onclick = e => {
$("symList").addEventListener("itemSelected", e => { $("symList").addEventListener("itemSelected", e => {
const sel = e.detail.selectionRange; const sel = e.detail.selectionRange;
console.log("SYM selection range", sel); console.log("SYM selection range", sel);
const an = 17 * e.detail.kind const an = extLsp.fromPosition(sel.start)
view.dispatch({ selection: { anchor: an, head: an + 34 }, scrollIntoView: true }); const hd = extLsp.fromPosition(sel.end)
view.dispatch({ selection: { anchor: an, head: hd}, scrollIntoView: true });
}); });
$("lint").onclick = async e => { $("lint").onclick = async e => {
@ -158,7 +159,7 @@ function connect() {
client.connect(transport); client.connect(transport);
$("popConnect").hidePopover(); $("popConnect").hidePopover();
$("tConnect").checked=true; $("tConnect").checked=true;
let extLsp = client.plugin(file, "xquery"); extLsp = client.plugin(file, "xquery");
view.dispatch({ view.dispatch({
effects: lsp.StateEffect.appendConfig.of( effects: lsp.StateEffect.appendConfig.of(