Compare commits

..

No commits in common. "2d0ae19026e17ed7157ee91038cb9527c69d3e67" and "c4d2770bae5b78dcf64635771f3184e4bd940f9f" have entirely different histories.

11 changed files with 48 additions and 171 deletions

View file

@ -1,20 +1,10 @@
import module namespace ast="lsp/ast" at "../webapp/lsp/ast/ast.xqm"; import module namespace syms="lsp/symbols" at "../webapp/lsp/ast/ast.xqm";
(: declare variable $file:="sample.docs/pdfbox.xqm"; :) declare $file:="C:\Users\mrwhe\git\quodatum\basex-lsp\test\sample.docs\pdfbox.xqm";
declare variable $file:="sample.docs/simple.xq";
declare variable $A:=doc("sample.docs/parse-pdfbox.xml"); declare variable $A:=doc("C:\Users\mrwhe\git\quodatum\basex-lsp\test\sample.docs\parse-pdfbox.xml");
$A update{
for $e in .//element()
unparsed-text($file)
=>ast:build()
update{
for $e in descendant-or-self::element()
let $len:=string-length($e) let $len:=string-length($e)
let $before:=$e/preceding-sibling::node()/string-length()=>sum() return insert node attribute len { $len } into $e
return (insert node attribute len { $len } into $e, }
insert node attribute before { $before } into $e
)
}

View file

@ -1,6 +0,0 @@
(: simple parse test :)
declare function local:print($src as xs:string, $dest as xs:string)
{
concat($src,"->", $dest)
};
local:print("sss","bbb")

View file

