module namespace lsp-diags = 'lsp-diags';
(:~ codemirror 
from: number The start position of the relevant text.
to: number The end position. May be equal to from, though actually covering text is preferable.
severity: "error" | "hint" | "info" | "warning" The severity of the problem. This will influence how it is displayed.
markClass⁠?: string When given, add an extra CSS class to parts of the code that this diagnostic applies to.
source⁠?: string An optional source string indicating where the diagnostic is coming from. You can put the name of your linter here, if applicable.
message: string The message associated with this diagnostic.
renderMessage⁠?: fn(view: EditorView) → Node An optional custom rendering function that displays the message as a DOM node.
actions⁠?: readonly Action[] An optional array of actions that can be taken on this diagnostic.
:)
import module namespace lspt = 'lsp-typedefs' at "../lsp-typedefs.xqm";
import module namespace hnd="lsp/handlers" at "../handlers.xqm";
import module namespace pos="lsp/position" at "../position.xqm";

declare type lsp-diags:ParseResult as element(Module|ERROR);

declare variable $lsp-diags:actions as hnd:actionMap :={
      "Module":     lsp-diags:Module#2,
      "ModuleDecl": lsp-diags:ModuleDecl#2
};

declare function lsp-diags:list(
    $uri as xs:string,
    $text as xs:string,
    $xml as lsp-diags:ParseResult)
as lspt:Diagnostic*{
    if($xml/self::ERROR)
    then lsp-diags:parse-error($text, $xml)
    else  lsp-diags:parse-xquery($text,$xml)
};

(:~ 
<ERROR b="10819" e="10820" o="234" s="43">syntax error, found '}' while expecting [S,'else'] at line 290, column 'XPST0003', 'parse error'
'XQLT0001', 'previous parse error'
:)
declare function lsp-diags:parse-error($text as xs:string, $xml as element(ERROR))
as lspt:Diagnostic*
{ 
    if(string-length($text) eq 0)
    then ()
    else  let $last:= string-length($text)-1
          let $dmesg:=$xml/string()=>trace("parse-error")
          let $dmesg:=translate($dmesg,"&#10;",";")
          let $b:=number($xml/@b)-1
          let $e:= number($xml/@e)-1
          return (
              (: mark error :)
              lspt:Diagnostic(lspt:Range(pos:toPosition($text, $b), 
                                         pos:toPosition($text, $e)
                                         ),
                      1,
                      $dmesg,'XPST0003'),
    
              (:mark after error:)            
              if($e ge string-length($text))
              then ()               
              else lspt:Diagnostic(lspt:Range(pos:toPosition($text, $e +1 ), 
                                              pos:toPosition($text, $last)
                                              ),
                          2,
                          "Unparsed due to previous parser error.",
                          "XQLT0001")
          )
};

(: walk in tree :)
declare function lsp-diags:parse-xquery($text as xs:string, $parse as element(Module))
as lspt:Diagnostic*{
  let $state:= hnd:State((),false(),{"text":$text})
  let $state:= hnd:walk($parse,$lsp-diags:actions,$state)
  return $state?result
};

declare function lsp-diags:Module($parse  as element(Module),$state as hnd:State  )
as hnd:State{
  let $new:=if(exists($parse/VersionDecl))
            then ()
            else let $text:=$state?extras?text
                 
                 return lspt:Diagnostic(lspt:Range(
                                    pos:toPosition($text, 0 ), 
                                    pos:toPosition($text, 0)
                                    ),
                            $lspt:SeverityKind?info,
                            "No XQuery version declaration present",
                            "XQLT???")
  return (
            $state
            =>map:put("result",($state?result,$new=>trace(" VER ")))
            =>map:put("skipchildren",false())
   
   ,message(name($parse),"Module: ")
   ) 
};


declare function lsp-diags:ModuleDecl($parse  as element(ModuleDecl),$state as hnd:State  )
as hnd:State{
  (: ModuleDecl ::= 'module' 'namespace' NCName '=' URILiteral ';':)
  let $mod:=$parse/NCName/string()=>trace("pre")
  let $extras:=$state?extras=>map:put("mod",$mod)
  return $state
            =>map:put("extras",$extras)
            =>map:put("skipchildren",true())
};