diff --git a/compose.yaml b/compose.yaml index 1162664..d2f366c 100644 --- a/compose.yaml +++ b/compose.yaml @@ -13,7 +13,7 @@ services: - ./webapp/custom:/srv/basex/lib/custom # jars - ./webapp/lsp:/srv/basex/webapp/lsp - ./webapp/static/clients:/srv/basex/webapp/static/clients - + - ./webapp/lsp-manager:/srv/basex/webapp/lsp-manager # - ./repo:/srv/basex/repo environment: - "BASEX_JVM= -Dorg.basex.RESTXQERRORS=false -Dorg.basex.LOGEXCLUDE=/dba" diff --git a/docs/wordat.xqbk b/docs/wordat.xqbk index 50eff76..2fda00f 100644 --- a/docs/wordat.xqbk +++ b/docs/wordat.xqbk @@ -1 +1 @@ -{"cells":[{"kind":1,"language":"markdown","value":"XPath name charactors"},{"kind":2,"language":"xquery","value":"let $s:=`[A-Z]\r\n | '_'\r\n | [a-z]\r\n | [#xC0-#xD6]\r\n | [#xD8-#xF6]\r\n | [#xF8-#x2FF]\r\n | [#x370-#x37D]\r\n | [#x37F-#x1FFF]\r\n | [#x200C-#x200D]\r\n | [#x2070-#x218F]\r\n | [#x2C00-#x2FEF]\r\n | [#x3001-#xD7FF]\r\n | [#xF900-#xFDCF]\r\n | [#xFDF0-#xFFFD]\r\n `\r\nlet $sreg:= normalize-space($s)\r\n =>tokenize(\"\\|\")\r\n =!>normalize-space()\r\n =!>replace(\"(#x[0-9A-F]+)\",fn($s,$m){\r\n convert:integer-from-base(substring($m,3),16)\r\n =>codepoints-to-string()\r\n })\r\n =!>translate(\"'\",\"\")\r\n =>string-join(\"|\")\r\nreturn $sreg"},{"kind":1,"language":"markdown","value":"# Functions"},{"kind":2,"language":"xquery","value":"declare namespace fos=\"http://www.w3.org/xpath-functions/spec/namespace\";\r\ndoc(\"C:/Users/mrwhe/git/quodatum/basex-lsp/bundles/grammar/function-catalog.xml\")\r\n//fos:function[@prefix=\"fn\"]/@name"}]} \ No newline at end of file +{"cells":[{"kind":1,"language":"markdown","value":"XPath name charactors"},{"kind":2,"language":"xquery","value":"let $s:=`[A-Z]\r\n | '_'\r\n | [a-z]\r\n | [#xC0-#xD6]\r\n | [#xD8-#xF6]\r\n | [#xF8-#x2FF]\r\n | [#x370-#x37D]\r\n | [#x37F-#x1FFF]\r\n | [#x200C-#x200D]\r\n | [#x2070-#x218F]\r\n | [#x2C00-#x2FEF]\r\n | [#x3001-#xD7FF]\r\n | [#xF900-#xFDCF]\r\n | [#xFDF0-#xFFFD]\r\n `\r\nlet $sreg:= normalize-space($s)\r\n =>tokenize(\"\\|\")\r\n =!>normalize-space()\r\n =!>replace(\"(#x[0-9A-F]+)\",fn($s,$m){\r\n convert:integer-from-base(substring($m,3),16)\r\n =>codepoints-to-string()\r\n })\r\n =!>translate(\"'\",\"\")\r\n =>string-join(\"|\")\r\nreturn $sreg"},{"kind":1,"language":"markdown","value":"# Functions"},{"kind":2,"language":"xquery","value":"declare namespace fos=\"http://www.w3.org/xpath-functions/spec/namespace\";\r\ndoc(\"C:/Users/mrwhe/git/quodatum/basex-lsp/bundles/grammar/function-catalog.xml\")\r\n//fos:function[@prefix=\"fn\"]/@name"},{"kind":2,"language":"xquery","value":"let $a:=\"/srv/basex/webapp/lsp/etc/snippets.jsonc\"\r\n\r\nreturn unparsed-text($a)!replace(.,\"^(.*)//.*$\",\"$1\",\"m\")"}]} \ No newline at end of file diff --git a/webapp/lsp-manager/api-jobs.xqm b/webapp/lsp-manager/api-jobs.xqm new file mode 100644 index 0000000..9eaaa58 --- /dev/null +++ b/webapp/lsp-manager/api-jobs.xqm @@ -0,0 +1,37 @@ +module namespace joba = 'app/jobs'; + +declare variable $joba:names:=("id","type","state","user","duration","time","start"); +(: + id: job ID + type: type of the job (command, query, REST, RESTXQ, etc.) + state: current state of the job: scheduled, queued, running, cached + user: user who started the job + duration: evaluation time (included if a job is running or if the result was cached) + start: next start of job (included if a job will be executed repeatedly) + time: time when job was registered + :) +declare function joba:jobs() +as element(job)* +{ + job:list()[. ne job:current()] ! job:list-details(.) +}; + +declare function joba:table($jobs as element(job)*,$names as xs:string+) +as element(table) +{ + + + {$names!} + + + {for $j in $jobs + return + {for $n in $names + let $val:= $j/@*[name() eq $n] + return + } + } + +
{ . }
{$val/string()}
+ +}; \ No newline at end of file diff --git a/webapp/lsp-manager/app.xqm b/webapp/lsp-manager/app.xqm new file mode 100644 index 0000000..a657959 --- /dev/null +++ b/webapp/lsp-manager/app.xqm @@ -0,0 +1,80 @@ +(:~ + app + :) +module namespace app = 'app/common'; +import module namespace cm = "app/cm" at "common.xqm"; + + +(:~ + : Redirects to the start page. + : @return redirection + :) +declare %rest:path('/app') +function app:redirect( +) as element(rest:response) { + web:redirect('/app/home') +}; + +(:~ + : Returns a file. + : @param $file file or unknown path + : @return rest binary data + :) +declare %rest:path('/app/static/{$file=.+}') + %output:method('basex') + %perm:allow('public') +function app:file( $file as xs:string) +as item()+ { + let $path:=file:base-dir() || 'static/' || $file + return if(file:exists($path)) + then cm:response-file($path) + else ( + + + + + + , + "The requested resource is not available." + ) +}; + + + +(:~ start page. :) +declare %rest:path('/app/home') +function app:home() as item()* { + rest:init(true()), + cm:htmx2("home.htm", map{"version" :"0.0.2"}) +}; + + + +declare %rest:path('/app/profile') +function app:profile() { + cm:htmx2("profile.htm", map{}) +}; + +declare %rest:path('/app/dev') +function app:dev() { + cm:htmx2("dev/home.htm", map{}) +}; + + +declare %rest:path('/app/notifications') +function app:notifications() { + cm:htmx2("notifications.htm", map{}) +}; + +declare %rest:path('/app/components') +function app:components() { + cm:htmx2("components.htm", map{}) +}; + +declare %rest:path('/app/tweets') +function app:tweets() { + cm:htmx2("tweets.htm", map{}) +}; + + + diff --git a/webapp/lsp-manager/common.xqm b/webapp/lsp-manager/common.xqm new file mode 100644 index 0000000..0ea74d9 --- /dev/null +++ b/webapp/lsp-manager/common.xqm @@ -0,0 +1,88 @@ +module namespace cm = 'app/cm'; +import module namespace ore = 'urn:quodatum:template:oregano' at "lib.xq/oregano.xqm"; +declare variable $cm:opts:=map{"base": file:resolve-path("views/", file:base-dir()), + "layout": true(), + "indent": "no" + }; +declare variable $cm:model:=map{"_ctx": "app"}; + +declare function cm:header($path as xs:string?,$data as xs:base64Binary?,$content-type as xs:string?) +as element(rest:response) +{ switch(true()) +case exists($path) return + web:response-header( + map { 'media-type': web:content-type($path) }, + + map { + 'Cache-Control': 'max-age=3600,public', + 'Content-Length': file:size($path), + 'ETag': file:last-modified($path) + } + ) + +default return + web:response-header( + map { 'media-type':$content-type }, + (map:entry('Cache-Control', 'max-age=3600,public'), + if(exists($data)) then map:entry('Content-Length', bin:length($data)) + )=>map:merge() + ) +}; + +(:~ http response from file content:) +declare function cm:response-file($path as xs:string) +as item()*{ +if(file:exists($path)) + then ( + cm:header($path,(),()), + file:read-binary($path) + )else error(xs:QName('cm:not-found'),$path) + () +}; + +(:~ http response from data content:) +declare function cm:response-data($data as xs:base64Binary,$content-type as xs:string) +as item()* +{ + cm:header((),$data,$content-type), + $data + +}; + + +(:~ http response for htmx :) +declare function cm:htmx2($template as xs:string,$model as map(*)) +as item()* +{ + cm:header((),(),"text/html"), +ore:render($template,$cm:opts,$model)=>ore:serialize($cm:opts) +}; + + +(:~ read a relative file :) +declare function cm:read($path as xs:string) +as xs:string{ +file:resolve-path( $path,file:base-dir())=>file:read-text() +}; + +(:~ write a message to standard BaseX log passthru item :) +declare function cm:trace($items as item()*,$msg as xs:string) +as item()*{ + $items,admin:write-log($msg || ": " || serialize($items,map{"method":"basex"}),"MDUI") +}; + +declare function cm:status-404() +as item()*{ + + + + + + , + "The requested resource is not available." +}; + +declare function cm:status($status as xs:integer,$msg as xs:string) + as item()*{ + web:response-header((),(), map { 'status': $status, 'message': "OK"}),$msg +}; \ No newline at end of file diff --git a/webapp/lsp-manager/error-reporting.xqm b/webapp/lsp-manager/error-reporting.xqm new file mode 100644 index 0000000..32a2a02 --- /dev/null +++ b/webapp/lsp-manager/error-reporting.xqm @@ -0,0 +1,26 @@ +module namespace _ = 'app/error-reporting'; +import module namespace cm = "app/cm" at "common.xqm"; + + +declare + %rest:error("*") + %rest:error-param("code", "{$code}") + %rest:error-param("description", "{$description}") + %rest:error-param("value", "{$value}") + %rest:error-param("module", "{$module}") + %rest:error-param("line-number", "{$line-number}") + %rest:error-param("column-number","{$column-number}") + %rest:error-param("additional", "{$additional}") +function _:error($code,$description,$value, + $module,$line-number,$column-number,$additional) { + let $err:=map{"code":$code, "description":$description, + "value": _:format($value), "module": $module, + "line-number": $line-number, "column-number": $column-number, + "additional": _:format($additional)} + return cm:htmx2("error.htm", $err) +}; + +declare function _:format($item) +as xs:string{ + serialize($item,map{"method":"basex"}) +}; \ No newline at end of file diff --git a/webapp/lsp-manager/lib.xq/Thyme.xqm b/webapp/lsp-manager/lib.xq/Thyme.xqm new file mode 100644 index 0000000..00e8a03 --- /dev/null +++ b/webapp/lsp-manager/lib.xq/Thyme.xqm @@ -0,0 +1,1279 @@ +xquery version "1.0" encoding "UTF-8"; + +(: This file was generated on Fri Apr 18, 2025 11:03 (UTC+01) by REx v6.1 which is Copyright (c) 1979-2025 by Gunther Rademacher :) +(: REx command line: thymeleaf.ebnf -xquery -name Thyme -tree :) + +(:~ + : The parser that was generated for the Thyme grammar. + :) +module namespace p="Thyme"; +declare default function namespace "http://www.w3.org/2005/xpath-functions"; + +(:~ + : The index of the lexer state for accessing the combined + : (i.e. level > 1) lookahead code. + :) +declare variable $p:lk as xs:integer := 1; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the begin of the token that has been consumed. + :) +declare variable $p:b0 as xs:integer := 2; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the end of the token that has been consumed. + :) +declare variable $p:e0 as xs:integer := 3; + +(:~ + : The index of the lexer state for accessing the code of the + : level-1-lookahead token. + :) +declare variable $p:l1 as xs:integer := 4; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the begin of the level-1-lookahead token. + :) +declare variable $p:b1 as xs:integer := 5; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the end of the level-1-lookahead token. + :) +declare variable $p:e1 as xs:integer := 6; + +(:~ + : The index of the lexer state for accessing the code of the + : level-2-lookahead token. + :) +declare variable $p:l2 as xs:integer := 7; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the begin of the level-2-lookahead token. + :) +declare variable $p:b2 as xs:integer := 8; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the end of the level-2-lookahead token. + :) +declare variable $p:e2 as xs:integer := 9; + +(:~ + : The index of the lexer state for accessing the code of the + : level-3-lookahead token. + :) +declare variable $p:l3 as xs:integer := 10; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the begin of the level-3-lookahead token. + :) +declare variable $p:b3 as xs:integer := 11; + +(:~ + : The index of the lexer state for accessing the position in the + : input string of the end of the level-3-lookahead token. + :) +declare variable $p:e3 as xs:integer := 12; + +(:~ + : The index of the lexer state for accessing the token code that + : was expected when an error was found. + :) +declare variable $p:error as xs:integer := 13; + +(:~ + : The index of the lexer state that points to the first entry + : used for collecting action results. + :) +declare variable $p:result as xs:integer := 14; + +(:~ + : The codepoint to charclass mapping for 7 bit codepoints. + :) +declare variable $p:MAP0 as xs:integer+ := +( + 42, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 2, 2, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 2, 2, 16, 2, 2, 17, 18, 18, 18, 18, 19, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 2, 2, 2, 20, 2, 21, 22, 23, 24, + 25, 18, 26, 18, 27, 28, 18, 29, 30, 31, 32, 33, 18, 34, 35, 36, 37, 18, 18, 18, 38, 18, 39, 2, 40, 41, 2 +); + +(:~ + : The codepoint to charclass mapping for codepoints below the surrogate block. + :) +declare variable $p:MAP1 as xs:integer+ := +( + 54, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 90, 122, 184, 216, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 42, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 2, 2, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 14, 2, 2, 16, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 17, 18, + 18, 18, 18, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 2, 2, 2, 20, 2, + 21, 22, 23, 24, 25, 18, 26, 18, 27, 28, 18, 29, 30, 31, 32, 33, 18, 34, 35, 36, 37, 18, 18, 18, 38, 18, 39, 2, 40, 41, + 2 +); + +(:~ + : The codepoint to charclass mapping for codepoints above the surrogate block. + :) +declare variable $p:MAP2 as xs:integer+ := +( + 57344, 65536, 65533, 1114111, 2, 2 +); + +(:~ + : The token-set-id to DFA-initial-state mapping. + :) +declare variable $p:INITIAL as xs:integer+ := +( + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 +); + +(:~ + : The DFA transition table. + :) +declare variable $p:TRANSITION as xs:integer+ := +( + 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 688, 688, 694, 717, 704, 717, 851, + 1112, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 704, 717, 851, 1112, 717, 717, 717, 717, 717, 717, + 717, 717, 717, 718, 716, 717, 726, 717, 1115, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 717, 696, 738, 717, 704, + 717, 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 717, 739, 747, 717, 704, 717, 851, 1112, 717, 717, 717, 717, + 717, 717, 717, 717, 717, 748, 707, 717, 756, 717, 872, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 1148, 827, 1151, + 717, 704, 717, 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 1058, 768, 771, 717, 704, 717, 851, 1112, 717, 717, + 717, 717, 717, 717, 717, 717, 717, 708, 784, 717, 704, 717, 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 717, + 717, 717, 717, 704, 717, 851, 793, 717, 717, 717, 717, 717, 717, 717, 717, 717, 1035, 717, 717, 704, 717, 851, 1112, + 717, 717, 717, 717, 717, 717, 717, 717, 717, 886, 717, 887, 704, 717, 851, 793, 717, 717, 717, 717, 717, 717, 717, + 717, 904, 804, 811, 796, 820, 717, 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 717, 886, 717, 887, 704, 717, + 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 717, 835, 849, 1211, 843, 717, 760, 859, 1018, 717, 717, 717, 717, + 717, 717, 717, 986, 717, 717, 717, 704, 717, 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 717, 1152, 870, 717, + 704, 717, 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 1257, 880, 717, 1005, 704, 717, 760, 1112, 717, 717, 717, + 717, 717, 717, 717, 717, 1257, 880, 717, 1005, 895, 717, 760, 901, 717, 717, 717, 717, 717, 717, 717, 717, 1257, 1258, + 717, 948, 704, 717, 760, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 944, 880, 730, 912, 704, 717, 760, 1207, 717, + 958, 717, 717, 717, 717, 717, 717, 1001, 880, 717, 1092, 704, 717, 760, 1129, 717, 717, 717, 717, 717, 717, 717, 717, + 1054, 880, 717, 1005, 704, 717, 760, 1112, 717, 920, 717, 717, 717, 717, 717, 717, 1175, 880, 773, 1005, 704, 717, + 760, 1112, 717, 1238, 717, 717, 717, 717, 717, 717, 1257, 880, 717, 1185, 895, 717, 760, 930, 956, 717, 717, 717, 717, + 717, 717, 717, 1257, 880, 717, 1005, 704, 717, 760, 1112, 717, 775, 717, 717, 717, 717, 717, 717, 966, 880, 785, 1005, + 704, 717, 760, 1112, 984, 717, 717, 717, 717, 717, 717, 717, 1257, 880, 717, 1005, 704, 717, 994, 1112, 717, 717, 717, + 717, 717, 717, 717, 717, 1013, 880, 717, 1005, 704, 971, 760, 1143, 717, 717, 717, 717, 717, 717, 717, 717, 1030, 880, + 717, 1005, 704, 717, 1047, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 1066, 880, 717, 1005, 704, 717, 760, 1112, + 922, 1086, 717, 717, 717, 717, 717, 717, 1100, 880, 1180, 1005, 704, 1106, 760, 1112, 717, 717, 717, 717, 717, 717, + 717, 717, 1257, 880, 717, 1005, 704, 772, 760, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 1257, 880, 1072, 1005, + 704, 1123, 976, 1112, 717, 776, 717, 717, 717, 717, 717, 717, 1137, 880, 717, 1005, 704, 862, 760, 1112, 717, 717, + 717, 717, 717, 717, 717, 717, 1257, 880, 717, 1243, 704, 1039, 1078, 1112, 717, 717, 717, 717, 717, 717, 717, 717, + 1257, 880, 717, 1160, 704, 717, 760, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 1257, 880, 717, 1005, 704, 717, + 760, 1112, 774, 717, 717, 717, 717, 717, 717, 717, 717, 1168, 717, 717, 1193, 1201, 851, 1112, 717, 717, 717, 717, + 717, 717, 717, 717, 812, 937, 717, 717, 704, 717, 1022, 1112, 1232, 717, 717, 717, 717, 717, 717, 717, 717, 1219, + 1226, 887, 704, 717, 851, 1112, 717, 717, 717, 717, 717, 717, 717, 717, 1251, 717, 717, 717, 717, 717, 717, 717, 717, + 717, 717, 717, 717, 717, 717, 717, 274, 274, 274, 274, 274, 274, 274, 274, 0, 0, 0, 0, 0, 0, 32, 1954, 33, 0, 0, 36, + 0, 0, 0, 0, 0, 0, 0, 37, 33, 0, 0, 0, 0, 0, 0, 0, 0, 33, 1590, 0, 0, 36, 0, 0, 0, 0, 44, 45, 0, 0, 1954, 0, 0, 0, 0, + 0, 0, 0, 35, 35, 0, 0, 0, 0, 0, 0, 0, 36, 33, 0, 0, 1592, 0, 0, 0, 0, 53, 0, 67, 0, 0, 2304, 0, 2304, 0, 0, 0, 0, 0, + 0, 0, 46, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 47, 0, 68, 69, 0, 0, 0, 0, 0, 542, 0, 800, 542, 0, 0, 0, 2688, 2688, + 542, 38, 0, 0, 0, 0, 0, 0, 0, 3072, 33, 0, 0, 36, 0, 0, 1337, 0, 0, 2176, 0, 0, 2176, 2176, 2176, 542, 0, 0, 0, 0, 0, + 542, 1191, 33, 0, 0, 36, 0, 1337, 1191, 0, 0, 0, 0, 0, 0, 0, 67, 0, 1337, 1476, 69, 0, 0, 0, 0, 0, 1664, 51, 0, 40, 0, + 0, 0, 0, 0, 0, 0, 67, 36, 542, 1821, 0, 0, 0, 0, 542, 0, 0, 0, 0, 0, 542, 0, 0, 33, 0, 0, 36, 0, 0, 58, 0, 69, 0, 0, + 0, 0, 0, 2688, 0, 0, 48, 0, 0, 0, 1821, 542, 53, 800, 51, 0, 0, 0, 0, 0, 0, 0, 75, 0, 58, 0, 69, 0, 0, 71, 46, 0, 0, + 3072, 0, 3072, 3072, 3072, 0, 19, 1821, 0, 0, 0, 0, 0, 1821, 0, 53, 800, 73, 0, 0, 0, 0, 0, 0, 76, 0, 0, 0, 23, 1821, + 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 66, 53, 0, 67, 0, 0, 74, 0, 0, 0, 0, 0, 0, 2816, 0, 0, 65, 0, 0, 53, 0, 67, 0, 20, + 1821, 0, 0, 0, 0, 0, 1821, 542, 53, 800, 0, 24, 1821, 0, 0, 0, 0, 0, 1476, 0, 0, 0, 0, 640, 0, 67, 0, 0, 25, 1821, 0, + 0, 0, 0, 0, 2560, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 0, 0, 53, 0, 67, 0, 21, 1821, 0, 0, 0, 0, 0, 2304, 0, 0, 0, 0, 26, + 1821, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 46, 0, 53, 0, 67, 0, 0, 77, 0, 0, 0, 0, 0, 0, 50, 0, 1821, 542, 53, 800, 0, + 27, 1821, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 33, 67, 0, 0, 60, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 72, + 0, 28, 1821, 0, 0, 0, 0, 0, 69, 0, 46, 0, 0, 0, 2176, 0, 0, 0, 0, 0, 0, 0, 40, 0, 49, 0, 0, 1821, 542, 53, 800, 31, 0, + 0, 0, 0, 0, 31, 0, 22, 1821, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 51, 1821, 542, 53, 800, 33, 55, 2048, 36, 2432, 0, 0, + 2944, 59, 0, 0, 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 1821, 542, 0, 800, 542, 0, 0, 0, 0, 0, 542, 41, 0, 0, 0, 0, 0, 0, + 0, 384, 0, 896, 0, 0, 0, 78, 0, 0, 0, 0, 0, 52, 1821, 542, 53, 800, 1024, 0, 0, 0, 0, 0, 0, 0, 1821, 0, 0, 0, 0, 0, 0 +); + +(:~ + : The DFA-state to expected-token-set mapping. + :) +declare variable $p:EXPECTED as xs:integer+ := +( + 20, 24, 28, 32, 36, 60, 60, 42, 53, 46, 59, 60, 60, 50, 57, 60, 38, 59, 60, 38, 130, 4098, 8194, 65538, 131074, + 1048578, 2097154, 8388610, 26, 139266, 8454146, 655362, 9437186, 9502722, 8454202, 4575046, 4706118, 2, 4096, 4096, 4, + 1024, 8192, 8, 16, 32, 262144, 1536, 1792, 4194304, 16, 2048, 4, 2048, 4, 32768, 2048, 1536, 1024, 64, 4096, 4096, + 4096, 4096 +); + +(:~ + : The token-string table. + :) +declare variable $p:TOKEN as xs:string+ := +( + "%ERROR", + "Whitespace", + "MessageExpression", + "UrlFrag", + "UrlVar", + "Hash", + "FragmentExpression", + "EOF", + "IntegerLiteral", + "DecimalLiteral", + "DoubleLiteral", + "StringLiteral", + "UtilityObject", + "Identifier", + "'#'", + "'${'", + "'('", + "')'", + "'*{'", + "','", + "'.'", + "'='", + "'@{'", + "'}'" +); + +(:~ + : Match next token in input string, starting at given index, using + : the DFA entry state for the set of tokens that are expected in + : the current context. + : + : @param $input the input string. + : @param $begin the index where to start in input string. + : @param $token-set the expected token set id. + : @return a sequence of three: the token code of the result token, + : with input string begin and end positions. If there is no valid + : token, return the negative id of the DFA state that failed, along + : with begin and end positions of the longest viable prefix. + :) +declare function p:match($input as xs:string, + $begin as xs:integer, + $token-set as xs:integer) as xs:integer+ +{ + let $result := $p:INITIAL[1 + $token-set] + return p:transition($input, + $begin, + $begin, + $begin, + $result, + $result mod 128, + 0) +}; + +(:~ + : The DFA state transition function. If we are in a valid DFA state, save + : it's result annotation, consume one input codepoint, calculate the next + : state, and use tail recursion to do the same again. Otherwise, return + : any valid result or a negative DFA state id in case of an error. + : + : @param $input the input string. + : @param $begin the begin index of the current token in the input string. + : @param $current the index of the current position in the input string. + : @param $end the end index of the result in the input string. + : @param $result the result code. + : @param $current-state the current DFA state. + : @param $previous-state the previous DFA state. + : @return a sequence of three: the token code of the result token, + : with input string begin and end positions. If there is no valid + : token, return the negative id of the DFA state that failed, along + : with begin and end positions of the longest viable prefix. + :) +declare function p:transition($input as xs:string, + $begin as xs:integer, + $current as xs:integer, + $end as xs:integer, + $result as xs:integer, + $current-state as xs:integer, + $previous-state as xs:integer) +{ + if ($current-state eq 0) then + let $result := $result idiv 128 + let $end := if ($end gt string-length($input)) then string-length($input) + 1 else $end + return + if ($result ne 0) then + ( + $result - 1, + $begin, + $end + ) + else + ( + - $previous-state, + $begin, + $current - 1 + ) + else + let $c0 := (string-to-codepoints(substring($input, $current, 1)), 0)[1] + let $c1 := + if ($c0 < 128) then + $p:MAP0[1 + $c0] + else if ($c0 < 55296) then + let $c1 := $c0 idiv 32 + let $c2 := $c1 idiv 32 + return $p:MAP1[1 + $c0 mod 32 + $p:MAP1[1 + $c1 mod 32 + $p:MAP1[1 + $c2]]] + else + p:map2($c0, 1, 2) + let $current := $current + 1 + let $i0 := 128 * $c1 + $current-state - 1 + let $i1 := $i0 idiv 8 + let $next-state := $p:TRANSITION[$i0 mod 8 + $p:TRANSITION[$i1 + 1] + 1] + return + if ($next-state > 127) then + p:transition($input, $begin, $current, $current, $next-state, $next-state mod 128, $current-state) + else + p:transition($input, $begin, $current, $end, $result, $next-state, $current-state) +}; + +(:~ + : Recursively translate one 32-bit chunk of an expected token bitset + : to the corresponding sequence of token strings. + : + : @param $result the result of previous recursion levels. + : @param $chunk the 32-bit chunk of the expected token bitset. + : @param $base-token-code the token code of bit 0 in the current chunk. + : @return the set of token strings. + :) +declare function p:token($result as xs:string*, + $chunk as xs:integer, + $base-token-code as xs:integer) +{ + if ($chunk = 0) then + $result + else + p:token + ( + ($result, if ($chunk mod 2 != 0) then $p:TOKEN[$base-token-code] else ()), + if ($chunk < 0) then $chunk idiv 2 + 2147483648 else $chunk idiv 2, + $base-token-code + 1 + ) +}; + +(:~ + : Calculate expected token set for a given DFA state as a sequence + : of strings. + : + : @param $state the DFA state. + : @return the set of token strings. + :) +declare function p:expected-token-set($state as xs:integer) as xs:string* +{ + if ($state > 0) then + for $t in 0 to 0 + let $i0 := $t * 78 + $state - 1 + let $i1 := $i0 idiv 4 + return p:token((), $p:EXPECTED[$i0 mod 4 + $p:EXPECTED[$i1 + 1] + 1], $t * 32 + 1) + else + () +}; + +(:~ + : Classify codepoint by doing a tail recursive binary search for a + : matching codepoint range entry in MAP2, the codepoint to charclass + : map for codepoints above the surrogate block. + : + : @param $c the codepoint. + : @param $lo the binary search lower bound map index. + : @param $hi the binary search upper bound map index. + : @return the character class. + :) +declare function p:map2($c as xs:integer, $lo as xs:integer, $hi as xs:integer) as xs:integer +{ + if ($lo > $hi) then + 0 + else + let $m := ($hi + $lo) idiv 2 + return + if ($p:MAP2[$m] > $c) then + p:map2($c, $lo, $m - 1) + else if ($p:MAP2[2 + $m] < $c) then + p:map2($c, $m + 1, $hi) + else + $p:MAP2[4 + $m] +}; + +(:~ + : Parse NumericLiteral. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-NumericLiteral($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] = 8) then (: IntegerLiteral :) + let $state := p:consume(8, $input, $state) (: IntegerLiteral :) + return $state + else if ($state[$p:l1] = 9) then (: DecimalLiteral :) + let $state := p:consume(9, $input, $state) (: DecimalLiteral :) + return $state + else + let $state := p:consume(10, $input, $state) (: DoubleLiteral :) + return $state + let $end := $state[$p:e0] + return p:reduce($state, "NumericLiteral", $count, $begin, $end) +}; + +(:~ + : Parse Literal. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-Literal($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] = 11) then (: StringLiteral :) + let $state := p:consume(11, $input, $state) (: StringLiteral :) + return $state + else + let $state := + if ($state[$p:error]) then + $state + else + p:parse-NumericLiteral($input, $state) + return $state + let $end := $state[$p:e0] + return p:reduce($state, "Literal", $count, $begin, $end) +}; + +(:~ + : Parse UtilityExpression. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-UtilityExpression($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(14, $input, $state) (: '#' :) + let $state := p:lookahead1W(1, $input, $state) (: Whitespace | UtilityObject :) + let $state := p:consume(12, $input, $state) (: UtilityObject :) + let $state := p:lookahead1W(5, $input, $state) (: Whitespace | '.' :) + let $state := p:consume(20, $input, $state) (: '.' :) + let $end := $state[$p:e0] + return p:reduce($state, "UtilityExpression", $count, $begin, $end) +}; + +(:~ + : Parse UrlParameter. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-UrlParameter($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(13, $input, $state) (: Identifier :) + let $state := p:lookahead1W(6, $input, $state) (: Whitespace | '=' :) + let $state := p:consume(21, $input, $state) (: '=' :) + let $state := p:lookahead1W(15, $input, $state) (: Whitespace | MessageExpression | FragmentExpression | + IntegerLiteral | DecimalLiteral | DoubleLiteral | + StringLiteral | '#' | '${' | '(' | '*{' | '@{' :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Expression($input, $state) + let $end := $state[$p:e0] + return p:reduce($state, "UrlParameter", $count, $begin, $end) +}; + +(:~ + : Parse the 1st loop of production UrlParameters (zero or more). Use + : tail recursion for iteratively updating the lexer state. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-UrlParameters-1($input as xs:string, $state as item()+) +{ + if ($state[$p:error]) then + $state + else + let $state := p:lookahead1W(11, $input, $state) (: Whitespace | ')' | ',' :) + return + if ($state[$p:l1] != 19) then (: ',' :) + $state + else + let $state := p:consume(19, $input, $state) (: ',' :) + let $state := p:lookahead1W(2, $input, $state) (: Whitespace | Identifier :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-UrlParameter($input, $state) + return p:parse-UrlParameters-1($input, $state) +}; + +(:~ + : Parse UrlParameters. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-UrlParameters($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(16, $input, $state) (: '(' :) + let $state := p:lookahead1W(9, $input, $state) (: Whitespace | Identifier | ')' :) + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] = 13) then (: Identifier :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-UrlParameter($input, $state) + let $state := p:parse-UrlParameters-1($input, $state) + return $state + else + $state + let $state := p:consume(17, $input, $state) (: ')' :) + let $end := $state[$p:e0] + return p:reduce($state, "UrlParameters", $count, $begin, $end) +}; + +(:~ + : Parse the 1st loop of production LinkExpression (one or more). Use + : tail recursion for iteratively updating the lexer state. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-LinkExpression-1($input as xs:string, $state as item()+) +{ + if ($state[$p:error]) then + $state + else + let $state := p:lookahead1W(8, $input, $state) (: Whitespace | UrlFrag | UrlVar :) + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] = 3) then (: UrlFrag :) + let $state := p:consume(3, $input, $state) (: UrlFrag :) + return $state + else + let $state := p:consume(4, $input, $state) (: UrlVar :) + return $state + let $state := p:lookahead1W(14, $input, $state) (: Whitespace | UrlFrag | UrlVar | Hash | '(' | '}' :) + return + if ($state[$p:l1] != 3 (: UrlFrag :) + and $state[$p:l1] != 4) then (: UrlVar :) + $state + else + p:parse-LinkExpression-1($input, $state) +}; + +(:~ + : Parse LinkExpression. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-LinkExpression($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(22, $input, $state) (: '@{' :) + let $state := p:parse-LinkExpression-1($input, $state) + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] = 5) then (: Hash :) + let $state := p:consume(5, $input, $state) (: Hash :) + return $state + else + $state + let $state := p:lookahead1W(10, $input, $state) (: Whitespace | '(' | '}' :) + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] = 16) then (: '(' :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-UrlParameters($input, $state) + return $state + else + $state + let $state := p:lookahead1W(7, $input, $state) (: Whitespace | '}' :) + let $state := p:consume(23, $input, $state) (: '}' :) + let $end := $state[$p:e0] + return p:reduce($state, "LinkExpression", $count, $begin, $end) +}; + +(:~ + : Parse SelectionExpression. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-SelectionExpression($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(18, $input, $state) (: '*{' :) + let $state := p:lookahead1W(2, $input, $state) (: Whitespace | Identifier :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-ObjectAccess($input, $state) + let $state := p:consume(23, $input, $state) (: '}' :) + let $end := $state[$p:e0] + return p:reduce($state, "SelectionExpression", $count, $begin, $end) +}; + +(:~ + : Parse Argument. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-Argument($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Expression($input, $state) + let $end := $state[$p:e0] + return p:reduce($state, "Argument", $count, $begin, $end) +}; + +(:~ + : Parse MethodName. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-MethodName($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(13, $input, $state) (: Identifier :) + let $end := $state[$p:e0] + return p:reduce($state, "MethodName", $count, $begin, $end) +}; + +(:~ + : Parse the 1st loop of production MethodInvocation (zero or more). Use + : tail recursion for iteratively updating the lexer state. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-MethodInvocation-1($input as xs:string, $state as item()+) +{ + if ($state[$p:error]) then + $state + else + let $state := p:lookahead1W(11, $input, $state) (: Whitespace | ')' | ',' :) + return + if ($state[$p:l1] != 19) then (: ',' :) + $state + else + let $state := p:consume(19, $input, $state) (: ',' :) + let $state := p:lookahead1W(15, $input, $state) (: Whitespace | MessageExpression | FragmentExpression | + IntegerLiteral | DecimalLiteral | DoubleLiteral | + StringLiteral | '#' | '${' | '(' | '*{' | '@{' :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Argument($input, $state) + return p:parse-MethodInvocation-1($input, $state) +}; + +(:~ + : Parse MethodInvocation. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-MethodInvocation($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(20, $input, $state) (: '.' :) + let $state := p:lookahead1W(2, $input, $state) (: Whitespace | Identifier :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-MethodName($input, $state) + let $state := p:lookahead1W(3, $input, $state) (: Whitespace | '(' :) + let $state := p:consume(16, $input, $state) (: '(' :) + let $state := p:lookahead1W(16, $input, $state) (: Whitespace | MessageExpression | FragmentExpression | + IntegerLiteral | DecimalLiteral | DoubleLiteral | + StringLiteral | '#' | '${' | '(' | ')' | '*{' | '@{' :) + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] != 17) then (: ')' :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Argument($input, $state) + let $state := p:parse-MethodInvocation-1($input, $state) + return $state + else + $state + let $state := p:consume(17, $input, $state) (: ')' :) + let $end := $state[$p:e0] + return p:reduce($state, "MethodInvocation", $count, $begin, $end) +}; + +(:~ + : Parse Property. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-Property($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(13, $input, $state) (: Identifier :) + let $end := $state[$p:e0] + return p:reduce($state, "Property", $count, $begin, $end) +}; + +(:~ + : Parse Variable. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-Variable($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(13, $input, $state) (: Identifier :) + let $end := $state[$p:e0] + return p:reduce($state, "Variable", $count, $begin, $end) +}; + +(:~ + : Parse the 1st loop of production ObjectAccess (zero or more). Use + : tail recursion for iteratively updating the lexer state. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-ObjectAccess-1($input as xs:string, $state as item()+) +{ + if ($state[$p:error]) then + $state + else + let $state := p:lookahead1W(12, $input, $state) (: Whitespace | '.' | '}' :) + let $state := + if ($state[$p:l1] eq 20) then (: '.' :) + let $state := p:lookahead2W(2, $input, $state) (: Whitespace | Identifier :) + let $state := + if ($state[$p:lk] eq 436) then (: '.' Identifier :) + let $state := p:lookahead3W(13, $input, $state) (: Whitespace | '(' | '.' | '}' :) + return $state + else + $state + return $state + else + ($state[$p:l1], subsequence($state, $p:lk + 1)) + return + if ($state[$p:lk] != 20916 (: '.' Identifier '.' :) + and $state[$p:lk] != 23988) then (: '.' Identifier '}' :) + $state + else + let $state := p:consume(20, $input, $state) (: '.' :) + let $state := p:lookahead1W(2, $input, $state) (: Whitespace | Identifier :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Property($input, $state) + return p:parse-ObjectAccess-1($input, $state) +}; + +(:~ + : Parse the 2nd loop of production ObjectAccess (zero or more). Use + : tail recursion for iteratively updating the lexer state. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-ObjectAccess-2($input as xs:string, $state as item()+) +{ + if ($state[$p:error]) then + $state + else + let $state := p:lookahead1W(12, $input, $state) (: Whitespace | '.' | '}' :) + return + if ($state[$p:l1] != 20) then (: '.' :) + $state + else + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-MethodInvocation($input, $state) + return p:parse-ObjectAccess-2($input, $state) +}; + +(:~ + : Parse ObjectAccess. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-ObjectAccess($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Variable($input, $state) + let $state := p:parse-ObjectAccess-1($input, $state) + let $state := p:parse-ObjectAccess-2($input, $state) + let $end := $state[$p:e0] + return p:reduce($state, "ObjectAccess", $count, $begin, $end) +}; + +(:~ + : Parse VariableExpression. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-VariableExpression($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:consume(15, $input, $state) (: '${' :) + let $state := p:lookahead1W(2, $input, $state) (: Whitespace | Identifier :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-ObjectAccess($input, $state) + let $state := p:consume(23, $input, $state) (: '}' :) + let $end := $state[$p:e0] + return p:reduce($state, "VariableExpression", $count, $begin, $end) +}; + +(:~ + : Parse Expression. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-Expression($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := + if ($state[$p:error]) then + $state + else if ($state[$p:l1] = 15) then (: '${' :) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-VariableExpression($input, $state) + return $state + else if ($state[$p:l1] = 18) then (: '*{' :) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-SelectionExpression($input, $state) + return $state + else if ($state[$p:l1] = 2) then (: MessageExpression :) + let $state := p:consume(2, $input, $state) (: MessageExpression :) + return $state + else if ($state[$p:l1] = 22) then (: '@{' :) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-LinkExpression($input, $state) + return $state + else if ($state[$p:l1] = 6) then (: FragmentExpression :) + let $state := p:consume(6, $input, $state) (: FragmentExpression :) + return $state + else if ($state[$p:l1] = 14) then (: '#' :) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-UtilityExpression($input, $state) + return $state + else if ($state[$p:l1] = 16) then (: '(' :) + let $state := p:consume(16, $input, $state) (: '(' :) + let $state := p:lookahead1W(15, $input, $state) (: Whitespace | MessageExpression | FragmentExpression | + IntegerLiteral | DecimalLiteral | DoubleLiteral | + StringLiteral | '#' | '${' | '(' | '*{' | '@{' :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Expression($input, $state) + let $state := p:lookahead1W(4, $input, $state) (: Whitespace | ')' :) + let $state := p:consume(17, $input, $state) (: ')' :) + return $state + else + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Literal($input, $state) + return $state + let $end := $state[$p:e0] + return p:reduce($state, "Expression", $count, $begin, $end) +}; + +(:~ + : Parse ThymeLeaf. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:parse-ThymeLeaf($input as xs:string, $state as item()+) as item()+ +{ + let $count := count($state) + let $begin := $state[$p:e0] + let $state := p:lookahead1W(15, $input, $state) (: Whitespace | MessageExpression | FragmentExpression | + IntegerLiteral | DecimalLiteral | DoubleLiteral | + StringLiteral | '#' | '${' | '(' | '*{' | '@{' :) + let $state := p:whitespace($input, $state) + let $state := + if ($state[$p:error]) then + $state + else + p:parse-Expression($input, $state) + let $state := p:lookahead1W(0, $input, $state) (: Whitespace | EOF :) + let $state := p:consume(7, $input, $state) (: EOF :) + let $end := $state[$p:e0] + return p:reduce($state, "ThymeLeaf", $count, $begin, $end) +}; + +(:~ + : Create a textual error message from a parsing error. + : + : @param $input the input string. + : @param $error the parsing error descriptor. + : @return the error message. + :) +declare function p:error-message($input as xs:string, $error as element(error)) as xs:string +{ + let $begin := xs:integer($error/@b) + let $context := string-to-codepoints(substring($input, 1, $begin - 1)) + let $linefeeds := index-of($context, 10) + let $line := count($linefeeds) + 1 + let $column := ($begin - $linefeeds[last()], $begin)[1] + return + string-join + ( + ( + if ($error/@o) then + ("syntax error, found ", $p:TOKEN[$error/@o + 1]) + else + "lexical analysis failed", + " ", + "while expecting ", + if ($error/@x) then + $p:TOKEN[$error/@x + 1] + else + let $expected := p:expected-token-set($error/@s) + return + ( + "["[exists($expected[2])], + string-join($expected, ", "), + "]"[exists($expected[2])] + ), + " ", + if ($error/@o or $error/@e = $begin) then + () + else + ("after successfully scanning ", string($error/@e - $begin), " characters beginning "), + "at line ", string($line), ", column ", string($column), ": ", + "...", substring($input, $begin, 64), "..." + ), + "" + ) +}; + +(:~ + : Consume one token, i.e. compare lookahead token 1 with expected + : token and in case of a match, shift lookahead tokens down such that + : l1 becomes the current token, and higher lookahead tokens move down. + : When lookahead token 1 does not match the expected token, raise an + : error by saving the expected token code in the error field of the + : lexer state. + : + : @param $code the expected token. + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:consume($code as xs:integer, $input as xs:string, $state as item()+) as item()+ +{ + if ($state[$p:error]) then + $state + else if ($state[$p:l1] eq $code) then + ( + subsequence($state, $p:l1, 9), + 0, 0, 0, + subsequence($state, 13), + let $begin := $state[$p:e0] + let $end := $state[$p:b1] + where $begin ne $end + return + text + { + substring($input, $begin, $end - $begin) + }, + let $token := $p:TOKEN[1 + $state[$p:l1]] + let $name := if (starts-with($token, "'")) then "TOKEN" else $token + let $begin := $state[$p:b1] + let $end := $state[$p:e1] + return + element {$name} + { + substring($input, $begin, $end - $begin) + } + ) + else + ( + subsequence($state, 1, $p:error - 1), + element error + { + attribute b {$state[$p:b1]}, + attribute e {$state[$p:e1]}, + if ($state[$p:l1] lt 0) then + attribute s {- $state[$p:l1]} + else + (attribute o {$state[$p:l1]}, attribute x {$code}) + }, + subsequence($state, $p:error + 1) + ) +}; + +(:~ + : Consume whitespace. + : + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:whitespace($input as xs:string, + $state as item()+) as item()+ +{ + if ($state[$p:e0] = $state[$p:b1]) then + $state + else + let $begin := $state[$p:e0] + let $end := $state[$p:b1] + return + ( + 0, + $state[$p:b0], + $end, + subsequence($state, $p:e0 + 1), + text + { + substring($input, $begin, $end - $begin) + } + ) +}; + +(:~ + : Use p:match to fetch the next token, but skip any leading + : whitespace. + : + : @param $input the input string. + : @param $begin the index where to start. + : @param $token-set the valid token set id. + : @return a sequence of three values: the token code of the result + : token, with input string positions of token begin and end. + :) +declare function p:matchW($input as xs:string, + $begin as xs:integer, + $token-set as xs:integer) +{ + let $match := p:match($input, $begin, $token-set) + return + if ($match[1] = 1) then (: Whitespace :) + p:matchW($input, $match[3], $token-set) + else + $match +}; + +(:~ + : Lookahead one token on level 1 with whitespace skipping. + : + : @param $set the code of the DFA entry state for the set of valid tokens. + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:lookahead1W($set as xs:integer, $input as xs:string, $state as item()+) as item()+ +{ + if ($state[$p:l1] ne 0) then + $state + else + let $match := + ( + p:matchW($input, $state[$p:e0], $set), + 0, 0, 0 + ) + return + ( + $match[1], + subsequence($state, $p:b0, 2), + $match, + subsequence($state, 10) + ) +}; + +(:~ + : Lookahead one token on level 2 with whitespace skipping. + : + : @param $set the code of the DFA entry state for the set of valid tokens. + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:lookahead2W($set as xs:integer, $input as xs:string, $state as item()+) as item()+ +{ + let $match := + if ($state[$p:l2] ne 0) then + subsequence($state, $p:l2, 6) + else + ( + p:matchW($input, $state[$p:e1], $set), + 0, 0, 0 + ) + return + ( + $match[1] * 32 + $state[$p:l1], + subsequence($state, $p:b0, 5), + $match, + subsequence($state, 13) + ) +}; + +(:~ + : Lookahead one token on level 3 with whitespace skipping. + : + : @param $set the code of the DFA entry state for the set of valid tokens. + : @param $input the input string. + : @param $state lexer state, error indicator, and result stack. + : @return the updated state. + :) +declare function p:lookahead3W($set as xs:integer, $input as xs:string, $state as item()+) as item()+ +{ + let $match := + if ($state[$p:l3] ne 0) then + subsequence($state, $p:l3, 3) + else + p:matchW($input, $state[$p:e2], $set) + return + ( + $match[1] * 1024 + $state[$p:lk], + subsequence($state, $p:b0, 8), + $match, + subsequence($state, 13) + ) +}; + +(:~ + : Reduce the result stack, creating a nonterminal element. Pop + : $count elements off the stack, wrap them in a new element + : named $name, and push the new element. + : + : @param $state lexer state, error indicator, and result stack. + : @param $name the name of the result node. + : @param $count the number of child nodes. + : @param $begin the input index where the nonterminal begins. + : @param $end the input index where the nonterminal ends. + : @return the updated state. + :) +declare function p:reduce($state as item()+, $name as xs:string, $count as xs:integer, $begin as xs:integer, $end as xs:integer) as item()+ +{ + subsequence($state, 1, $count), + element {$name} + { + subsequence($state, $count + 1) + } +}; + +(:~ + : Parse start symbol ThymeLeaf from given string. + : + : @param $s the string to be parsed. + : @return the result as generated by parser actions. + :) +declare function p:parse-ThymeLeaf($s as xs:string) as item()* +{ + let $state := (0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, false()) + let $state := p:parse-ThymeLeaf($s, $state) + let $error := $state[$p:error] + return + if ($error) then + element ERROR {$error/@*, p:error-message($s, $error)} + else + subsequence($state, $p:result) +}; + +(: End :) diff --git a/webapp/lsp-manager/lib.xq/oregano.xqm b/webapp/lsp-manager/lib.xq/oregano.xqm new file mode 100644 index 0000000..4fb79e0 --- /dev/null +++ b/webapp/lsp-manager/lib.xq/oregano.xqm @@ -0,0 +1,235 @@ +xquery version '4.0'; +(: oregano: A thymeleaf style templating library - INCOMPLETE +Aims to be a compatable implementation of a subset https://www.thymeleaf.org/ +for use as a HTML5 templating engine with BaseX 10+ +supports: th:text, layout:decorate, layout:fragment +@author Andy Bunce +@status INCOMPLETE alpha +@licence Apache 2 +:) +module namespace ore = 'urn:quodatum:template:oregano'; +import module namespace p = 'Thyme' at "Thyme.xqm"; + +declare namespace th="http://www.thymeleaf.org"; +declare namespace layout="http://www.ultraq.net.nz/thymeleaf/layout"; + +declare variable $ore:default-options:=map{"base": file:base-dir(), + "layout": true(), + "indent": "no" + }; + +(:~ updated html doc :) +declare function ore:render($view as xs:string,$opts as map(*),$model as map(*)) +as document-node(){ + let $opts:=map:merge(($opts,$ore:default-options)) + let $doc:=(ore:doc($view,$opts) + ,message($model,"£ RENDER MODEL(" || $view ||"): ")) + let $a:= ore:update($doc,$opts,$model) + return ore:decorate($a,$opts) +}; + +(:~ update doc by evaluating th:each and th:text :) +declare function ore:update($doc ,$opts as map(*),$model as map(*)) +{ + $doc update { ( + for $each in outermost(*//*[@th:each]) + let $p:=ore:parse-each($each/@th:each) + return ( + replace node $each with ( + for $item in map:get($model,substring-after($p,",")) + let $model2:=map:put($model,substring-before($p,","),$item) + (:=>trace("MODEL2:"):) + let $x:=ore:update($each,$opts,$model2) + return $x (:=>trace("DD: "):) + )) + ,delete node @th:each + ) + }update{ + for $text in .//*[@th:text] + where not($text/ancestor::*/@th:each) + return ( + replace value of node $text with ore:expression($text/@th:text,$model) + ,delete node $text/@th:text + ) + , ore:update-attrib(., "value", $model)=>prof:time("£ ore:update-attrib value: ") + , ore:update-attrib(., "href", $model)=>prof:time("£ ore:update-attrib href: ") + , ore:update-attrib(., "src", $model)=>prof:time("£ ore:update-attrib src: ") + } +}; + +(:~ update-attrib: use value of expression @th:name to update @name :) +declare %updating function ore:update-attrib($context as node(),$name as xs:string,$model as map(*)) +as empty-sequence(){ +for $at in $context//*/@th:*[local-name(.) eq $name] + where not($at/ancestor::*/@th:each) + let $target:=$at/../@*[name() eq $name]=>trace("£ target: ") + return ( + replace value of node $target with + ore:expression($at, $model)=>trace("£ ore:expression: ") + ,delete node $at + ) +}; + +(: load doc from base folder:) +declare function ore:doc($file as xs:string,$opts as map(*)) +as document-node(){ + let $f:=file:resolve-path($file,$opts?base)=>trace("ore:doc") + return doc($f) +}; + +(:~ wrap with template named in root attribute @layout:decorate :) +declare function ore:decorate($doc as document-node(),$opts as map(*)) +as document-node(){ + if(not($opts?layout and $doc/*/@layout:decorate)) + then $doc + else + let $wrap:=$doc/*/@layout:decorate=>replace(".+\{(.+)\}","$1") + let $wdoc:=ore:doc($wrap,$opts) + return $wdoc update{ + + replace node html/head/title + with $doc/html/head/title, + + insert node $doc/html/head/*[name() ne 'title'] + as last into html/head, + + (: update matching fragments :) + for $frag in //*/@layout:fragment + where $doc//*/@layout:fragment=$frag + return replace node //*[@layout:fragment=$frag] + with ($doc//*[@layout:fragment=$frag] update {delete node @layout:fragment}) + + } +}; + +(:~ serialize as HTML5 :) +declare function ore:serialize($doc as document-node(),$opts as map(*)) +as xs:string{ + serialize($doc , + map { 'method': 'html', 'version': '5.0'} + ) +}; + +(:~ Return value of $atrib from model :) +declare function ore:expression($atrb as xs:string,$model as map(*)) +as xs:string{ + let $p:=ore:parse-text($atrb)=>trace("ore:expression: ") + let $exp:=substring-after($p,",") + return switch(substring-before($p,",")) + + case "$" case "#" return + (: @TODO assumes simple dotted :) + ore:expression-value($exp,$model) + + case "@" return + let $x:=ore:expression-link($exp)=>ore:parse-params() + return ore:template1($x,$model) + + case "*" return + error(xs:QName('ore:expression'),"*" || $exp) + + default return + error(xs:QName('ore:expression'),"bad exp start: " || $atrb) +}; + +(:~ evaluate link $exp is body from "@{ body }" :) +declare function ore:expression-link($exp as xs:string) +as xs:string { + switch (true()) + case starts-with($exp=>trace("EEEE: "), "http") + case starts-with($exp, "//") + return $exp + case starts-with($exp, "/") + return concat("/{_ctx}",$exp) (: prepend app:) + case starts-with($exp, "~/") + return substring($exp, 2) + + default + return $exp +}; + +(:~ $exp is name or dotted name :) +declare function ore:expression-value($exp as xs:string, $model as map (*)) +as xs:string { + (: @TODO assumes simple dotted :) + try { + normalize-space($exp)=> tokenize("\.")=>fold-left( $model, map:get#2)=>string() + } catch *{ + error(xs:QName('expression-value'),"bad: " || $exp) + } +}; + +(: extract param map if present in link +@result 1st item string updated link , optional 2nd item is map of params + :) +declare function ore:parse-params($link as xs:string) +as item()* +{ + let $rex:="[^(]+\((.+)\)$" + let $p:=replace($link,$rex,"$1") + return if($p ne $link) + then (: params present :) + let $params:=(for $i in tokenize($p,",") + let $v:=substring-after($i,"=")=>replace("['""](.*)['""]","$1") (: remove quotes :) + return map:entry(substring-before($i,"="),$v) + )=>map:merge() + + let $ps:=map:keys($params)!concat(.,"=",$params(.))=>string-join("&") + let $u:=substring-before($link,"(") + let $anchor:=if(contains($u,"#")) + then "#" || substring-after($u,"#") + + return (concat(substring-before($u || "#","#"),"?",$ps,$anchor),$params) + + else $link +}; + +(:~ return "name,collection" from @th:each="name: ${collection}" or error :) +declare function ore:parse-each($atrb as xs:string) +as xs:string{ + let $rex:="^(\w+):\s*\$\{\s*(\w+)\s*\}\s*$" + return if(matches($atrb,$rex)) + then replace($atrb,$rex,"$1,$2") + else error(xs:QName('ore:parse-each'),"bad th:each: " || $atrb) +}; + +(:~ return "type,expression" from @th:text or error + Variable Expressions: ${...} + Selection Variable Expressions: *{...} + Message Expressions: #{...} + Link URL Expressions: @{...} +:) +declare function ore:parse-text($atrb as xs:string) +as xs:string{ + let $rex:="^([$*#@])\{\s*([^\s]+)\s*\}$" + return if(matches($atrb,$rex)) + then replace($atrb,$rex,"$1,$2") + else error(xs:QName('ore:parse-text'),"bad th:text: " || $atrb) +}; + + + +(:~ REx parse :) +declare function ore:parse($exp as xs:string) +as element(*) +{ +let $x:=p:parse-ThymeLeaf($exp) +return if($x/self::ERROR) + then error(xs:QName('ore:parse'),"bad: " || $exp) + else $x +}; + + +(:~ subst {key} strings in $tmp from model dotted values :) +declare function ore:template1($tmp as xs:string, $model as map(*)) +as xs:string{ + (: @todo xquery 4 replace :) + let $s:=analyze-string($tmp, "\{([^}]+)\}") + update{ + for $m in fn:match + + let $g:= normalize-space($m/fn:group)=> tokenize("\.")=>fold-left( $model, map:get#2)=>string() + return replace value of node $m with $g + } + return string($s) +}; diff --git a/webapp/lsp-manager/lib.xq/outlet.xqm b/webapp/lsp-manager/lib.xq/outlet.xqm new file mode 100644 index 0000000..193a33a --- /dev/null +++ b/webapp/lsp-manager/lib.xq/outlet.xqm @@ -0,0 +1,102 @@ +xquery version '3.1'; +(:~ save conversion outputs to file +ensures target folders created +save multiple docs when root found +optionally serialize XML with default namespace +@author quodatum Andy Bunce +:) +module namespace outlet = 'urn:conversion:outlet'; + +(: serialization options + key is arbitary name, value is BaseX serialization option map +for xml serializations the non-standard 'ns' option supplies a namespace to use as default ' :) +declare variable $outlet:serial:=map{ + "xvrl": map{"method":"xml","ns":"http://www.xproc.org/ns/xvrl"}, + "docbook":map{"method":"xml","ns":"http://docbook.org/ns/docbook"}, + "csv": map{"method":"text"}, + "xml": map{"method":"xml"} +}; + +(:~save $doc to $dest unless empty,will create directories as required +@param $opts serialization options and ns to set a default namespace :) +declare function outlet:save($doc as item(),$dest as xs:string?,$opts as map(*)?) +as xs:string?{ + if (exists($dest)) + then + let $doc:=if($opts?ns) + then outlet:change-element-ns-deep($doc,$opts?ns,"") + else $doc + return ( + file:create-dir(file:parent($dest)), + file:write($dest,$doc,map:remove($opts,"ns")), + $dest + ) +}; + +(:~ save file or files if node is wrapper +@return paths to saved files +:) +declare function outlet:save-wrapper($doc as item(),$dest as xs:string?,$opts as map(*)?) +as xs:string*{ + if ($doc/wrapper) + then $doc/wrapper/result! outlet:save(*,file:resolve-path(@href,$dest),$opts) + else outlet:save($doc,$dest,$opts) +}; + +(:~ from functx http://www.xqueryfunctions.com/xq/functx_change-element-ns-deep.html :) +declare function outlet:change-element-ns-deep + ( $nodes as node()* , + $newns as xs:string , + $prefix as xs:string ) as node()* { + + for $node in $nodes + return if ($node instance of element()) + then (element + {QName ($newns, + concat($prefix, + if ($prefix = '') + then '' + else ':', + local-name($node)))} + {$node/@*, + outlet:change-element-ns-deep($node/node(), + $newns, $prefix)}) + else if ($node instance of document-node()) + then outlet:change-element-ns-deep($node/node(), + $newns, $prefix) + else $node + } ; + +(:~ @see https://stackoverflow.com/a/8600987/3210344 :) + declare function outlet:remove-prefixes($node as node(), $prefixes as xs:string*) + as node(){ + typeswitch ($node) + case element() + return + if ($prefixes = ('#all', prefix-from-QName(node-name($node)))) then + element {QName(namespace-uri($node), local-name($node))} { + $node/@*, + $node/node()/outlet:remove-prefixes(., $prefixes) + } + else + element {node-name($node)} { + $node/@*, + $node/node()/outlet:remove-prefixes(., $prefixes) + } + case document-node() + return + document { + $node/node()/outlet:remove-prefixes(., $prefixes) + } + default + return $node +}; + +(:~ relative file paths below folder $path, matching $selector :) +declare function outlet:select($path as xs:string,$selector as map(xs:string,item()*)) +as xs:string* +{ +file:list($path,true(),$selector?pattern) +[some $i in $selector?include satisfies contains(.,$i)] +[every $x in $selector?exclude satisfies not(contains(.,$x))] +}; \ No newline at end of file diff --git a/webapp/lsp-manager/lib.xq/tree.xqm b/webapp/lsp-manager/lib.xq/tree.xqm new file mode 100644 index 0000000..ffe8417 --- /dev/null +++ b/webapp/lsp-manager/lib.xq/tree.xqm @@ -0,0 +1,59 @@ +xquery version "3.1"; +(:~ + : represant sequence of path strings as sequence of xml trees + :) +module namespace tree = 'quodatum.data.tree'; + + +(:~ + : convert path(s) to tree + :) +declare function tree:build($paths as xs:string*,$del as xs:string) +{ +fn:fold-right($paths, + (), + function($this,$acc){ tree:merge($acc,tree:nest($this,$del)) } + ) +}; + +(:~ convert a path to xml :) +declare function tree:nest($path as xs:string,$del as xs:string) +as element(*) +{ + let $path:=if(starts-with($path,$del)) then $path else $del || $path + let $parts:=fn:tokenize(($path),"\" || $del) + return fn:fold-right(subsequence($parts,1,count($parts)-1), + , + tree:wrap#2 + ) +}; + +declare function tree:wrap($this as xs:string,$acc) +as element(*) +{ + {$acc} +}; + + +declare function tree:merge($a1 as element(*)?,$a2 as element(*)?) +as element(*)* +{ + if($a1/@name=$a2/@name) then + let $n1:=$a1/* + let $n2:=$a2/* + + let $t:=( + for $x in fn:distinct-values($n1/@name[.=$n2/@name]) (:both:) + return tree:merge($a1/*[@name=$x],$a2/*[@name=$x]), + + for $x in fn:distinct-values($n1/@name[fn:not(.=$n2/@name)]) (:only $a1 :) + return $a1/*[@name=$x], + + for $x in fn:distinct-values($n2/@name[fn:not(.=$n1/@name)]) (:only $a2 :) + return $a2/*[@name=$x] + ) + return tree:wrap($a1/@name,for $x in $t order by $x/@name return $x) + else + ($a1,$a2) +}; + diff --git a/webapp/lsp-manager/lib.xq/wrangler.xqm b/webapp/lsp-manager/lib.xq/wrangler.xqm new file mode 100644 index 0000000..570d2ef --- /dev/null +++ b/webapp/lsp-manager/lib.xq/wrangler.xqm @@ -0,0 +1,199 @@ +xquery version '3.1'; +(:~ +Library to manage running multiple jobs using BaseX job:eval, +where the jobs are the execution of one function for a set of arguments + +The function will have signature `fn($key as xs:string) as element()` +The function will typically create outputs and have side effects. + +State information is persisted with the storage module, using the key '_wrangle' +It is a map{$wid: {"jobs":}} +requires basex 10+ +@licence BSD +@author: quodatum +@date: 2023/02/12 +:) +module namespace wrangle = 'urn:quodatum:wrangler'; +(:~ semantic version :) +declare variable $wrangle:version:="1.0.0"; +(:~ used in bindings to indicate a wrangle job and as store key :) +declare variable $wrangle:id:="_wrangle"; + +(:~ +submit wrangle jobs for each $item +@param wrangle data{xq:...,bindings:..} +@return unique id for the job set +:) +declare function wrangle:queue($items as item()*,$wrangle as map(*)) +as xs:string{ +let $wid := random:uuid() +let $jobs := $items!job:eval($wrangle?xq, + map:merge(($wrangle?bindings(.),map:entry($wrangle:id,$wid))), + map{"cache":true()} + ) + +let $this := map:entry($wid,map:entry("jobs",$jobs!map:entry(., + map{ + "complete":false(), + "details":job:list-details(.) + }))) + +let $_:=store:put($wrangle:id,map:merge((($this,wrangle:store())))) +return $wid +}; + +(:~ active wrangle ids :) +declare function wrangle:active() +as xs:string*{ + job:list()!job:bindings(.)?($wrangle:id)=>distinct-values() +}; + +(:~ known wrangles :) +declare function wrangle:list() +as xs:string*{ + wrangle:store()=>map:keys() +}; + +(:~ details for $wid wrangle :) +declare function wrangle:list-details($wid as xs:string) +as map(*){ + wrangle:store()=>map:get($wid) +}; + +(:~ all wrangled jobs :) +declare function wrangle:job-list() +as xs:string*{ + job:list()[job:bindings(.)=>map:contains($wrangle:id)] +}; + +(:~ jobs for wrangle id :) +declare function wrangle:job-list($wid as xs:string) + as xs:string*{ + job:list()[job:bindings(.)?($wrangle:id) eq $wid] +}; + +(:~ is wrangle id finished (or unknown) :) +declare function wrangle:finished($wid as xs:string) + as xs:string*{ + every $job in job:list()[job:bindings(.)?($wrangle:id) eq $wid] satisfies job:finished($job) +}; + +(:~ wait wrangle id finished (or unknown) :) +declare function wrangle:wait($wid as xs:string) + as empty-sequence(){ + let $done:=every $job in job:list()[job:bindings(.)?($wrangle:id) eq $wid] + satisfies empty(job:wait($job)) + return if($done) then () +}; + +(:~ cancel wrangle id :) +declare function wrangle:remove($wid as xs:string) +as empty-sequence(){ + job:list()[job:bindings(.)?($wrangle:id) eq $wid]!job:remove(.), + store:put($wrangle:id,wrangle:store()=>map:remove($wid)) +}; + +(:~ tally of non-zero job status for $wid "scheduled", "queued", "running", "cached" :) +declare function wrangle:status($wid as xs:string) +as map(*){ + wrangle:job-list($wid)!job:list-details(.)/@state/string() + =>fold-left(map{},wrangle:tally-count#2) +}; + +(:~ job-results with no error as sequence:) +declare function wrangle:results($wid as xs:string) +as item()*{ + wrangle:job-list($wid)!wrangle:job-result(.)[not(?error)]?result +}; + +(:~ error counts keyed on $err:code :) +declare function wrangle:errors($wid as xs:string) +as map(*){ + wrangle:job-list($wid)!wrangle:job-result(.)[?error]?result?code!string() + =>fold-left(map{},wrangle:tally-count#2) +}; + +(:~ key is $err:code values are joblists :) +declare function wrangle:jobs-by-error($wid as xs:string) +as map(*){ + (for $jobId in wrangle:job-list($wid) + let $result:=wrangle:job-result($jobId)[?error] + where exists($result) + return map:entry($result?result?code!string(),$jobId) + ) +=> map:merge( map{"duplicates":"combine"}) +}; + +(:~ return key for job:) +declare function wrangle:job-key($jobId as xs:string) +as xs:string{ +let $b:=job:bindings($jobId) +return $b?(map:keys($b)[. ne $wrangle:id]) +}; + +(:~ return map from peek at result:) +declare function wrangle:job-result($jobId as xs:string) +as map(*){ + try{ + map{ + "error":false(), + "result": job:result($jobId,map{"keep":true()}) + } + }catch *{ + map{ + "error":true(), + "result": map{"description": $err:description, + "code": $err:code, + "line": $err:column-number, + "additional": $err:additional, + "value":$err:value + } + } + } +}; + +(:~ XQuery for background service :) +declare function wrangle:service() +as xs:string{ +``[ +import module namespace wrangle = 'urn:quodatum:wrangler:v1'; at "`{ static-base-uri() }`"; +let $done:=wrangle:job-list()[job:finished(.)] +if(exists($done)) +then let $w:=store:get($wrangle:id) + for $job in $done + group by $wid= job:bindings($job)?($wrangle:id) + for $job in $job + let $_:=store:put($wrangle:id) +]`` + +}; +(:~ schedule as service :) +declare function wrangle:schedule-service() +as xs:string{ + wrangle:service() + =>job:eval((), map { 'id':$wrangle:id, 'service':true(), + 'interval': 'PT1S','log': $wrangle:id}) +}; + +(:~ cached data as map :) +declare function wrangle:store() +as map(*){ + store:get-or-put($wrangle:id,function(){map{}}) +}; + +(:~ @return map string->count for fold-left :) +declare %private function wrangle:tally-count($r as map(*),$this as xs:string) +as map(*){ + map:merge( + (map:entry($this,if(map:contains($r,$this)) then $r($this)+1 else 1),$r), + map{"duplicates":"use-first"} + ) +}; +(:~ @return map string->(string*) for fold-left :) +declare %private function wrangle:tally-list($r as map(*),$key as xs:string,$value as xs:string) +as map(*){ + map:merge( + (map:entry($key,$value),$r), + map{"duplicates":"combine"} + ) +}; \ No newline at end of file diff --git a/webapp/lsp-manager/views/components.htm b/webapp/lsp-manager/views/components.htm new file mode 100644 index 0000000..67702c2 --- /dev/null +++ b/webapp/lsp-manager/views/components.htm @@ -0,0 +1,58 @@ + + + + + Components + + + +
+

Components (test page) wc

+

You have no direct messages + +

+ + Available Trees + + +
+ + Slide 1 + Slide 2 + Slide 3 + Slide 4 + Slide 5 + Slide 6 + Slide 21 + Slide 22 + Slide 23 + Slide 24 + Slide 25 + Slide 26 + + +
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/dev/dba-iframe.htm b/webapp/lsp-manager/views/dev/dba-iframe.htm new file mode 100644 index 0000000..1f76fb4 --- /dev/null +++ b/webapp/lsp-manager/views/dev/dba-iframe.htm @@ -0,0 +1,21 @@ + + + + + dev + + + +
+ + DEV + dba + + + +
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/dev/home.htm b/webapp/lsp-manager/views/dev/home.htm new file mode 100644 index 0000000..729ddc8 --- /dev/null +++ b/webapp/lsp-manager/views/dev/home.htm @@ -0,0 +1,40 @@ + + + + + Dev home + + + +
+ + + list tasks etc +
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/dev/job.htm b/webapp/lsp-manager/views/dev/job.htm new file mode 100644 index 0000000..e31942c --- /dev/null +++ b/webapp/lsp-manager/views/dev/job.htm @@ -0,0 +1,28 @@ + + + + + Jobs + + + +
+
+ + Jobs home + jobs 4 + + Refresh +
+ +

Jobs

+
+
+ Nothing Yet! +
+ +
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/error.htm b/webapp/lsp-manager/views/error.htm new file mode 100644 index 0000000..3f5c2de --- /dev/null +++ b/webapp/lsp-manager/views/error.htm @@ -0,0 +1,27 @@ + + + + + Error + + + +
+

Error:

+
+
Description
+
+
Value
+
+
Module
+
[,]
+
Additional
+
+

+      
+
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/home.htm b/webapp/lsp-manager/views/home.htm new file mode 100644 index 0000000..13cb0d7 --- /dev/null +++ b/webapp/lsp-manager/views/home.htm @@ -0,0 +1,64 @@ + + + + +Home + + +
+ +
+

ver

+ + Joke + +

+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+

Contacts

+ + + + + + + + + + +
NameEmail
+

Add A Contact

+
+ + +
+
+ +
+ + \ No newline at end of file diff --git a/webapp/lsp-manager/views/job/home.htm b/webapp/lsp-manager/views/job/home.htm new file mode 100644 index 0000000..b40c99f --- /dev/null +++ b/webapp/lsp-manager/views/job/home.htm @@ -0,0 +1,56 @@ + + + + + Jobs home + + + +
+ + + +
+
+
+
+
Something + ,covers:
+
    +
  • aaa
  • +
+
+
+
+
+
+
+
Job tasks
+ + + +
+

Last updated 3 mins ago

+
+
+
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/job/list.htm b/webapp/lsp-manager/views/job/list.htm new file mode 100644 index 0000000..e31942c --- /dev/null +++ b/webapp/lsp-manager/views/job/list.htm @@ -0,0 +1,28 @@ + + + + + Jobs + + + +
+
+ + Jobs home + jobs 4 + + Refresh +
+ +

Jobs

+
+
+ Nothing Yet! +
+ +
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/layout.htm b/webapp/lsp-manager/views/layout.htm new file mode 100644 index 0000000..f33c025 --- /dev/null +++ b/webapp/lsp-manager/views/layout.htm @@ -0,0 +1,157 @@ + + + + + + + + MDUI + + + + + + + + + + + + + + + + + + +
+ +
+

MAIN

+
+
+ + hi + LoggedIn: ?? + + + +
+ + + +
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/login.htm b/webapp/lsp-manager/views/login.htm new file mode 100644 index 0000000..3eb4da1 --- /dev/null +++ b/webapp/lsp-manager/views/login.htm @@ -0,0 +1,25 @@ + + + + + Login + + + +
+
+
+ + + + +
+ + +
+
+ + \ No newline at end of file diff --git a/webapp/lsp-manager/views/notifications.htm b/webapp/lsp-manager/views/notifications.htm new file mode 100644 index 0000000..93238a5 --- /dev/null +++ b/webapp/lsp-manager/views/notifications.htm @@ -0,0 +1,15 @@ + + + + + Notifications + + + +
+

Notifications

+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/pdf/home.htm b/webapp/lsp-manager/views/pdf/home.htm new file mode 100644 index 0000000..94ed1ae --- /dev/null +++ b/webapp/lsp-manager/views/pdf/home.htm @@ -0,0 +1,78 @@ + + + + + PDF home + + + +
+ + + +
+
+
+
+
PDF slugs + ,covers:
+
    +
  • aaa
  • +
+
+
+
+
+
+
+
PDF tasks
+ +
+ +
+ + +
+ +
+
+ update covers + + +
+ +
+ +
+

Last updated 3 mins ago

+
+
+
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/pdf/item-iframe.htm b/webapp/lsp-manager/views/pdf/item-iframe.htm new file mode 100644 index 0000000..7e3cea2 --- /dev/null +++ b/webapp/lsp-manager/views/pdf/item-iframe.htm @@ -0,0 +1,16 @@ + + + + + PDF-view + + + +
+ +
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/pdf/item.htm b/webapp/lsp-manager/views/pdf/item.htm new file mode 100644 index 0000000..f1b0b59 --- /dev/null +++ b/webapp/lsp-manager/views/pdf/item.htm @@ -0,0 +1,60 @@ + + + + + PDF + + + +
+ + PDF + list + # + + +
+
+ Image of cover page. + +
+
+
+
Card title
+

This is a wider card with supporting text below as a natural lead-in to additional + content. This card has even longer content than the first to show that equal height action.

+

Last updated 3 mins ago

+
+
+ +
+
+
Tasks
+
+
+ + + +
+ + +
+
+
+

Last updated 3 mins ago

+
+
+ +
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/pdf/list.htm b/webapp/lsp-manager/views/pdf/list.htm new file mode 100644 index 0000000..be60fff --- /dev/null +++ b/webapp/lsp-manager/views/pdf/list.htm @@ -0,0 +1,53 @@ + + + + + Pdfs + + + +
+ + PDF + list (#) + + +
+ + + +
+ +
path
+ {pdf.index} +
+ + + {pdf} + + + + +
+
+ + Image of cover page. + +
+
+ +
+ slug:
{pdf.id}
+
+
+
+ + +
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/pdf/settings.htm b/webapp/lsp-manager/views/pdf/settings.htm new file mode 100644 index 0000000..168c1a3 --- /dev/null +++ b/webapp/lsp-manager/views/pdf/settings.htm @@ -0,0 +1,97 @@ + + + + + Settings + + + +
+ + PDF + settings + +
+
+ Settings +
+ +
+ +
+
+
+ +
+
+ + +
+ +
+
+
+
+ +
+ + +
+ + None + Fade + slide-fade + bounce + +
+
+
+
+
+
+ Save + Reset to default values +
+
+ +
+ + + + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/profile.htm b/webapp/lsp-manager/views/profile.htm new file mode 100644 index 0000000..0d45d02 --- /dev/null +++ b/webapp/lsp-manager/views/profile.htm @@ -0,0 +1,17 @@ + + + + + Profile + + + +
+

{uid} profile

+
+
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/test.htm b/webapp/lsp-manager/views/test.htm new file mode 100644 index 0000000..aa7c478 --- /dev/null +++ b/webapp/lsp-manager/views/test.htm @@ -0,0 +1,42 @@ + + + + + test + + + +
+ + +
+ +
path
+ {pdf.index} +
+ + {pdf} + + + + +
+ +
+
+ + A kitten sits patiently. + +
+
+ slug:
{pdf.id}
+
+
+
+
+ + + \ No newline at end of file diff --git a/webapp/lsp-manager/views/tweets.htm b/webapp/lsp-manager/views/tweets.htm new file mode 100644 index 0000000..bf3928f --- /dev/null +++ b/webapp/lsp-manager/views/tweets.htm @@ -0,0 +1,67 @@ + + + + +Tweets + + +
+

Tweets

+
+ +
+ 2020-07-15T09:17:00-04:00 +
+
Another use case for 'this.context' I think might be valid: forms. They're too painful right now.
+
+
+ +
+ 2024-04-15T09:17:00-04:00
+
I just published “What will Datasmoothie bring to the analytics startup landscape?” https://medium.com/@afanasjevas/what-will-datasmoothie-bring-to-the-analytics-startup-landscape-f7dab70d75c3?source=tw-81c4e81fe6f8-1427630532296
+
+
+ +
52m52 minutes ago
+
new talks uploaded on our YouTube page - check them out http://bit.ly/1yoXSAO
+
+ +
+ + diff --git a/webapp/lsp-manager/views/unknown.htm b/webapp/lsp-manager/views/unknown.htm new file mode 100644 index 0000000..6c0fa95 --- /dev/null +++ b/webapp/lsp-manager/views/unknown.htm @@ -0,0 +1,19 @@ + + + + + 404 + + + +
+

Page not found:

+
    +
  • Page: pdf2/${ path }
  • +
  • Method: ${ method }
  • +
+
+ + + \ No newline at end of file diff --git a/webapp/lsp/etc/snippets.json b/webapp/lsp/etc/snippets.json new file mode 100644 index 0000000..3eb5801 --- /dev/null +++ b/webapp/lsp/etc/snippets.json @@ -0,0 +1,266 @@ +{ + "new library module": { + "isFileTemplate": true, + "prefix": "library module", + "body": [ + "xquery version '3.1';", + "(:~", + "@author: ", + "@date: $CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE", + ":)", + "module namespace ${1:prefix} = '${2:http://www.example.com/}';", + "" + ], + "description": "New library module template" + }, + "new main module": { + "isFileTemplate": true, + "prefix": "main module", + "body": [ + "xquery version '3.1';", + "(:~", + ":)", + "${1:expr}", + "" + ], + "description": "New main module template" + }, + "flowr": { + "prefix": [ + "for", + "flowr" + ], + "body": [ + "for \\$${1:var} at \\$${2:pos} in ${3:expr}", + "let \\$${4:var2} := ${5:expr}", + "where ${6:boolean}", + "order by ${7:expr}", + "return ${8:expr2}" + ], + "description": "Full FLOWR expression" + }, + "return": { + "prefix": "return", + "body": "return ${1:expr}" + }, + "import": { + "prefix": "import", + "body": "import module namespace ${1:ns} = '${2:http://www.example.com/}';", + "description": "Import module" + }, + "if": { + "prefix": "if", + "body": [ + "if (${1:boolean})", + "then ${2:expr1}", + "else ${3:expr2}" + ], + "description": "If then else expression" + }, + "module": { + "prefix": "module", + "body": "module namespace ${1:ns} = '${2:http://www.example.com}';" + }, + "every": { + "prefix": "every", + "body": "every \\$${1:varname} in ${2:expr} satisfies ${3:expr}" + }, + "some": { + "prefix": "some", + "body": "some \\$${1:varname} in ${2:expr} satisfies ${3:expr}" + }, + "declare namespace": { + "prefix": [ + "declare", + "namespace" + ], + "body": [ + "declare ${1:prefix}='${2:namespace}';", + "" + ], + "description": "declare namespace" + }, + "declare base-uri": { + "prefix": [ + "declare", + "baseuri" + ], + "body": [ + "declare base-uri '${1:uriliteral}';", + "" + ], + "description": "declare base-uri" + }, + "declare option": { + "prefix": [ + "declare", + "option" + ], + "body": [ + "declare option ${1:eqname} '${2:string}';", + "" + ], + "description": "declare option" + }, + "declare function": { + "prefix": [ + "declare", + "function" + ], + "body": [ + "(:~ ${1:name} :)", + "declare function ${2:ns}:${1:name}()", + "as ${3:type}{", + "${3:expr}", + "};", + "" + ], + "description": "declare function" + }, + "declare variable": { + "prefix": [ + "declare", + "variable" + ], + "body": [ + "(:~ \\$${1:varname} :)", + "declare variable \\$${1:varname} := ${2:expr};", + "" + ], + "description": "declare variable" + }, + "switch": { + "prefix": "switch", + "body": [ + "switch(${1:foo})", + "case ${2:foo} return ${3:true}", + "default return ${4:false}" + ], + "description": "switch statement" + }, + "typeswitch": { + "prefix": "type", + "body": [ + "typeswitch(${1:foo})", + "case ${2:foo} return ${3:true}", + "default return ${4:false}" + ], + "description": "typeswitch statement" + }, + "try": { + "prefix": "try", + "body": [ + "try {", + " ${1:expr}", + "} catch ${2:*}", + " { ${3:expr}", + "}" + ], + "description": "try catch" + }, + "tumbling": { + "prefix": [ + "for", + "tumbling", + "window" + ], + "body": [ + "for tumbling window \\$${1:varname} in ${2:expr}", + "start at \\$${3:start} when ${4:expr}", + "end at \\$${5:end} when ${6:expr}", + "return ${7:expr}" + ], + "description": "tumbling window" + }, + "sliding": { + "prefix": [ + "for", + "sliding", + "window" + ], + "body": [ + "for sliding window \\$${1:varname} in ${2:expr}", + "start at \\$${3:start} when ${4:expr}", + "end at \\$${5:end} when ${6:expr}", + "return ${7:expr}" + ], + "description": "sliding window" + }, + "let": { + "prefix": "let", + "body": "let \\$${1:varname} := ${2:expr}" + }, + "castable": { + "body": "castable as ${1:atomicType}" + }, + "cast": { + "body": "cast as ${1:atomicType}" + }, + + "update insert": { + "prefix": [ + "update", + "insert" + ], + "body": "insert node ${1:expr} into ${2:xpath}" + }, + "update delete": { + "prefix": ["delete","update"], + "body": "delete node ${1:xpath}" + }, + "update replace node": { + "prefix":["update","replace"], + "body": "replace node ${1:xpath} with ${2:expr}" + }, + "update replace value": { + "prefix": [ "update", + "replace", + "value" + ], + "body": "replace value of node ${1:xpath} with ${2:expr}" + }, + "update rename": { + "prefix": [ + "update", + "rename" + ], + "body": "rename node ${1:xpath} as ${2:eqname}" + }, + "copy modify return": { + "prefix": [ + "copy", + "modify", + "return" + ], + "body": [ + "copy \\$${1:varname} := ${2:node}", + "modify ${3:updates}", + "return \\$${1:varname}" + ] + }, + "transform with": { + "prefix": [ + "transform", + "with", + "update" + ], + "body": [ + "${1:node} transform with {", + " ${2:update}", + "}" + ] + }, + "transform update": { + "prefix": [ + "transform", + "update" + ], + "body": [ + "${1:node} update {", + "${2:update}", + "}" + ] + } +} + + diff --git a/webapp/lsp/lsp-text.xqm b/webapp/lsp/lsp-text.xqm index 661a2d4..83102be 100644 --- a/webapp/lsp/lsp-text.xqm +++ b/webapp/lsp/lsp-text.xqm @@ -50,9 +50,9 @@ declare function lsp-text:completion($json as map(*)) as map(*)? { - let $doc:=$json?params?textDocument?uri + let $uri:=$json?params?textDocument?uri let $context:=$json?params?context (:{"triggerCharacter":":","triggerKind":2.0e0}:) - let $result:=comp:list($context) + let $result:=comp:list($context)=>prof:time("⏱️ completions " || $uri) return rpc:result($json,array:build($result)) }; diff --git a/webapp/lsp/providers/completions.xqm b/webapp/lsp/providers/completions.xqm index 9b14a99..d903d40 100644 --- a/webapp/lsp/providers/completions.xqm +++ b/webapp/lsp/providers/completions.xqm @@ -2,13 +2,15 @@ module namespace comp = 'lsp-completions'; import module namespace lspt = 'lsp-typedefs' at "../lsp-typedefs.xqm"; import module namespace ctx="lsp/context" at "../context.xqm"; +declare variable $comp:snippet-source:="../etc/snippets.json"; +declare variable $comp:snippets:=json:doc($comp:snippet-source,{"format":"w3"}); (: (:{"triggerCharacter":":","triggerKind":2.0e0}:):) declare function comp:list($context as map(*)) as lspt:CompletionItem* { message($context,"context: "), - ctx:functions("fn")!ctx:map(.)=>trace("aaa") + ctx:functions("fn")!ctx:map(.) }; declare function comp:dummy($context as map(*)) diff --git a/webapp/lsp/providers/documentSymbols.xqm b/webapp/lsp/providers/documentSymbols.xqm index 5550161..4c136f4 100644 --- a/webapp/lsp/providers/documentSymbols.xqm +++ b/webapp/lsp/providers/documentSymbols.xqm @@ -5,17 +5,18 @@ import module namespace hnd="lsp/handlers" at "../handlers.xqm"; import module namespace lspt = 'lsp-typedefs' at "../lsp-typedefs.xqm"; import module namespace pos="lsp/position" at "../position.xqm"; -declare function syms:list($parse as element(),$text as xs:string) -as lspt:DocumentSymbol*{ - let $actions as hnd:actionMap :={ +declare variable $syms:actions as hnd:actionMap :={ "ContextValueDecl": syms:action#2, "VarDecl": syms:VarDecl#2, "FunctionDecl": syms:FunctionDecl#2, "ItemTypeDecl": syms:action#2, - "NamedRecordTypeDecl": syms:action#2 - } + "NamedRecordTypeDecl": syms:NamedRecordTypeDecl#2 + }; + +declare function syms:list($parse as element(),$text as xs:string) +as lspt:DocumentSymbol*{ let $state:= hnd:Result(()) - let $result:= hnd:walk($parse,$actions,$state) + let $result:= hnd:walk($parse,$syms:actions,$state) return $result?result }; @@ -42,7 +43,7 @@ as hnd:Result{ let $name:=syms:localName($parse/VarNameAndType/EQName) let $length:=string($parse)=>string-length() let $range:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3)) - let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Variable'),$range,$range,"TODO") + let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Variable'),$range,$range,"VAR") return ($state?result,$sym)=>hnd:Result(true()) }; @@ -50,7 +51,16 @@ declare function syms:FunctionDecl($parse as element(FunctionDecl),$state as hn as hnd:Result{ let $name:=syms:localName($parse/UnreservedFunctionEQName) let $range:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3)) - let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Method'),$range,$range,"TODO") + let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('Method'),$range,$range,"FUN") + return ($state?result,$sym)=>hnd:Result(true()) +}; + +declare function syms:NamedRecordTypeDecl($parse as element(NamedRecordTypeDecl), $state as hnd:Result ) +as hnd:Result{ + let $name:=syms:localName($parse/EQName) + let $range:=lspt:Range(lspt:Position(0,0),lspt:Position(0,3)) + let $sym:=lspt:DocumentSymbol($name,$lspt:SymbolKindMap('TypeParameter'),$range,$range,"--RECORD") + =>trace("RECORD") return ($state?result,$sym)=>hnd:Result(true()) }; diff --git a/webapp/static/clients/codemirror/grail.css b/webapp/static/clients/codemirror/grail.css index 53ed78a..96f37fe 100644 --- a/webapp/static/clients/codemirror/grail.css +++ b/webapp/static/clients/codemirror/grail.css @@ -63,6 +63,12 @@ form header { overflow: auto; } +details[open]::details-content { + color: dodgerblue; + padding: 0.1em; + border: thin solid grey; + overflow:auto; +} ::backdrop { backdrop-filter: blur(2px); } @@ -78,6 +84,7 @@ form header { } } + .page-header { grid-column: 1 / -1; background: #ffcdd2; diff --git a/webapp/static/clients/codemirror/grail.html b/webapp/static/clients/codemirror/grail.html index 823357a..b57b126 100644 --- a/webapp/static/clients/codemirror/grail.html +++ b/webapp/static/clients/codemirror/grail.html @@ -26,7 +26,7 @@ Editor