@valtzu/codemirror-lang-el 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,37 +8,48 @@
8
8
  ```html
9
9
  <div id="editor"></div>
10
10
  <script type="importmap">
11
- {
12
- "imports": {
13
- "codemirror": "https://esm.sh/codemirror@6.0.1",
14
- "@codemirror/autocomplete": "https://esm.sh/@codemirror/autocomplete@6.9.0",
15
- "@codemirror/view": "https://esm.sh/@codemirror/view@6.17.1",
16
- "@valtzu/codemirror-lang-el": "https://esm.sh/@valtzu/codemirror-lang-el@0.2.1"
17
- }
18
- }
11
+ {
12
+ "imports": {
13
+ "codemirror": "https://esm.sh/*codemirror@6.0.1",
14
+ "@codemirror/state": "https://esm.sh/*@codemirror/state@6.4.1",
15
+ "@codemirror/search": "https://esm.sh/*@codemirror/search@6.5.6",
16
+ "@codemirror/autocomplete": "https://esm.sh/*@codemirror/autocomplete@6.9.0",
17
+ "@codemirror/view": "https://esm.sh/*@codemirror/view@6.17.1",
18
+ "@codemirror/commands": "https://esm.sh/*@codemirror/commands@6.2.5",
19
+ "@codemirror/language": "https://esm.sh/*@codemirror/language@6.9.0",
20
+ "@codemirror/lint": "https://esm.sh/*@codemirror/lint@6.4.1",
21
+ "@lezer/lr": "https://esm.sh/*@lezer/lr@1.3.9",
22
+ "@lezer/highlight": "https://esm.sh/*@lezer/highlight@1.1.6",
23
+ "@lezer/common": "https://esm.sh/*@lezer/common@1.2.1",
24
+ "style-mod": "https://esm.sh/*style-mod@4.1.2",
25
+ "w3c-keyname": "https://esm.sh/*w3c-keyname@2.2.8",
26
+ "crelt": "https://esm.sh/*crelt@1.0.6",
27
+ "@valtzu/codemirror-lang-el": "https://esm.sh/*@valtzu/codemirror-lang-el@0.2.3"
28
+ }
29
+ }
19
30
  </script>
20
31
  <script type="module">
21
- import {EditorView, basicSetup} from "codemirror";
22
- import { expressionlanguage } from "@valtzu/codemirror-lang-el";
23
- import {acceptCompletion} from "@codemirror/autocomplete";
24
- import {keymap} from "@codemirror/view";
32
+ import { EditorView, basicSetup } from "codemirror";
33
+ import { acceptCompletion } from "@codemirror/autocomplete";
34
+ import { keymap } from "@codemirror/view";
35
+ import { expressionlanguage } from "@valtzu/codemirror-lang-el";
25
36
 
26
- let editor = new EditorView({
27
- extensions: [
28
- basicSetup,
29
- keymap.of([{key: "Tab", run: acceptCompletion}]),
30
- expressionlanguage({
31
- identifiers: [
32
- { name: 'foo' },
33
- { name: 'bar' }
34
- ],
35
- functions: [
36
- {name: 'smh'},
37
- {name: 'smash_my_head', args: ['object']},
38
- ],
39
- })
40
- ],
41
- parent: document.getElementById('editor'),
42
- });
37
+ let editor = new EditorView({
38
+ extensions: [
39
+ basicSetup,
40
+ keymap.of([{key: "Tab", run: acceptCompletion}]),
41
+ expressionlanguage({
42
+ identifiers: [
43
+ { name: 'foo', info: 'Foo is a variable' },
44
+ { name: 'bar' }
45
+ ],
46
+ functions: [
47
+ { name: 'smh' },
48
+ { name: 'smash_my_head', args: ['object'], info: 'This is a function' },
49
+ ],
50
+ })
51
+ ],
52
+ parent: document.getElementById('editor'),
53
+ });
43
54
  </script>
44
55
  ```
package/dist/index.cjs CHANGED
@@ -6,11 +6,12 @@ var lr = require('@lezer/lr');
6
6
  var language = require('@codemirror/language');
7
7
  var highlight = require('@lezer/highlight');
8
8
  var lint = require('@codemirror/lint');
9
+ var view = require('@codemirror/view');
9
10
 
10
11
  // This file was generated by lezer-generator. You probably shouldn't edit it.
