diff --git a/test/didchange.xq b/test/didchange.xq index b18fe38..e0a3c66 100644 --- a/test/didchange.xq +++ b/test/didchange.xq @@ -1 +1,14 @@ -json:doc("sample.msg/didChange/client2.json",{"format":"w3"}) +import module namespace pos="lsp/position" at "../webapp/lsp/position.xqm"; + +declare variable $msg:=json:doc("sample.msg/didChange/client2.json",{"format":"w3"}); +declare variable $text:=`12345678 +bbbbb +cccccc`; + + + + + +(: return pos:resolvePosition($text,$p) :) + pos:apply-changes($text,$msg?params?contentChanges) +(: $msg?params?contentChanges :) \ No newline at end of file diff --git a/webapp/lsp/docs.xqm b/webapp/lsp/docs.xqm index e0a59b1..a36fd45 100644 --- a/webapp/lsp/docs.xqm +++ b/webapp/lsp/docs.xqm @@ -5,15 +5,8 @@ :) module namespace docs="lsp/docs"; import module namespace p="xq4" at "xq4.xqm"; +import module namespace pos="lsp/position" at "position.xqm"; -(:~ -@param line Line position in a document (zero-based). -@param character Character offset on a line in a document (zero-based). -:) -declare record docs:Position( - line as xs:integer, - character as xs:integer -); (: document info :) declare type docs:property as enum( @@ -58,29 +51,3 @@ return ( ) }; -declare function docs:resolvePosition($text as xs:string, $pos as docs:Position) -as xs:integer -{ - (: let line = 0, off = 0 - while (line < pos.line) { - let next = text.indexOf("\n", off) - if (!next) throw new RangeError("Position out of bounds") - off = next + 1 - line++ - } - off += pos.character - if (off > string-length($text)) throw new RangeError("Position out of bounds") - return off :) - 0 -}; - -declare function docs:toPosition($text as xs:string, $pos as xs:integer) -as docs:Position { - (: for (let off = 0, line = 0;;) { - let next = text.indexOf("\n", off) - if (next < 0 || next >= pos) return {line, character: pos - off} - off = next + 1 - line++ - } :) - docs:Position(0,0) -}; \ No newline at end of file diff --git a/webapp/lsp/parse.xq b/webapp/lsp/parse.xq index eeae300..9dc8ce4 100644 --- a/webapp/lsp/parse.xq +++ b/webapp/lsp/parse.xq @@ -5,10 +5,10 @@ import module namespace docs="lsp/docs" at "docs.xqm"; declare variable $params as map(*) external; declare variable $webSocket as xs:string external; -prof:time(docs:save( +docs:save( $webSocket, $params -),"⏱️ doc:save "), +)=>prof:time("⏱️ doc:save "), ws:send( {"jsonrpc": "2.0", diff --git a/webapp/lsp/position.xqm b/webapp/lsp/position.xqm new file mode 100644 index 0000000..47d0eb5 --- /dev/null +++ b/webapp/lsp/position.xqm @@ -0,0 +1,67 @@ +(:~ +text editing API +:) +module namespace pos="lsp/position"; + +(:~ +@param line Line position in a document (zero-based). +@param character Character offset on a line in a document (zero-based). +:) +declare record pos:Position( + line as xs:integer, + character as xs:integer +); + +declare function pos:apply-changes($text as xs:string, $chs as array(*)) +as xs:string{ +array:fold-left($chs,$text,pos:apply-change#2) +}; + +(:~ text updated with $ch changes from didChange :) +declare function pos:apply-change($text as xs:string, $ch as map(*)) +as xs:string{ + if(exists($ch?range)) + then substring($text,1, pos:resolvePosition($text, $ch?range?start)) + || $ch?text || + substring($text,pos:resolvePosition($text, $ch?range?end)) + else $ch?text +}; + +(:~ find index from Position :) +declare function pos:resolvePosition($text as xs:string, $pos as map(*)) +as xs:integer +{ + let $nl:=characters($text)=>index-of(char("\n")) + let $s:= while-do( + map{"off":0,"line":0}, + fn($r, $i){$r?line < $pos?line}, + fn($r,$i){ let $next:=$nl[$i] + return if(empty($next)) + then error(xs:QName("docs:range"),"bad line") + else {"off": $next+1,"line":$r?line+1}=>trace("AA ") } +) + let $off:= $s?off+$pos?character + return if($off>string-length($text)) + then error(xs:QName("docs:range"),"bad offset") + else xs:integer($off) + +}; + +(:~ convert index into Position :) +declare function pos:toPosition($text as xs:string, $pos as xs:integer) +as pos:Position { + let $nl:=characters($text)=>index-of(char("\n")) + let $predicate:=fn($r, $i){ + let $next:=$nl[$i=>trace("I ")] + return exists($next) and $next < $pos +} + + let $s:= while-do( + map{"off":0,"line":0}, + $predicate, + fn($r,$i){ let $next:=$nl[$i] return {"off": $next+1,"line":$r?line+1}=>trace("AA ") } +) +return pos:Position($s?line, $pos - $s?off) +}; + +