(:~ positions in text :) module namespace pos="lsp/position"; import module namespace lspt = 'lsp-typedefs' at "lsp-typedefs.xqm"; (:~ find index from Position :) declare function pos:resolvePosition($text as xs:string, $pos as lspt:Position) as lspt:num { let $nl:= index-of(string-to-codepoints($text),10) let $off:=if($pos?line eq 0) then 0 else $nl[$pos?line] return $off+$pos?character }; (:~ convert index into Position :) declare function pos:toPosition($text as xs:string, $index as lspt:num ) as lspt:Position { let $nl:= if($index=>trace("IN ") gt string-length($text)=>trace("L ")) then error(xs:QName("pos:range"),`out of range: index={$index},length={string-length($text)}`) else index-of(string-to-codepoints($text),10) let $line:=pos:lineAt($nl,$index) let $off:=if($line eq 0) then 0 else $nl[$line] return lspt:Position($line, $index - $off) }; (:~ line number for $pos :) declare function pos:lineAt($nl as xs:integer*,$pos as lspt:num) as xs:integer { if(empty($nl) or $pos le head($nl)) then 0 else if($pos gt foot($nl)) then count($nl) else let $s:=do-until( {"min":1,"max":count($nl)}, fn($r){ let $mid:=round(($r?min+$r?max) div 2,0,"away-from-zero") return if ($nl[$mid] lt $pos) then map:put($r,"min",$mid) else map:put($r,"max",$mid -1) }, fn($r){$r?max eq $r?min} ) return $s?max }; (:~ format position for text display :) declare function pos:ln-col($pos as lspt:Position,$offset as xs:integer:=1) { `Ln { $pos?line + $offset}, Col { $pos?character + $offset}` }; (:~ update $text with changes $chs from didChange :) 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 single change :) 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 }; (:~ full range for $text :) declare function pos:full-range($text as xs:string) as lspt:Range{ lspt:Range(pos:toPosition($text,0), pos:toPosition($text, string-length($text))) };