11
12
  const parser = lr.LRParser.deserialize({
12
13
  version: 14,
13
- states: "$nQVQPOOO!QQQO'#ChO!VQPO'#CjO!^QPO'#CmOOQO'#Cs'#CsOOQO'#Cn'#CnQVQPOOO!eQQO,59SOOQO,59U,59UO!jQPO,59UOOQO,59X,59XO!rQPO,59XOOQO-E6l-E6lOVQPO1G.nOVQPO'#CoO!yQPO1G.pOOQO1G.p1G.pOOQO1G.s1G.sO#RQPO7+$YOOQO,59Z,59ZOOQO-E6m-E6mOOQO7+$[7+$[OOQO<<Gt<<GtO#ZQQO<<GtO#`QQOAN=`OVQPOG22zO#eQPOLD(fOOQO!$'LQ!$'LQ",
14
+ states: "$nQVQPOOO!QQQO'#ChO!VQPO'#CjO!^QPO'#CmOOQO'#Cs'#CsOOQO'#Cn'#CnQVQPOOO!eQQO,59SOOQO,59U,59UO!jQQO,59UOOQO,59X,59XO!rQPO,59XOOQO-E6l-E6lOVQPO1G.nOVQPO'#CoO!yQQO1G.pOOQO1G.p1G.pOOQO1G.s1G.sO#RQQO7+$YOOQO,59Z,59ZOOQO-E6m-E6mOOQO7+$[7+$[OOQO<<Gt<<GtO#ZQQO<<GtO#`QQOAN=`OVQPOG22zO#eQPOLD(fOOQO!$'LQ!$'LQ",
14
15
  stateData: "#r~OfOS~OQSORSOSSOTSOUSOVSOWSOXSOYSOZSO`ROiPOmQO~O]VO~OlWO~PVO_YO~PVOj]O~Ok^Ol`O~O_aO~PVOk^OleO~OhfOkgO~O]hO~OjiO~OhkO~OQUTXWRW~",
15
16
  goto: "!ghPPPPPPPPPPPPiPiPPis}PPP!TaSOQRUZ]^iQUOQZRT[UZQ_XRd_WTORUZQXQQb]Qc^Rji",
16
17
  nodeNames: "⚠ Program Number Identifier String Boolean Null Operator OperatorKeyword Punctuation NullSafe NullCoalescing Object ObjectKey Array ClosingBracket OpeningBracket Application",
@@ -21,33 +22,71 @@ const parser = lr.LRParser.deserialize({
21
22
  ],
22
23
  skippedNodes: [0],
23
24
  repeatNodeCount: 2,
24
- tokenData: "By~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&{wx'Txy(xyz(}z{)S{|$z|})[}!O$z!O!P)a!P!Q$z!Q![*r![!],R!^!_$r!_!`,Y!`!a$r!a!b,`!c!},u!}#O-Y#P#Q-_#Q#R$z#R#S,u#T#U-d#U#V,u#V#W.}#W#X,u#X#Y2q#Y#Z5e#Z#],u#]#^8P#^#a,u#a#b8j#b#c;Y#c#d>s#d#g,u#g#h?^#h#iAc#i#o,u#o#pBg#p#qBl#q#rBt#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SXOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u<%l~%P~O%P~~%oR%vO]QSPR%yRO;'S%P;'S;=`&S;=`O%PR&VYOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u;=`<%l%P<%l~%P~O%P~~%oR&xP;=`<%l%P~'QPV~vw$zR'WXOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%oR'vRO;'S'T;'S;=`(P;=`O'TR(SYOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%oR(uP;=`<%l'T~(}O`~~)SO_~~)XPV~z{$z~)aOk~~)fQXP!O!P$z!Q![)l~)qSQ~!Q![)l!g!h)}#R#S*l#X#Y)}~*QR{|*Z}!O*Z!Q![*a~*^P!Q![*a~*fQQ~!Q![*a#R#S*Z~*oP!Q![)l~*wTQ~!O!P+W!Q![*r!g!h)}#R#S+{#X#Y)}~+ZSO!O+g!P;'S+g;'S;=`+u<%lO+g~+lRQ~!Q![)l!g!h)}#X#Y)}~+xP;=`<%l+g~,OP!Q![*rR,YOjQXP~,]P!_!`$r~,eQXP!O!P,k!a!b,p~,pOY~~,uOZ~R,|S]QRP!Q![,u!c!},u#R#S,u#T#o,u~-_Om~~-dOl~R-kU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c-}#c#o,uR.UU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X.h#X#o,uR.qS]QWPRP!Q![,u!c!},u#R#S,u#T#o,uR/UU]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d/h#d#o,uR/oU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c0R#c#o,uR0YU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i0l#i#o,uR0sT]QRP!Q![,u!c!},u#R#S,u#T#U1S#U#o,uR1ZU]QRP!Q![,u!c!},u#R#S,u#T#],u#]#^1m#^#o,uR1tU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c2W#c#o,uR2_U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h.h#h#o,uR2xU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c3[#c#o,uR3cU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X3u#X#o,uR3|U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h4`#h#o,uR4gT]QRPpq4v!Q![,u!c!},u#R#S,u#T#o,uP4yP#k#l4|P5PP#]#^5SP5VP#h#i5YP5]P#[#]5`P5eOWPR5lT]QRP!Q![,u!c!},u#R#S,u#T#U5{#U#o,uR6SU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a6f#a#o,uR6mU]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h7P#h#o,uR7WU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y7j#Y#o,uR7sS]QTPRP!Q![,u!c!},u#R#S,u#T#o,uR8WU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c.h#c#o,uR8qT]QRP!Q![,u!c!},u#R#S,u#T#U9Q#U#o,uR9XU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i9k#i#o,uR9rU]QRP!Q![,u!c!},u#R#S,u#T#V,u#V#W:U#W#o,uR:]U]QRP!Q![,u!c!},u#R#S,u#T#[,u#[#]:o#]#o,uR:vU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y2W#Y#o,uR;aW]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d;y#d#i,u#i#j=Y#j#o,uR<QU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i<d#i#o,uR<mT]QWPRPpq<|!Q![,u!c!},u#R#S,u#T#o,uP=PP#]#^=SP=VP#b#c5`R=aU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a=s#a#o,uR=zU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a>^#a#o,uR>gS]QUPRP!Q![,u!c!},u#R#S,u#T#o,uR>zU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g.h#g#o,uR?eU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i?w#i#o,uR@OT]QRP!Q![,u!c!},u#R#S,u#T#U@_#U#o,uR@fU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g@x#g#o,uRAPU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i3u#i#o,uRAjU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#gA|#g#o,uRBTU]QRP!Q![,u!c!},u#R#S,u#T#i,u#i#j7P#j#o,u~BlOi~~BqPV~#p#q$z~ByOh~",
25
+ tokenData: "B{~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&{wx'Txy(xyz(}z{)S{|$z|})[}!O$z!O!P)c!P!Q$z!Q![*t![!],T!^!_$r!_!`,[!`!a$r!a!b,b!c!},w!}#O-[#P#Q-a#Q#R$z#R#S,w#T#U-f#U#V,w#V#W/P#W#X,w#X#Y2s#Y#Z5g#Z#],w#]#^8R#^#a,w#a#b8l#b#c;[#c#d>u#d#g,w#g#h?`#h#iAe#i#o,w#o#pBi#p#qBn#q#rBv#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SXOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u<%l~%P~O%P~~%oR%vO]QSPR%yRO;'S%P;'S;=`&S;=`O%PR&VYOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u;=`<%l%P<%l~%P~O%P~~%oR&xP;=`<%l%P~'QPV~vw$zR'WXOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%oR'vRO;'S'T;'S;=`(P;=`O'TR(SYOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%oR(uP;=`<%l'T~(}O`~~)SO_~~)XPV~z{$zR)cOkQXP~)hQXP!O!P$z!Q![)n~)sSQ~!Q![)n!g!h*P#R#S*n#X#Y*P~*SR{|*]}!O*]!Q![*c~*`P!Q![*c~*hQQ~!Q![*c#R#S*]~*qP!Q![)n~*yTQ~!O!P+Y!Q![*t!g!h*P#R#S+}#X#Y*P~+]SO!O+i!P;'S+i;'S;=`+w<%lO+i~+nRQ~!Q![)n!g!h*P#X#Y*P~+zP;=`<%l+i~,QP!Q![*tR,[OjQXP~,_P!_!`$r~,gQXP!O!P,m!a!b,r~,rOY~~,wOZ~R-OS]QRP!Q![,w!c!},w#R#S,w#T#o,w~-aOm~~-fOl~R-mU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c.P#c#o,wR.WU]QRP!Q![,w!c!},w#R#S,w#T#W,w#W#X.j#X#o,wR.sS]QWPRP!Q![,w!c!},w#R#S,w#T#o,wR/WU]QRP!Q![,w!c!},w#R#S,w#T#c,w#c#d/j#d#o,wR/qU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c0T#c#o,wR0[U]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i0n#i#o,wR0uT]QRP!Q![,w!c!},w#R#S,w#T#U1U#U#o,wR1]U]QRP!Q![,w!c!},w#R#S,w#T#],w#]#^1o#^#o,wR1vU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c2Y#c#o,wR2aU]QRP!Q![,w!c!},w#R#S,w#T#g,w#g#h.j#h#o,wR2zU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c3^#c#o,wR3eU]QRP!Q![,w!c!},w#R#S,w#T#W,w#W#X3w#X#o,wR4OU]QRP!Q![,w!c!},w#R#S,w#T#g,w#g#h4b#h#o,wR4iT]QRPpq4x!Q![,w!c!},w#R#S,w#T#o,wP4{P#k#l5OP5RP#]#^5UP5XP#h#i5[P5_P#[#]5bP5gOWPR5nT]QRP!Q![,w!c!},w#R#S,w#T#U5}#U#o,wR6UU]QRP!Q![,w!c!},w#R#S,w#T#`,w#`#a6h#a#o,wR6oU]QRP!Q![,w!c!},w#R#S,w#T#g,w#g#h7R#h#o,wR7YU]QRP!Q![,w!c!},w#R#S,w#T#X,w#X#Y7l#Y#o,wR7uS]QTPRP!Q![,w!c!},w#R#S,w#T#o,wR8YU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c.j#c#o,wR8sT]QRP!Q![,w!c!},w#R#S,w#T#U9S#U#o,wR9ZU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i9m#i#o,wR9tU]QRP!Q![,w!c!},w#R#S,w#T#V,w#V#W:W#W#o,wR:_U]QRP!Q![,w!c!},w#R#S,w#T#[,w#[#]:q#]#o,wR:xU]QRP!Q![,w!c!},w#R#S,w#T#X,w#X#Y2Y#Y#o,wR;cW]QRP!Q![,w!c!},w#R#S,w#T#c,w#c#d;{#d#i,w#i#j=[#j#o,wR<SU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i<f#i#o,wR<oT]QWPRPpq=O!Q![,w!c!},w#R#S,w#T#o,wP=RP#]#^=UP=XP#b#c5bR=cU]QRP!Q![,w!c!},w#R#S,w#T#`,w#`#a=u#a#o,wR=|U]QRP!Q![,w!c!},w#R#S,w#T#`,w#`#a>`#a#o,wR>iS]QUPRP!Q![,w!c!},w#R#S,w#T#o,wR>|U]QRP!Q![,w!c!},w#R#S,w#T#f,w#f#g.j#g#o,wR?gU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i?y#i#o,wR@QT]QRP!Q![,w!c!},w#R#S,w#T#U@a#U#o,wR@hU]QRP!Q![,w!c!},w#R#S,w#T#f,w#f#g@z#g#o,wRARU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i3w#i#o,wRAlU]QRP!Q![,w!c!},w#R#S,w#T#f,w#f#gBO#g#o,wRBVU]QRP!Q![,w!c!},w#R#S,w#T#i,w#i#j7R#j#o,w~BnOi~~BsPV~#p#q$z~B{Oh~",
25
26
  tokenizers: [0, 1],
26
27
  topRules: {"Program":[0,1]},
27
28
  tokenPrec: 118
28
29
  });
29
30
 
30
31
  const identifier = /^[a-zA-Z_]+[a-zA-Z_0-9]*$/;
32
+ const isFunction = (identifier, config) => { var _a; return (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier); };
33
+ const isVariable = (identifier, config) => { var _a; return (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.find(variable => variable.name === identifier); };
31
34
  const expressionLanguageLinter = (config) => lint.linter(view => {
32
35
  let diagnostics = [];
36
+ let previousNode = null;
33
37
  language.syntaxTree(view.state).cursor().iterate(node => {
34
- var _a, _b;
38
+ var _a;
35
39
  if (node.name == "Identifier") {
40
+ if ((previousNode === null || previousNode === void 0 ? void 0 : previousNode.name) == "Identifier" && ((_a = node.node.parent) === null || _a === void 0 ? void 0 : _a.name) != 'Array') {
41
+ diagnostics.push({
42
+ from: node.from,
43
+ to: node.to,
44
+ severity: 'error',
45
+ message: `Unexpected identifier after another identifier`,
46
+ });
47
+ }
36
48
  const identifier = view.state.sliceDoc(node.from, node.to);
37
- const isFunction = (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier);
38
- const isVariable = (_b = config.identifiers) === null || _b === void 0 ? void 0 : _b.filter(variable => variable.name === identifier);
39
- if (!isFunction && !isVariable) {
49
+ if (!isFunction(identifier, config) && !isVariable(identifier, config)) {
40
50
  diagnostics.push({
41
51
  from: node.from,
42
52
  to: node.to,
43
- severity: 'warning',
53
+ severity: 'error',
44
54
  message: `Identifier "${identifier}" not found`,
45
55
  });
46
56
  }
47
57
  }
58
+ previousNode = node.node;
48
59
  });
49
60
  return diagnostics;
50
61
  });
62
+ const keywordTooltip = (config) => view.hoverTooltip((view, pos, side) => {
63
+ var _a, _b;
64
+ let { from, to, text } = view.state.doc.lineAt(pos);
65
+ let start = pos, end = pos;
66
+ while (start > from && /\w/.test(text[start - from - 1]))
67
+ start--;
68
+ while (end < to && /\w/.test(text[end - from]))
69
+ end++;
70
+ if (start == pos && side < 0 || end == pos && side > 0) {
71
+ return null;
72
+ }
73
+ const keyword = text.slice(start - from, end - from);
74
+ const info = (_b = ((_a = isFunction(keyword, config)) !== null && _a !== void 0 ? _a : isVariable(keyword, config))) === null || _b === void 0 ? void 0 : _b.info;
75
+ if (!info) {
76
+ return null;
77
+ }
78
+ return {
79
+ pos: start,
80
+ end,
81
+ above: true,
82
+ create(view) {
83
+ let dom = document.createElement("div");
84
+ dom.textContent = info;
85
+ dom.className = 'cm-diagnostic';
86
+ return { dom };
87
+ },
88
+ };
89
+ });
51
90
  const ELLanguage = language.LRLanguage.define({
52
91
  parser: parser.configure({
53
92
  props: [
@@ -76,57 +115,73 @@ const ELLanguage = language.LRLanguage.define({
76
115
  }),
77
116
  languageData: {}
78
117
  });
79
- function completeOperatorKeyword(state, config, tree, from, to) {
118
+ function completeOperatorKeyword(state, config, tree, from, to, explicit) {
80
119
  var _a, _b;
81
120
  const text = state.sliceDoc(from, to);
82
121
  return {
83
122
  from,
84
123
  to,
85
- options: (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.filter(value => value.startsWith(text)).map(keyword => ({ label: keyword, type: "property" }))) !== null && _b !== void 0 ? _b : [],
86
- validFor: (text) => { var _a, _b; return (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.some(value => value.startsWith(text))) !== null && _b !== void 0 ? _b : false; },
124
+ options: (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => explicit || name.startsWith(text)).map(({ name, info, detail }) => ({ label: name, apply: `${name} `, info, detail, type: "keyword" }))) !== null && _b !== void 0 ? _b : [],
125
+ validFor: (text) => { var _a, _b; return (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.some(({ name }) => explicit || name.startsWith(text))) !== null && _b !== void 0 ? _b : false; },
87
126
  };
88
127
  }
89
- function completeIdentifier(state, config, tree, from, to) {
90
- var _a, _b, _c, _d, _e, _f;
128
+ function completeIdentifier(state, config, tree, from, to, explicit) {
129
+ var _a, _b, _c, _d, _e, _f, _g;
91
130
  const text = state.sliceDoc(from, to);
92
- const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
93
- const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
131
+ const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => explicit || name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
132
+ const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => explicit || name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
133
+ const prevName = (_e = tree.prevSibling) === null || _e === void 0 ? void 0 : _e.name;
134
+ const apply = (name) => !prevName || !['OpeningBracket', 'Operator', 'OperatorKeyword', 'Punctuation'].includes(prevName) ? `${name} ` : name;
94
135
  return {
95
136
  from,
96
137
  to,
97
138
  options: [
98
- ...((_e = identifiers.map(({ name, info, detail }) => ({ label: name, info, detail, type: 'variable' }))) !== null && _e !== void 0 ? _e : []),
99
- ...((_f = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _f !== void 0 ? _f : []),
139
+ ...((_f = identifiers.map(({ name, info, detail }) => ({ label: name, apply: apply(name), info, detail, type: 'variable' }))) !== null && _f !== void 0 ? _f : []),
140
+ ...((_g = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _g !== void 0 ? _g : []),
100
141
  ],
101
142
  validFor: identifier,
102
143
  };
103
144
  }
104
145
  function expressionLanguageCompletionFor(config, context) {
105
- let { state, pos } = context;
146
+ let { state, pos, explicit } = context;
106
147
  let tree = language.syntaxTree(state).resolveInner(pos, -1);
148
+ const isOperator = (node) => ['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(node.name);
149
+ const isIdentifier = (node) => node.name === 'Identifier';
107
150
  if (tree.name == 'String') {
108
151
  return null;
109
152
  }
110
- if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(tree.prevSibling.name)) {
111
- return completeOperatorKeyword(state, config, tree, tree.from, pos);
153
+ if (tree.prevSibling && !isOperator(tree.prevSibling) && (!explicit || !isOperator(tree.node))) {
154
+ return completeOperatorKeyword(state, config, tree, tree.from, pos, explicit);
112
155
  }
113
- if (tree.name == "Identifier") {
114
- return completeIdentifier(state, config, tree, tree.from, pos);
156
+ if ((!tree.prevSibling || !isIdentifier(tree.prevSibling) || isOperator(tree.node)) && (explicit || isIdentifier(tree.node))) {
157
+ return completeIdentifier(state, config, tree, isIdentifier(tree.node) ? tree.from : pos, pos, explicit);
115
158
  }
116
159
  return null;
117
160
  }
118
161
  function expressionLanguageCompletionSourceWith(config) {
119
162
  var _a;
120
- (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = ['starts with', 'ends with', 'contains', 'matches', 'not in', 'in', 'not', 'or', 'and']);
163
+ (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = [
164
+ { name: 'starts with' },
165
+ { name: 'ends with' },
166
+ { name: 'contains' },
167
+ { name: 'matches' },
168
+ { name: 'not in' },
169
+ { name: 'in' },
170
+ { name: 'not' },
171
+ { name: 'or' },
172
+ { name: 'and' },
173
+ ]);
121
174
  return (context) => expressionLanguageCompletionFor(config, context);
122
175
  }
123
176
  function expressionlanguage(config = {}, extensions = []) {
124
177
  return new language.LanguageSupport(ELLanguage, [
125
178
  ELLanguage.data.of({ autocomplete: expressionLanguageCompletionSourceWith(config) }),
126
179
  expressionLanguageLinter(config),
180
+ keywordTooltip(config),
127
181
  ...extensions,
128
182
  ]);
129
183
  }
130
184
 
131
185
  exports.ELLanguage = ELLanguage;
132
186
  exports.expressionlanguage = expressionlanguage;
187
+ exports.keywordTooltip = keywordTooltip;
package/dist/index.d.cts CHANGED
@@ -10,8 +10,13 @@ interface ExpressionLanguageConfig {
10
10
  args: string[];
11
11
  info?: string;
12
12
  }[];
13
- operatorKeywords?: readonly string[];
13
+ operatorKeywords?: readonly {
14
+ name: string;
15
+ detail?: string;
16
+ info?: string;
17
+ }[];
14
18
  }
19
+ declare const keywordTooltip: (config: ExpressionLanguageConfig) => import("@codemirror/state").Extension;
15
20
  declare const ELLanguage: LRLanguage;
16
21
  declare function expressionlanguage(config?: ExpressionLanguageConfig, extensions?: Array<any>): LanguageSupport;
17
- export { ExpressionLanguageConfig, ELLanguage, expressionlanguage };
22
+ export { ExpressionLanguageConfig, keywordTooltip, ELLanguage, expressionlanguage };
package/dist/index.d.ts CHANGED
@@ -10,8 +10,13 @@ interface ExpressionLanguageConfig {
10
10
  args: string[];
11
11
  info?: string;
12
12
  }[];
13
- operatorKeywords?: readonly string[];
13
+ operatorKeywords?: readonly {
14
+ name: string;
15
+ detail?: string;
16
+ info?: string;
17
+ }[];
14
18
  }
19
+ declare const keywordTooltip: (config: ExpressionLanguageConfig) => import("@codemirror/state").Extension;
15
20
  declare const ELLanguage: LRLanguage;
16
21
  declare function expressionlanguage(config?: ExpressionLanguageConfig, extensions?: Array<any>): LanguageSupport;
17
- export { ExpressionLanguageConfig, ELLanguage, expressionlanguage };
22
+ export { ExpressionLanguageConfig, keywordTooltip, ELLanguage, expressionlanguage };
package/dist/index.js CHANGED
@@ -2,11 +2,12 @@ import { LRParser } from '@lezer/lr';
2
2
  import { LRLanguage, indentNodeProp, delimitedIndent, foldNodeProp, foldInside, LanguageSupport, syntaxTree } from '@codemirror/language';
3
3
  import { styleTags, tags } from '@lezer/highlight';
4
4
  import { linter } from '@codemirror/lint';
5
+ import { hoverTooltip } from '@codemirror/view';
5
6
 
6
7
  // This file was generated by lezer-generator. You probably shouldn't edit it.
7
8
  const parser = LRParser.deserialize({
8
9
  version: 14,
9
- states: "$nQVQPOOO!QQQO'#ChO!VQPO'#CjO!^QPO'#CmOOQO'#Cs'#CsOOQO'#Cn'#CnQVQPOOO!eQQO,59SOOQO,59U,59UO!jQPO,59UOOQO,59X,59XO!rQPO,59XOOQO-E6l-E6lOVQPO1G.nOVQPO'#CoO!yQPO1G.pOOQO1G.p1G.pOOQO1G.s1G.sO#RQPO7+$YOOQO,59Z,59ZOOQO-E6m-E6mOOQO7+$[7+$[OOQO<<Gt<<GtO#ZQQO<<GtO#`QQOAN=`OVQPOG22zO#eQPOLD(fOOQO!$'LQ!$'LQ",
10
+ states: "$nQVQPOOO!QQQO'#ChO!VQPO'#CjO!^QPO'#CmOOQO'#Cs'#CsOOQO'#Cn'#CnQVQPOOO!eQQO,59SOOQO,59U,59UO!jQQO,59UOOQO,59X,59XO!rQPO,59XOOQO-E6l-E6lOVQPO1G.nOVQPO'#CoO!yQQO1G.pOOQO1G.p1G.pOOQO1G.s1G.sO#RQQO7+$YOOQO,59Z,59ZOOQO-E6m-E6mOOQO7+$[7+$[OOQO<<Gt<<GtO#ZQQO<<GtO#`QQOAN=`OVQPOG22zO#eQPOLD(fOOQO!$'LQ!$'LQ",
10
11
  stateData: "#r~OfOS~OQSORSOSSOTSOUSOVSOWSOXSOYSOZSO`ROiPOmQO~O]VO~OlWO~PVO_YO~PVOj]O~Ok^Ol`O~O_aO~PVOk^OleO~OhfOkgO~O]hO~OjiO~OhkO~OQUTXWRW~",
11
12
  goto: "!ghPPPPPPPPPPPPiPiPPis}PPP!TaSOQRUZ]^iQUOQZRT[UZQ_XRd_WTORUZQXQQb]Qc^Rji",
12
13
  nodeNames: "⚠ Program Number Identifier String Boolean Null Operator OperatorKeyword Punctuation NullSafe NullCoalescing Object ObjectKey Array ClosingBracket OpeningBracket Application",
@@ -17,33 +18,71 @@ const parser = LRParser.deserialize({
17
18
  ],
18
19
  skippedNodes: [0],
19
20
  repeatNodeCount: 2,
20
- tokenData: "By~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&{wx'Txy(xyz(}z{)S{|$z|})[}!O$z!O!P)a!P!Q$z!Q![*r![!],R!^!_$r!_!`,Y!`!a$r!a!b,`!c!},u!}#O-Y#P#Q-_#Q#R$z#R#S,u#T#U-d#U#V,u#V#W.}#W#X,u#X#Y2q#Y#Z5e#Z#],u#]#^8P#^#a,u#a#b8j#b#c;Y#c#d>s#d#g,u#g#h?^#h#iAc#i#o,u#o#pBg#p#qBl#q#rBt#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SXOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u<%l~%P~O%P~~%oR%vO]QSPR%yRO;'S%P;'S;=`&S;=`O%PR&VYOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u;=`<%l%P<%l~%P~O%P~~%oR&xP;=`<%l%P~'QPV~vw$zR'WXOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%oR'vRO;'S'T;'S;=`(P;=`O'TR(SYOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%oR(uP;=`<%l'T~(}O`~~)SO_~~)XPV~z{$z~)aOk~~)fQXP!O!P$z!Q![)l~)qSQ~!Q![)l!g!h)}#R#S*l#X#Y)}~*QR{|*Z}!O*Z!Q![*a~*^P!Q![*a~*fQQ~!Q![*a#R#S*Z~*oP!Q![)l~*wTQ~!O!P+W!Q![*r!g!h)}#R#S+{#X#Y)}~+ZSO!O+g!P;'S+g;'S;=`+u<%lO+g~+lRQ~!Q![)l!g!h)}#X#Y)}~+xP;=`<%l+g~,OP!Q![*rR,YOjQXP~,]P!_!`$r~,eQXP!O!P,k!a!b,p~,pOY~~,uOZ~R,|S]QRP!Q![,u!c!},u#R#S,u#T#o,u~-_Om~~-dOl~R-kU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c-}#c#o,uR.UU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X.h#X#o,uR.qS]QWPRP!Q![,u!c!},u#R#S,u#T#o,uR/UU]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d/h#d#o,uR/oU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c0R#c#o,uR0YU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i0l#i#o,uR0sT]QRP!Q![,u!c!},u#R#S,u#T#U1S#U#o,uR1ZU]QRP!Q![,u!c!},u#R#S,u#T#],u#]#^1m#^#o,uR1tU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c2W#c#o,uR2_U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h.h#h#o,uR2xU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c3[#c#o,uR3cU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X3u#X#o,uR3|U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h4`#h#o,uR4gT]QRPpq4v!Q![,u!c!},u#R#S,u#T#o,uP4yP#k#l4|P5PP#]#^5SP5VP#h#i5YP5]P#[#]5`P5eOWPR5lT]QRP!Q![,u!c!},u#R#S,u#T#U5{#U#o,uR6SU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a6f#a#o,uR6mU]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h7P#h#o,uR7WU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y7j#Y#o,uR7sS]QTPRP!Q![,u!c!},u#R#S,u#T#o,uR8WU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c.h#c#o,uR8qT]QRP!Q![,u!c!},u#R#S,u#T#U9Q#U#o,uR9XU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i9k#i#o,uR9rU]QRP!Q![,u!c!},u#R#S,u#T#V,u#V#W:U#W#o,uR:]U]QRP!Q![,u!c!},u#R#S,u#T#[,u#[#]:o#]#o,uR:vU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y2W#Y#o,uR;aW]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d;y#d#i,u#i#j=Y#j#o,uR<QU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i<d#i#o,uR<mT]QWPRPpq<|!Q![,u!c!},u#R#S,u#T#o,uP=PP#]#^=SP=VP#b#c5`R=aU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a=s#a#o,uR=zU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a>^#a#o,uR>gS]QUPRP!Q![,u!c!},u#R#S,u#T#o,uR>zU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g.h#g#o,uR?eU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i?w#i#o,uR@OT]QRP!Q![,u!c!},u#R#S,u#T#U@_#U#o,uR@fU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g@x#g#o,uRAPU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i3u#i#o,uRAjU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#gA|#g#o,uRBTU]QRP!Q![,u!c!},u#R#S,u#T#i,u#i#j7P#j#o,u~BlOi~~BqPV~#p#q$z~ByOh~",
21
+ tokenData: "B{~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&{wx'Txy(xyz(}z{)S{|$z|})[}!O$z!O!P)c!P!Q$z!Q![*t![!],T!^!_$r!_!`,[!`!a$r!a!b,b!c!},w!}#O-[#P#Q-a#Q#R$z#R#S,w#T#U-f#U#V,w#V#W/P#W#X,w#X#Y2s#Y#Z5g#Z#],w#]#^8R#^#a,w#a#b8l#b#c;[#c#d>u#d#g,w#g#h?`#h#iAe#i#o,w#o#pBi#p#qBn#q#rBv#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SXOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u<%l~%P~O%P~~%oR%vO]QSPR%yRO;'S%P;'S;=`&S;=`O%PR&VYOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u;=`<%l%P<%l~%P~O%P~~%oR&xP;=`<%l%P~'QPV~vw$zR'WXOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%oR'vRO;'S'T;'S;=`(P;=`O'TR(SYOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%oR(uP;=`<%l'T~(}O`~~)SO_~~)XPV~z{$zR)cOkQXP~)hQXP!O!P$z!Q![)n~)sSQ~!Q![)n!g!h*P#R#S*n#X#Y*P~*SR{|*]}!O*]!Q![*c~*`P!Q![*c~*hQQ~!Q![*c#R#S*]~*qP!Q![)n~*yTQ~!O!P+Y!Q![*t!g!h*P#R#S+}#X#Y*P~+]SO!O+i!P;'S+i;'S;=`+w<%lO+i~+nRQ~!Q![)n!g!h*P#X#Y*P~+zP;=`<%l+i~,QP!Q![*tR,[OjQXP~,_P!_!`$r~,gQXP!O!P,m!a!b,r~,rOY~~,wOZ~R-OS]QRP!Q![,w!c!},w#R#S,w#T#o,w~-aOm~~-fOl~R-mU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c.P#c#o,wR.WU]QRP!Q![,w!c!},w#R#S,w#T#W,w#W#X.j#X#o,wR.sS]QWPRP!Q![,w!c!},w#R#S,w#T#o,wR/WU]QRP!Q![,w!c!},w#R#S,w#T#c,w#c#d/j#d#o,wR/qU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c0T#c#o,wR0[U]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i0n#i#o,wR0uT]QRP!Q![,w!c!},w#R#S,w#T#U1U#U#o,wR1]U]QRP!Q![,w!c!},w#R#S,w#T#],w#]#^1o#^#o,wR1vU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c2Y#c#o,wR2aU]QRP!Q![,w!c!},w#R#S,w#T#g,w#g#h.j#h#o,wR2zU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c3^#c#o,wR3eU]QRP!Q![,w!c!},w#R#S,w#T#W,w#W#X3w#X#o,wR4OU]QRP!Q![,w!c!},w#R#S,w#T#g,w#g#h4b#h#o,wR4iT]QRPpq4x!Q![,w!c!},w#R#S,w#T#o,wP4{P#k#l5OP5RP#]#^5UP5XP#h#i5[P5_P#[#]5bP5gOWPR5nT]QRP!Q![,w!c!},w#R#S,w#T#U5}#U#o,wR6UU]QRP!Q![,w!c!},w#R#S,w#T#`,w#`#a6h#a#o,wR6oU]QRP!Q![,w!c!},w#R#S,w#T#g,w#g#h7R#h#o,wR7YU]QRP!Q![,w!c!},w#R#S,w#T#X,w#X#Y7l#Y#o,wR7uS]QTPRP!Q![,w!c!},w#R#S,w#T#o,wR8YU]QRP!Q![,w!c!},w#R#S,w#T#b,w#b#c.j#c#o,wR8sT]QRP!Q![,w!c!},w#R#S,w#T#U9S#U#o,wR9ZU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i9m#i#o,wR9tU]QRP!Q![,w!c!},w#R#S,w#T#V,w#V#W:W#W#o,wR:_U]QRP!Q![,w!c!},w#R#S,w#T#[,w#[#]:q#]#o,wR:xU]QRP!Q![,w!c!},w#R#S,w#T#X,w#X#Y2Y#Y#o,wR;cW]QRP!Q![,w!c!},w#R#S,w#T#c,w#c#d;{#d#i,w#i#j=[#j#o,wR<SU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i<f#i#o,wR<oT]QWPRPpq=O!Q![,w!c!},w#R#S,w#T#o,wP=RP#]#^=UP=XP#b#c5bR=cU]QRP!Q![,w!c!},w#R#S,w#T#`,w#`#a=u#a#o,wR=|U]QRP!Q![,w!c!},w#R#S,w#T#`,w#`#a>`#a#o,wR>iS]QUPRP!Q![,w!c!},w#R#S,w#T#o,wR>|U]QRP!Q![,w!c!},w#R#S,w#T#f,w#f#g.j#g#o,wR?gU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i?y#i#o,wR@QT]QRP!Q![,w!c!},w#R#S,w#T#U@a#U#o,wR@hU]QRP!Q![,w!c!},w#R#S,w#T#f,w#f#g@z#g#o,wRARU]QRP!Q![,w!c!},w#R#S,w#T#h,w#h#i3w#i#o,wRAlU]QRP!Q![,w!c!},w#R#S,w#T#f,w#f#gBO#g#o,wRBVU]QRP!Q![,w!c!},w#R#S,w#T#i,w#i#j7R#j#o,w~BnOi~~BsPV~#p#q$z~B{Oh~",
21
22
  tokenizers: [0, 1],
22
23
  topRules: {"Program":[0,1]},
23
24
  tokenPrec: 118
24
25
  });
25
26
 
26
27
  const identifier = /^[a-zA-Z_]+[a-zA-Z_0-9]*$/;
28
+ const isFunction = (identifier, config) => { var _a; return (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier); };
29
+ const isVariable = (identifier, config) => { var _a; return (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.find(variable => variable.name === identifier); };
27
30
  const expressionLanguageLinter = (config) => linter(view => {
28
31
  let diagnostics = [];
32
+ let previousNode = null;
29
33
  syntaxTree(view.state).cursor().iterate(node => {
30
- var _a, _b;
34
+ var _a;
31
35
  if (node.name == "Identifier") {
36
+ if ((previousNode === null || previousNode === void 0 ? void 0 : previousNode.name) == "Identifier" && ((_a = node.node.parent) === null || _a === void 0 ? void 0 : _a.name) != 'Array') {
37
+ diagnostics.push({
38
+ from: node.from,
39
+ to: node.to,
40
+ severity: 'error',
41
+ message: `Unexpected identifier after another identifier`,
42
+ });
43
+ }
32
44
  const identifier = view.state.sliceDoc(node.from, node.to);
33
- const isFunction = (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier);
34
- const isVariable = (_b = config.identifiers) === null || _b === void 0 ? void 0 : _b.filter(variable => variable.name === identifier);
35
- if (!isFunction && !isVariable) {
45
+ if (!isFunction(identifier, config) && !isVariable(identifier, config)) {
36
46
  diagnostics.push({
37
47
  from: node.from,
38
48
  to: node.to,
39
- severity: 'warning',
49
+ severity: 'error',
40
50
  message: `Identifier "${identifier}" not found`,
41
51
  });
42
52
  }
43
53
  }
54
+ previousNode = node.node;
44
55
  });
45
56
  return diagnostics;
46
57
  });
58
+ const keywordTooltip = (config) => hoverTooltip((view, pos, side) => {
59
+ var _a, _b;
60
+ let { from, to, text } = view.state.doc.lineAt(pos);
61
+ let start = pos, end = pos;
62
+ while (start > from && /\w/.test(text[start - from - 1]))
63
+ start--;
64
+ while (end < to && /\w/.test(text[end - from]))
65
+ end++;
66
+ if (start == pos && side < 0 || end == pos && side > 0) {
67
+ return null;
68
+ }
69
+ const keyword = text.slice(start - from, end - from);
70
+ const info = (_b = ((_a = isFunction(keyword, config)) !== null && _a !== void 0 ? _a : isVariable(keyword, config))) === null || _b === void 0 ? void 0 : _b.info;
71
+ if (!info) {
72
+ return null;
73
+ }
74
+ return {
75
+ pos: start,
76
+ end,
77
+ above: true,
78
+ create(view) {
79
+ let dom = document.createElement("div");
80
+ dom.textContent = info;
81
+ dom.className = 'cm-diagnostic';
82
+ return { dom };
83
+ },
84
+ };
85
+ });
47
86
  const ELLanguage = LRLanguage.define({
48
87
  parser: parser.configure({
49
88
  props: [
@@ -72,56 +111,71 @@ const ELLanguage = LRLanguage.define({
72
111
  }),
73
112
  languageData: {}
74
113
  });
75
- function completeOperatorKeyword(state, config, tree, from, to) {
114
+ function completeOperatorKeyword(state, config, tree, from, to, explicit) {
76
115
  var _a, _b;
77
116
  const text = state.sliceDoc(from, to);
78
117
  return {
79
118
  from,
80
119
  to,
81
- options: (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.filter(value => value.startsWith(text)).map(keyword => ({ label: keyword, type: "property" }))) !== null && _b !== void 0 ? _b : [],
82
- validFor: (text) => { var _a, _b; return (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.some(value => value.startsWith(text))) !== null && _b !== void 0 ? _b : false; },
120
+ options: (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => explicit || name.startsWith(text)).map(({ name, info, detail }) => ({ label: name, apply: `${name} `, info, detail, type: "keyword" }))) !== null && _b !== void 0 ? _b : [],
121
+ validFor: (text) => { var _a, _b; return (_b = (_a = config.operatorKeywords) === null || _a === void 0 ? void 0 : _a.some(({ name }) => explicit || name.startsWith(text))) !== null && _b !== void 0 ? _b : false; },
83
122
  };
84
123
  }
85
- function completeIdentifier(state, config, tree, from, to) {
86
- var _a, _b, _c, _d, _e, _f;
124
+ function completeIdentifier(state, config, tree, from, to, explicit) {
125
+ var _a, _b, _c, _d, _e, _f, _g;
87
126
  const text = state.sliceDoc(from, to);
88
- const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
89
- const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
127
+ const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => explicit || name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
128
+ const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => explicit || name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
129
+ const prevName = (_e = tree.prevSibling) === null || _e === void 0 ? void 0 : _e.name;
130
+ const apply = (name) => !prevName || !['OpeningBracket', 'Operator', 'OperatorKeyword', 'Punctuation'].includes(prevName) ? `${name} ` : name;
90
131
  return {
91
132
  from,
92
133
  to,
93
134
  options: [
94
- ...((_e = identifiers.map(({ name, info, detail }) => ({ label: name, info, detail, type: 'variable' }))) !== null && _e !== void 0 ? _e : []),
95
- ...((_f = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _f !== void 0 ? _f : []),
135
+ ...((_f = identifiers.map(({ name, info, detail }) => ({ label: name, apply: apply(name), info, detail, type: 'variable' }))) !== null && _f !== void 0 ? _f : []),
136
+ ...((_g = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _g !== void 0 ? _g : []),
96
137
  ],
97
138
  validFor: identifier,
98
139
  };
99
140
  }
100
141
  function expressionLanguageCompletionFor(config, context) {
101
- let { state, pos } = context;
142
+ let { state, pos, explicit } = context;
102
143
  let tree = syntaxTree(state).resolveInner(pos, -1);
144
+ const isOperator = (node) => ['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(node.name);
145
+ const isIdentifier = (node) => node.name === 'Identifier';
103
146
  if (tree.name == 'String') {
104
147
  return null;
105
148
  }
106
- if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(tree.prevSibling.name)) {
107
- return completeOperatorKeyword(state, config, tree, tree.from, pos);
149
+ if (tree.prevSibling && !isOperator(tree.prevSibling) && (!explicit || !isOperator(tree.node))) {
150
+ return completeOperatorKeyword(state, config, tree, tree.from, pos, explicit);
108
151
  }
109
- if (tree.name == "Identifier") {
110
- return completeIdentifier(state, config, tree, tree.from, pos);
152
+ if ((!tree.prevSibling || !isIdentifier(tree.prevSibling) || isOperator(tree.node)) && (explicit || isIdentifier(tree.node))) {
153
+ return completeIdentifier(state, config, tree, isIdentifier(tree.node) ? tree.from : pos, pos, explicit);
111
154
  }
112
155
  return null;
113
156
  }
114
157
  function expressionLanguageCompletionSourceWith(config) {
115
158
  var _a;
116
- (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = ['starts with', 'ends with', 'contains', 'matches', 'not in', 'in', 'not', 'or', 'and']);
159
+ (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = [
160
+ { name: 'starts with' },
161
+ { name: 'ends with' },
162
+ { name: 'contains' },
163
+ { name: 'matches' },
164
+ { name: 'not in' },
165
+ { name: 'in' },
166
+ { name: 'not' },
167
+ { name: 'or' },
168
+ { name: 'and' },
169
+ ]);
117
170
  return (context) => expressionLanguageCompletionFor(config, context);
118
171
  }
119
172
  function expressionlanguage(config = {}, extensions = []) {
120
173
  return new LanguageSupport(ELLanguage, [
121
174
  ELLanguage.data.of({ autocomplete: expressionLanguageCompletionSourceWith(config) }),
122
175
  expressionLanguageLinter(config),
176
+ keywordTooltip(config),
123
177
  ...extensions,
124
178
  ]);
125
179
  }
126
180
 
127
- export { ELLanguage, expressionlanguage };
181
+ export { ELLanguage, expressionlanguage, keywordTooltip };
package/package.json CHANGED
@@ -41,5 +41,5 @@
41
41
  "access": "public",
42
42
  "registry": "https://registry.npmjs.org/"
43
43
  },
44
- "version": "0.2.2"
44
+ "version": "0.3.0"
45
45
  }