@ -5,23 +5,14 @@ module namespace ast="lsp/ast";
(: import module namespace p="xq4" at "xq4.xqm"; :) (: import module namespace p="xq4" at "xq4.xqm"; :)
import module namespace xq4="java:quodatum.parser.xq4"; import module namespace xq4="java:quodatum.parser.xq4";
(:~ build (:~ build :)
$opts uri, declare function ast:build($text as xs:string,$uri as xs:string:="")
abstract:true(),
position: true()
:)
declare function ast:build($text as xs:string,$opts as map(*):={})
as element(*){ as element(*){
let $xml:= xq4:parseModule($text)=>prof:time("⏱️ parseModule " || $opts?uri) let $xml:= xq4:parseModule($text)=>prof:time("⏱️ p:parse-Module " || $uri)
let $res:= if($opts?abstract) return ($xml,
then ast:flatten($xml)=>prof:time("⏱️ abstract ") message(ast:report($xml),"REP"),
else $xml message(ast:report(ast:flatten($xml)),"flat")
)
let $res:=if($opts?position)
then ast:annotate-with-positions($res)=>prof:time("⏱️ position ")
else $res
return $res
}; };
(:~ (:~
@ -40,82 +31,7 @@ declare function ast:flatten($input as element()) as element() {
} }
}; };
declare function ast:add-positions($nodes as node()*, $start-pos as xs:integer) as item()+ { (:-------------------------------------------:)
if (empty($nodes)) then (
$start-pos, ()
) else (
let $head := $nodes[1]
let $tail := $nodes[position() > 1]
return
typeswitch($head)
case element() return
let $children := $head/node()
let $child-result := ast:add-positions($children, $start-pos)
let $child-end-pos := $child-result[1]
let $processed-children := subsequence($child-result, 2)
let $element-text := string-join($processed-children ! string(.), "")
let $element-length := string-length($element-text)
let $element-end-pos :=
if ($element-length > 0) then $start-pos + $element-length - 1
else $start-pos
let $tail-result :=
if (exists($tail)) then
ast:add-positions($tail, $element-end-pos + 1)
else (
$element-end-pos + 1, ()
)
let $final-end-pos := $tail-result[1]
let $processed-tail := subsequence($tail-result, 2)
return (
$final-end-pos,
element {node-name($head)} {
attribute start {$start-pos},
attribute end {$element-end-pos},
$processed-children
},
$processed-tail
)
case text() return
let $text-length := string-length($head)
let $text-end-pos :=
if ($text-length > 0) then $start-pos + $text-length - 1
else $start-pos
let $tail-result :=
if (exists($tail)) then
ast:add-positions($tail, $text-end-pos + 1)
else (
$text-end-pos + 1, ()
)
let $final-end-pos := $tail-result[1]
let $processed-tail := subsequence($tail-result, 2)
return (
$final-end-pos,
$head,
$processed-tail
)
default return
let $tail-result :=
if (exists($tail)) then
ast:add-positions($tail, $start-pos)
else (
$start-pos, ()
)
let $final-end-pos := $tail-result[1]
let $processed-tail := subsequence($tail-result, 2)
return (
$final-end-pos,
$head,
$processed-tail
)
)
};
declare function ast:annotate-with-positions($xml as element()) as element() {
let $result := ast:add-positions($xml, 1)
return $result[2]
};
(:-------reporting------------------------------------:)
declare function ast:report($el as element(*)) { declare function ast:report($el as element(*)) {
{ {

View file

@ -118,7 +118,7 @@ declare function docs:parse(
)as map(*)? )as map(*)?
{ {
let $text:=docs:get($socket,$file,"textDocument")?text let $text:=docs:get($socket,$file,"textDocument")?text
let $xml:= ast:build($text, {"uri": $file, "abstract":false(), "position":true()}) let $xml:= ast:build($text, $file)
let $diags:=lsp-diags:list($file, $text, $xml)=>prof:time("⏱️ diags " || $file) let $diags:=lsp-diags:list($file, $text, $xml)=>prof:time("⏱️ diags " || $file)
return ( return (
ws:set($socket,docs:key($socket,$file,"parse"),$xml), ws:set($socket,docs:key($socket,$file,"parse"),$xml),

View file

@ -7,14 +7,10 @@ module namespace hnd="lsp/handlers";
(:~ structure returned by tree walkers :) (:~ structure returned by tree walkers :)
declare record hnd:Result( declare record hnd:Result(
result as item()*, result as item()*,
skipchildren? as xs:boolean:=false(), skipchildren? as xs:boolean:=false()
extras?
); );
declare type hnd:actionFn as fn( declare type hnd:actionFn as fn($parse as element(),$state as hnd:Result ) as hnd:Result;
$parse as element(),
$state as hnd:Result
) as hnd:Result;
declare type hnd:actionMap as map(xs:string,hnd:actionFn); declare type hnd:actionMap as map(xs:string,hnd:actionFn);

View file

@ -70,8 +70,7 @@ declare
function rpc:send($msg as map(*)) function rpc:send($msg as map(*))
as empty-sequence() as empty-sequence()
{ {
rpc:admin-log($msg,"⬅️"), rpc:admin-log($msg,"⬅️"),ws:send($msg ,ws:id())
ws:send($msg ,ws:id())
}; };
(:~ canned initialize response :) (:~ canned initialize response :)

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

@ -84,14 +84,3 @@ declare function pos:full-range($text as xs:string)
as lspt:Range{ as lspt:Range{
lspt:Range(pos:toPosition($text,0), pos:toPosition($text, string-length($text))) lspt:Range(pos:toPosition($text,0), pos:toPosition($text, string-length($text)))
}; };
(:~ range for $text from indices:)
declare function pos:range-from-ast(
$ast as element(*),
$text as xs:string)
as lspt:Range{
lspt:Range(
pos:toPosition($text,number($ast/@start)),
pos:toPosition($text, number($ast/@end))
)
};

View file

@ -51,9 +51,8 @@ declare function syms:FunctionDecl($parse as element(FunctionDecl),$state as hn
as hnd:Result{ as hnd:Result{
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:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3))
let $full-range:=$range let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Method'),$range,$range,"FUN")
let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Method'),$range,$full-range,"FUN")
return ($state?result,$sym)=>hnd:Result(true()) return ($state?result,$sym)=>hnd:Result(true())
}; };

View file

@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="en" class="quiet-cloak "> <!-- also quiet-dark --> <html lang="en" class="quiet-cloak">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -8,14 +8,8 @@
<link rel="icon" type="image/png" href="../favicon.png" /> <link rel="icon" type="image/png" href="../favicon.png" />
<!-- Quiet theme + autoloader --> <!-- Quiet theme + autoloader -->
<!-- Default theme (if not already installed) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@quietui/quiet-browser@1.4.0/dist/themes/quiet.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@quietui/quiet-browser@1.6.1/dist/themes/quiet.css"> <script type="module" src="https://cdn.jsdelivr.net/npm/@quietui/quiet-browser@1.4.0/dist/quiet.loader.js"></script>
<!-- Quiet Restyle
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@quietui/quiet-browser@1.6.1/dist/themes/restyle.css">
-->
<script type="module" src="https://cdn.jsdelivr.net/npm/@quietui/quiet-browser@1.6.1/dist/quiet.loader.js"></script>
<link rel="stylesheet" href="grail.css" /> <link rel="stylesheet" href="grail.css" />
@ -33,7 +27,7 @@
<quiet-icon slot="checked" name="network" family="outline"></quiet-icon> <quiet-icon slot="checked" name="network" family="outline"></quiet-icon>
</quiet-toggle-icon> </quiet-toggle-icon>
<quiet-toolbar label="Links"> <quiet-button-group label="Links">
<quiet-button href="#" variant="primary" aria-current="page" size="sm"> <quiet-button href="#" variant="primary" aria-current="page" size="sm">
Editor Editor
</quiet-button> </quiet-button>
@ -43,8 +37,8 @@
<quiet-button href="/dba/logs" target="dba" rel="noreferrer noopener" size="sm"> <quiet-button href="/dba/logs" target="dba" rel="noreferrer noopener" size="sm">
Dba Dba
</quiet-button> </quiet-button>
</quiet-toolbar> </quiet-button-group>
<quiet-select id="load" style="width:20em;"> <select id="load">
<option selected value="">load..</option> <option selected value="">load..</option>
<optgroup label="XQuery3"> <optgroup label="XQuery3">
<option <option
@ -67,7 +61,7 @@
value="https://raw.githubusercontent.com/dnovatchev/Articles/refs/heads/main/Generators/Code/generator.xq"> value="https://raw.githubusercontent.com/dnovatchev/Articles/refs/heads/main/Generators/Code/generator.xq">
generator.xquery</option> generator.xquery</option>
</optgroup> </optgroup>
</quiet-select> </select>
<button popovertarget="popAbout" type="button"> <button popovertarget="popAbout" type="button">
<i class="codicon codicon-info"></i> <i class="codicon codicon-info"></i>
@ -77,7 +71,12 @@
<main class="page-main" style="overflow: auto;"> <main class="page-main" style="overflow: auto;">
<quiet-toolbar> <quiet-toolbar>
<quiet-button-group>
<label for="file">File:</label>
<input id="iFile" type="url" value="file:///some/file.xqm" />
<label for="symbols">Symbols:</label><select id="symbols" disabled="disabled"></select>
</quiet-button-group>
<quiet-button-group> <quiet-button-group>
<button id="search" title="Search" type="button" class="btn btn-light"><i <button id="search" title="Search" type="button" class="btn btn-light"><i
@ -156,31 +155,25 @@
</aside> </aside>
<footer class="page-footer"> <footer class="page-footer">
<div style="display:flex;">
<select id="language" style="width:10em;display:inline-block;">
<option selected>Language</option>
<option value="plaintext">plaintext</option>
<option value="xquery">xquery</option>
<option value="xml">xml</option>
</select>
<div>
<label for="iFile">File:</label>
<input id="iFile" type="url" value="file:///some/file.xqm" style="width:10em;display:inline-block;" />
<label for="symbols">Symbols:</label> <select id="language">
<select id="symbols" disabled="disabled" style="width:10em;display:inline-block;"></select> <option selected>Language</option>
</div> <option value="plaintext">plaintext</option>
</div> <option value="xquery">xquery</option>
<option value="xml">xml</option>
</select>
<quiet-relative-time live id="relative-time__live" style="width:10em;"></quiet-relative-time> <quiet-relative-time live id="relative-time__live" style="width:10em;"></quiet-relative-time>
<quiet-dropdown id="dropdown__checkboxes"> <quiet-dropdown id="dropdown__checkboxes">
<quiet-button slot="trigger"><i class='codicon codicon-kebab-vertical'></i></quiet-button> <quiet-button slot="trigger"><i class='codicon codicon-kebab-vertical'></i></quiet-button>
<quiet-dropdown-item type="checkbox" value="canvas" checked>Follow cursor</quiet-dropdown-item> <quiet-dropdown-item type="checkbox" value="canvas" checked>Follow cursor</quiet-dropdown-item>
<quiet-divider></quiet-divider> <quiet-divider></quiet-divider>
<quiet-dropdown-item type="checkbox" value="position" checked>sort by: Position</quiet-dropdown-item> <quiet-radio orientation="vertical" name="sortBy">
<quiet-dropdown-item type="checkbox" value="name">sort by: Name</quiet-dropdown-item> <quiet-radio-item value="position">sort by: position</quiet-radio-item>
<quiet-dropdown-item type="checkbox" value="category">sort by: Category</quiet-dropdown-item> <quiet-radio-item value="name">sort by: name</quiet-radio-item>
<quiet-radio-item value="category">sort by: category</quiet-radio-item>
</quiet-radio>
</quiet-dropdown> </quiet-dropdown>

View file

@ -151,7 +151,7 @@ function connect() {
const file = $("iFile").value; const file = $("iFile").value;
lsp.simpleWebSocketTransport(server) lsp.simpleWebSocketTransport(server)
.then(transport => { .then(transport => {
transport.socket.onclose = (event) => $("tConnect").checked=false; transport.socket.onclose = (event) => connectStatus(false);
transport.socket.oneror = (event) => $("msg").innerText = "sock error!"; transport.socket.oneror = (event) => $("msg").innerText = "sock error!";
transport.subscribe(incoming); transport.subscribe(incoming);
client = new lsp.LSPClient({ extensions: lsp.languageServerExtensions() }); client = new lsp.LSPClient({ extensions: lsp.languageServerExtensions() });
@ -169,6 +169,7 @@ function connect() {
.catch(e => { .catch(e => {
console.log(e); console.log(e);
$("tConnect").checked=false; $("tConnect").checked=false;
connectStatus(false);
alert("connection failed: " + server) alert("connection failed: " + server)
}); });