97 lines
No EOL
2.6 KiB
Text
97 lines
No EOL
2.6 KiB
Text
(:~
|
|
positions in text
|
|
:)
|
|
module namespace pos="lsp/position";
|
|
|
|
(:~ json numbers :)
|
|
declare type pos:num as (xs:integer|xs:double);
|
|
|
|
(:~
|
|
@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 pos:num,
|
|
character as pos:num
|
|
);
|
|
|
|
(:~
|
|
@param line Line position in a document (zero-based).
|
|
@param character Character offset on a line in a document (zero-based).
|
|
:)
|
|
declare record pos:Range(
|
|
start as pos:Position,
|
|
end as pos:Position
|
|
);
|
|
|
|
(:~ 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
|
|
};
|
|
|
|
(:~ find index from Position :)
|
|
declare function pos:resolvePosition($text as xs:string, $pos as pos:Position)
|
|
as pos: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 pos:num
|
|
)
|
|
as pos:Position {
|
|
let $nl:= if($index=>trace("IN ") ge string-length($text)=>trace("L "))
|
|
then error(xs:QName("pos:range"),"out of range")
|
|
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 pos:Position($line, $index - $off)
|
|
};
|
|
|
|
|
|
|
|
(:~ line number for $pos :)
|
|
declare function pos:lineAt($nl as xs:integer*,$pos as pos: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 pos:Position,$offset as xs:integer:=1)
|
|
{
|
|
`Ln { $pos?line + $offset}, Col { $pos?character + $offset}`
|
|
}; |