@valtzu/codemirror-lang-el 0.2.3 → 0.4.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
@@ -1,44 +1,76 @@
1
- # Symfony Expression Language support for CodeMirror
1
+ ### Symfony Expression Language support for CodeMirror
2
2
 
3
- ### Example
3
+ #### Features
4
4
 
5
- ![image](https://github.com/valtzu/codemirror-lang-el/assets/652734/928ea2e8-6061-46c9-8ac1-16f95fb5661c)
5
+ 1. Autocompletion for variables, functions, operator keywords – with (some) type resolving
6
+ 2. Hover tooltip
7
+ 3. Linting
6
8
 
9
+ #### Example
10
+
11
+ ![image](https://github.com/valtzu/codemirror-lang-el/assets/652734/c139cc50-077e-4f58-87e4-7ce404a445f4)
7
12
 
8
13
  ```html
9
14
  <div id="editor"></div>
10
15
  <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.3"
17
- }
18
- }
16
+ {
17
+ "imports": {
18
+ "codemirror": "https://esm.sh/*codemirror@6.0.1",
19
+ "@codemirror/state": "https://esm.sh/*@codemirror/state@6.4.1",
20
+ "@codemirror/search": "https://esm.sh/*@codemirror/search@6.5.6",
21
+ "@codemirror/autocomplete": "https://esm.sh/*@codemirror/autocomplete@6.9.0",
22
+ "@codemirror/view": "https://esm.sh/*@codemirror/view@6.17.1",
23
+ "@codemirror/commands": "https://esm.sh/*@codemirror/commands@6.2.5",
24
+ "@codemirror/language": "https://esm.sh/*@codemirror/language@6.9.0",
25
+ "@codemirror/lint": "https://esm.sh/*@codemirror/lint@6.4.1",
26
+ "@lezer/lr": "https://esm.sh/*@lezer/lr@1.3.9",
27
+ "@lezer/highlight": "https://esm.sh/*@lezer/highlight@1.1.6",
28
+ "@lezer/common": "https://esm.sh/*@lezer/common@1.2.1",
29
+ "style-mod": "https://esm.sh/*style-mod@4.1.2",
30
+ "w3c-keyname": "https://esm.sh/*w3c-keyname@2.2.8",
31
+ "crelt": "https://esm.sh/*crelt@1.0.6",
32
+ "@valtzu/codemirror-lang-el": "https://esm.sh/*@valtzu/codemirror-lang-el@0.4.0"
33
+ }
34
+ }
19
35
  </script>
20
36
  <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";
37
+ import { EditorView, basicSetup } from "codemirror";
38
+ import { acceptCompletion } from "@codemirror/autocomplete";
39
+ import { keymap } from "@codemirror/view";
40
+ import { expressionlanguage } from "@valtzu/codemirror-lang-el";
41
+
25
42
 
26
- let editor = new EditorView({
27
- extensions: [
28
- basicSetup,
29
- keymap.of([{key: "Tab", run: acceptCompletion}]),
30
- expressionlanguage({
31
- identifiers: [
32
- { name: 'foo', info: 'Foo is a variable' },
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
- });
43
+ let editor = new EditorView({
44
+ extensions: [
45
+ basicSetup,
46
+ keymap.of([...defaultKeymap, {key: "Tab", run: acceptCompletion}]),
47
+ expressionlanguage({
48
+ types: {
49
+ "User": {
50
+ identifiers: [
51
+ { name: "self", type: ["User"], info: 'Self-reference for property-access demonstration purposes' },
52
+ { name: "name", type: ["string"] },
53
+ { name: "age", type: ["int"], info: "Years since birthday", detail: "years" },
54
+ ],
55
+ functions: [
56
+ { name: "isActive", returnType: ["bool"] },
57
+ { name: "getGroup", args: [], returnType: ["Group"], info: 'Get the user group' },
58
+ ],
59
+ },
60
+ "Group": {
61
+ identifiers: [{ name: "name", type: ["string"] }]
62
+ }
63
+ },
64
+ identifiers: [
65
+ { name: "user", type: ["User"], info: 'This is the user' },
66
+ ],
67
+ functions: [
68
+ { name: "is_granted", args: ["subject", "object"], info: 'Check if subject has permission to the object', returnType: ['bool'] },
69
+ ],
70
+ })
71
+ ],
72
+ parent: document.getElementById('editor'),
73
+ doc: 'is_granted(user, user.self.getGroup())',
74
+ });
43
75
  </script>
44
76
  ```
package/dist/index.cjs CHANGED
@@ -11,30 +11,63 @@ var view = require('@codemirror/view');
11
11
  // This file was generated by lezer-generator. You probably shouldn't edit it.
12
12
  const parser = lr.LRParser.deserialize({
13
13
  version: 14,
14
- 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",
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~",
16
- goto: "!ghPPPPPPPPPPPPiPiPPis}PPP!TaSOQRUZ]^iQUOQZRT[UZQ_XRd_WTORUZQXQQb]Qc^Rji",
17
- nodeNames: "⚠ Program Number Identifier String Boolean Null Operator OperatorKeyword Punctuation NullSafe NullCoalescing Object ObjectKey Array ClosingBracket OpeningBracket Application",
18
- maxTerm: 29,
19
- nodeProps: [
20
- ["openedBy", 15,"OpeningBracket"],
21
- ["closedBy", 16,"ClosingBracket"]
22
- ],
14
+ states: "'[OVQPOOQtQQO'#C^O!YQQOOO#TQPO'#ChO#[QPO'#CiOVQPO'#CnOOQO'#Cu'#CuOVQPO'#CuO#cQPO,58xOVQPO,58|OVQPO,59UOVQPO,59VO#hQPO,58}O#oQQO,59SOOQO,59S,59SO#vQQO,59TOOQO,59T,59TO$QQQO,59YO$hQQO,59aOOQO1G.d1G.dO$oQQO1G.hO$vQQO1G.pO$}QQO1G.qO%eQQO1G.iOOQO1G.i1G.iOVQPO1G.nOVQPO'#CpO%oQPO1G.oOOQO1G.o1G.oOOQO1G.{1G.{OOQO7+$S7+$SOVQPO7+$[O%wQPO7+$TOOQO7+$T7+$TO&PQQO7+$YO&ZQQO,59[OOQO-E6n-E6nOOQO7+$Z7+$ZO&hQQO<<GvOOQO<<Go<<GoOVQPO'#CqO'OQPO<<GtOOQO<<Gt<<GtO'WQQO,59]OOQO-E6o-E6oOOQOAN=`AN=`OVQPO1G.wO'_QQO7+$c",
15
+ stateData: "'s~OhOS~OTQOWUOXUOYUOZUOcTOkSOmVOpRO~ORWOSWO`ZOaZOkXOrYO~Om[ORiXSiX`iXaiXfiXkiXriXqiXjiXniXliXoiX~Oo^O~PVOj`O~PVOTcO~OlhO~PVOqiO~PtOjlOnjO~PtOfbaqbajbanbalbaoba~PtOlmO~PtOjnO~PtOqoO~PtOf_iq_ij_in_il_io_i~PtOlqOnjO~PtOjuOnjO~OlwOnjO~OnxOozO~PtOjdandalda~PtOf^yq^yj^yn^yl^yo^y~PtOnxOo}O~Oq!OO~PtOneqoeq~PtORSc`YZWTY~",
16
+ goto: "#ijPPkPPP{{PPPP{{{{PP{P!]!gPPP!mmQORSTVXYZ[ijox!OmUORSTVXYZ[ijox!OQk_QpgTtkpQyrR|yQPOQ]RQ_SQaTQbVQdXQeYQfZQg[QriQsjQvoQ{xR!P!O",
17
+ nodeNames: "⚠ Expression ObjectAccess NullSafeMemberOf MemberOf Identifier ArrayAccess FunctionCall Number String Boolean Null Object Array TernaryExpression BinaryExpression OperatorKeyword Operator UnaryExpression UnaryOperator",
18
+ maxTerm: 34,
23
19
  skippedNodes: [0],
24
20
  repeatNodeCount: 2,
25
- 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: "AZ~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Ruv$|vw&{wx'Txy(xyz(}z{)S{|)[|})c}!O)[!O!P)h!P!Q$|!Q![)p![!],V!^!_$t!_!`,[!`!a$t!a!b,b!c!},u!}#O-W#P#Q-]#Q#R$|#R#S,u#T#U-b#U#V,u#V#W.u#W#X,u#X#Y2Z#Y#Z4u#Z#],u#]#^7V#^#a,u#a#b7n#b#c:S#c#d=c#d#g,u#g#h=z#h#i?w#i#o,u#o#p@w#p#q@|#q#rAU#r#s$|~$^Sh~XY$XYZ$X]^$Xpq$XR$qPaQcP!_!`$tQ$yPaQ!_!`$|Q%ROaQ~%UXOr%Rrs%qs#O%R#O#P%v#P;'S%R;'S;=`&u<%l~%R~O%R~~%q~%vOX~~%yRO;'S%R;'S;=`&S;=`O%R~&VYOr%Rrs%qs#O%R#O#P%v#P;'S%R;'S;=`&u;=`<%l%R<%l~%R~O%R~~%q~&xP;=`<%l%RQ'QPaQvw$|~'WXOw'Twx%qx#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%q~'vRO;'S'T;'S;=`(P;=`O'T~(SYOw'Twx%qx#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%q~(uP;=`<%l'T~(}Om~~)SOl~Q)XPaQz{$|R)cOaQcP~)hOn~~)mPS~!O!P$|~)uTW~!O!P*U!Q![)p!g!h+U#R#S,P#X#Y+U~*XSO!O*e!P;'S*e;'S;=`+y<%lO*e~*jRW~!Q![*s!g!h+U#X#Y+U~*xSW~!Q![*s!g!h+U#R#S+s#X#Y+U~+XR{|+b}!O+b!Q![+h~+eP!Q![+h~+mQW~!Q![+h#R#S+b~+vP!Q![*s~+|P;=`<%l*e~,SP!Q![)p~,[Oq~Q,_P!_!`$t~,gRr~!O!P,p![!]$|!a!b$|~,uOR~~,zST~!Q![,u!c!},u#R#S,u#T#o,u~-]Ok~~-bOj~~-gUT~!Q![,u!c!},u#R#S,u#T#b,u#b#c-y#c#o,u~.OUT~!Q![,u!c!},u#R#S,u#T#W,u#W#X.b#X#o,u~.iS`~T~!Q![,u!c!},u#R#S,u#T#o,u~.zUT~!Q![,u!c!},u#R#S,u#T#c,u#c#d/^#d#o,u~/cUT~!Q![,u!c!},u#R#S,u#T#b,u#b#c/u#c#o,u~/zUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i0^#i#o,u~0cTT~!Q![,u!c!},u#R#S,u#T#U0r#U#o,u~0wUT~!Q![,u!c!},u#R#S,u#T#],u#]#^1Z#^#o,u~1`UT~!Q![,u!c!},u#R#S,u#T#b,u#b#c1r#c#o,u~1wUT~!Q![,u!c!},u#R#S,u#T#g,u#g#h.b#h#o,u~2`UT~!Q![,u!c!},u#R#S,u#T#b,u#b#c2r#c#o,u~2wUT~!Q![,u!c!},u#R#S,u#T#W,u#W#X3Z#X#o,u~3`UT~!Q![,u!c!},u#R#S,u#T#g,u#g#h3r#h#o,u~3wTT~pq4W!Q![,u!c!},u#R#S,u#T#o,u~4ZP#k#l4^~4aP#]#^4d~4gP#h#i4j~4mP#[#]4p~4uO`~~4zTT~!Q![,u!c!},u#R#S,u#T#U5Z#U#o,u~5`UT~!Q![,u!c!},u#R#S,u#T#`,u#`#a5r#a#o,u~5wUT~!Q![,u!c!},u#R#S,u#T#g,u#g#h6Z#h#o,u~6`UT~!Q![,u!c!},u#R#S,u#T#X,u#X#Y6r#Y#o,u~6ySY~T~!Q![,u!c!},u#R#S,u#T#o,u~7[UT~!Q![,u!c!},u#R#S,u#T#b,u#b#c.b#c#o,u~7sTT~!Q![,u!c!},u#R#S,u#T#U8S#U#o,u~8XUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i8k#i#o,u~8pUT~!Q![,u!c!},u#R#S,u#T#V,u#V#W9S#W#o,u~9XUT~!Q![,u!c!},u#R#S,u#T#[,u#[#]9k#]#o,u~9pUT~!Q![,u!c!},u#R#S,u#T#X,u#X#Y1r#Y#o,u~:XWT~!Q![,u!c!},u#R#S,u#T#c,u#c#d:q#d#i,u#i#j<O#j#o,u~:vUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i;Y#i#o,u~;cTcP`~T~pq;r!Q![,u!c!},u#R#S,u#T#o,u~;uP#]#^;x~;{P#b#c4p~<TUT~!Q![,u!c!},u#R#S,u#T#`,u#`#a<g#a#o,u~<lUT~!Q![,u!c!},u#R#S,u#T#`,u#`#a=O#a#o,u~=VSZ~T~!Q![,u!c!},u#R#S,u#T#o,u~=hUT~!Q![,u!c!},u#R#S,u#T#f,u#f#g.b#g#o,u~>PUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i>c#i#o,u~>hTT~!Q![,u!c!},u#R#S,u#T#U>w#U#o,u~>|UT~!Q![,u!c!},u#R#S,u#T#f,u#f#g?`#g#o,u~?eUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i3Z#i#o,u~?|UT~!Q![,u!c!},u#R#S,u#T#f,u#f#g@`#g#o,u~@eUT~!Q![,u!c!},u#R#S,u#T#i,u#i#j6Z#j#o,u~@|Op~QARPaQ#p#q$|~AZOo~",
26
22
  tokenizers: [0, 1],
27
- topRules: {"Program":[0,1]},
28
- tokenPrec: 118
23
+ topRules: {"Expression":[0,1]},
24
+ tokenPrec: 301
29
25
  });
30
26
 
31
- const identifier = /^[a-zA-Z_]+[a-zA-Z_0-9]*$/;
32
27
  const isFunction = (identifier, config) => { var _a; return (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier); };
33
28
  const isVariable = (identifier, config) => { var _a; return (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.find(variable => variable.name === identifier); };
29
+ const autocompleteFunction = (x) => { var _a, _b, _c; return ({ label: `${x.name}(${((_a = x.args) === null || _a === void 0 ? void 0 : _a.join(',')) || ''})`, apply: `${x.name}(${!((_b = x.args) === null || _b === void 0 ? void 0 : _b.length) ? ')' : ''}`, detail: (_c = x.returnType) === null || _c === void 0 ? void 0 : _c.join('|'), info: x.info, type: "function" }); };
30
+ const autocompleteIdentifier = (x) => { var _a; return ({ label: x.name, apply: x.name, info: x.info, detail: x.detail || ((_a = x.type) === null || _a === void 0 ? void 0 : _a.join('|')), type: 'variable' }); };
34
31
  const expressionLanguageLinter = (config) => lint.linter(view => {
35
32
  let diagnostics = [];
33
+ let previousNode = null;
36
34
  language.syntaxTree(view.state).cursor().iterate(node => {
35
+ var _a, _b, _c, _d, _e;
36
+ if (((_a = node.node.parent) === null || _a === void 0 ? void 0 : _a.name) == 'ObjectAccess' && ((_b = node.node.parent) === null || _b === void 0 ? void 0 : _b.firstChild) && node.name == "Identifier" && node.node.prevSibling) {
37
+ const leftArgument = node.node.parent.firstChild.node;
38
+ const types = resolveTypes(view.state, leftArgument, config, true);
39
+ const identifier = view.state.sliceDoc(node.from, node.to);
40
+ if (((_d = (_c = node.node.parent) === null || _c === void 0 ? void 0 : _c.parent) === null || _d === void 0 ? void 0 : _d.name) === 'FunctionCall') {
41
+ if (!Array.from(types).find(type => { var _a, _b, _c; return (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.functions) === null || _c === void 0 ? void 0 : _c.find(x => x.name === identifier); })) {
42
+ diagnostics.push({
43
+ from: node.from,
44
+ to: node.to,
45
+ severity: 'error',
46
+ message: `Method '${identifier}' not found in ${Array.from(types).join('|')}`,
47
+ });
48
+ }
49
+ }
50
+ else if (types.size > 0) {
51
+ if (!Array.from(types).find(type => { var _a, _b, _c; return (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.identifiers) === null || _c === void 0 ? void 0 : _c.find(x => x.name === identifier); })) {
52
+ diagnostics.push({
53
+ from: node.from,
54
+ to: node.to,
55
+ severity: 'warning',
56
+ message: `Property '${identifier}' not found in ${Array.from(types).join('|')}`,
57
+ });
58
+ }
59
+ }
60
+ return;
61
+ }
37
62
  if (node.name == "Identifier") {
63
+ if ((previousNode === null || previousNode === void 0 ? void 0 : previousNode.name) == "Identifier" && !['Array', 'FunctionCall'].includes(((_e = node.node.parent) === null || _e === void 0 ? void 0 : _e.name) || '')) {
64
+ diagnostics.push({
65
+ from: node.from,
66
+ to: node.to,
67
+ severity: 'error',
68
+ message: `Unexpected identifier after another identifier`,
69
+ });
70
+ }
38
71
  const identifier = view.state.sliceDoc(node.from, node.to);
39
72
  if (!isFunction(identifier, config) && !isVariable(identifier, config)) {
40
73
  diagnostics.push({
@@ -45,28 +78,40 @@ const expressionLanguageLinter = (config) => lint.linter(view => {
45
78
  });
46
79
  }
47
80
  }
81
+ previousNode = node.node;
48
82
  });
49
83
  return diagnostics;
50
84
  });
51
85
  const keywordTooltip = (config) => view.hoverTooltip((view, pos, side) => {
52
- var _a, _b;
53
- let { from, to, text } = view.state.doc.lineAt(pos);
54
- let start = pos, end = pos;
55
- while (start > from && /\w/.test(text[start - from - 1]))
56
- start--;
57
- while (end < to && /\w/.test(text[end - from]))
58
- end++;
59
- if (start == pos && side < 0 || end == pos && side > 0) {
86
+ var _a, _b, _c, _d, _e, _f;
87
+ const tree = language.syntaxTree(view.state).resolveInner(pos, side);
88
+ if (tree.name !== 'Identifier') {
60
89
  return null;
61
90
  }
62
- const keyword = text.slice(start - from, end - from);
63
- const info = (_b = ((_a = isFunction(keyword, config)) !== null && _a !== void 0 ? _a : isVariable(keyword, config))) === null || _b === void 0 ? void 0 : _b.info;
91
+ const skipEmpty = (x) => x;
92
+ let info;
93
+ if (((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.firstChild) && ((_b = tree.parent) === null || _b === void 0 ? void 0 : _b.name) === 'ObjectAccess' && tree.prevSibling) {
94
+ const node = tree.parent.firstChild;
95
+ const types = resolveTypes(view.state, node, config, true);
96
+ const name = view.state.sliceDoc(tree.from, tree.to);
97
+ info = [
98
+ ...Array.from(types).map(type => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.identifiers) === null || _c === void 0 ? void 0 : _c.find(x => x.name === name)) === null || _d === void 0 ? void 0 : _d.info; }).filter(skipEmpty),
99
+ ...Array.from(types).map(type => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.functions) === null || _c === void 0 ? void 0 : _c.find(x => x.name === name)) === null || _d === void 0 ? void 0 : _d.info; }).filter(skipEmpty),
100
+ ].join('\n');
101
+ }
102
+ else {
103
+ const name = view.state.sliceDoc(tree.from, tree.to);
104
+ info = [
105
+ ...[(_d = (_c = config.identifiers) === null || _c === void 0 ? void 0 : _c.find(x => x.name === name)) === null || _d === void 0 ? void 0 : _d.info].filter(skipEmpty),
106
+ ...[(_f = (_e = config.functions) === null || _e === void 0 ? void 0 : _e.find(x => x.name === name)) === null || _f === void 0 ? void 0 : _f.info].filter(skipEmpty),
107
+ ].join('\n');
108
+ }
64
109
  if (!info) {
65
110
  return null;
66
111
  }
67
112
  return {
68
- pos: start,
69
- end,
113
+ pos: tree.from,
114
+ end: tree.to,
70
115
  above: true,
71
116
  create(view) {
72
117
  let dom = document.createElement("div");
@@ -90,62 +135,124 @@ const ELLanguage = language.LRLanguage.define({
90
135
  Boolean: highlight.tags.bool,
91
136
  String: highlight.tags.string,
92
137
  Number: highlight.tags.number,
93
- OpeningBracket: highlight.tags.paren,
94
- ClosingBracket: highlight.tags.paren,
138
+ '(': highlight.tags.paren,
139
+ ')': highlight.tags.paren,
95
140
  '[': highlight.tags.squareBracket,
96
141
  ']': highlight.tags.squareBracket,
97
142
  OperatorKeyword: highlight.tags.operatorKeyword,
98
143
  Operator: highlight.tags.operator,
99
- NullSafe: highlight.tags.operator,
100
- NullCoalescing: highlight.tags.operator,
101
- Punctuation: highlight.tags.punctuation,
144
+ MemberOf: highlight.tags.operator,
145
+ NullSafeMemberOf: highlight.tags.operator,
102
146
  })
103
147
  ]
104
148
  }),
105
149
  languageData: {}
106
150
  });
107
- function completeOperatorKeyword(state, config, tree, from, to) {
151
+ function completeOperatorKeyword(state, config, tree, from, to, explicit) {
108
152
  var _a, _b;
109
153
  const text = state.sliceDoc(from, to);
110
154
  return {
111
155
  from,
112
156
  to,
113
- 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 : [],
114
- 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; },
157
+ 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 : [],
158
+ 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; },
115
159
  };
116
160
  }
117
- function completeIdentifier(state, config, tree, from, to) {
118
- var _a, _b, _c, _d, _e, _f;
119
- const text = state.sliceDoc(from, to);
120
- const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
121
- const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
161
+ function completeIdentifier(state, config, tree, from, to, explicit) {
162
+ var _a, _b, _c, _d;
163
+ const text = state.sliceDoc(from, to).toLowerCase();
164
+ const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => explicit || name.toLowerCase().startsWith(text))) !== null && _b !== void 0 ? _b : [];
165
+ const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => explicit || name.toLowerCase().startsWith(text))) !== null && _d !== void 0 ? _d : [];
166
+ return {
167
+ from,
168
+ to,
169
+ options: [...(identifiers.map(autocompleteIdentifier)), ...(functions.map(autocompleteFunction))],
170
+ // validFor: identifier,
171
+ filter: false,
172
+ };
173
+ }
174
+ function resolveTypes(state, node, config, matchExact) {
175
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
176
+ let types = new Set();
177
+ if (node.name === 'FunctionCall' && node.firstChild && node.lastChild) {
178
+ resolveTypes(state, node.firstChild, config, matchExact).forEach(x => types.add(x));
179
+ }
180
+ else if (node.name === 'Identifier') {
181
+ const varName = state.sliceDoc(node.from, node.to) || '';
182
+ (_c = (_b = (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(x => x.name == varName)) === null || _b === void 0 ? void 0 : _b.returnType) === null || _c === void 0 ? void 0 : _c.forEach(x => types.add(x));
183
+ (_f = (_e = (_d = config.identifiers) === null || _d === void 0 ? void 0 : _d.find(x => x.name == varName)) === null || _e === void 0 ? void 0 : _e.type) === null || _f === void 0 ? void 0 : _f.forEach(x => types.add(x));
184
+ }
185
+ else if (node.name === 'ObjectAccess' && node.firstChild && ((_g = node.lastChild) === null || _g === void 0 ? void 0 : _g.name) === 'Identifier') {
186
+ const baseTypes = resolveTypes(state, node.firstChild, config, matchExact);
187
+ const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
188
+ for (const baseType of baseTypes) {
189
+ const type = (_h = config.types) === null || _h === void 0 ? void 0 : _h[baseType];
190
+ (_k = (_j = type === null || type === void 0 ? void 0 : type.functions) === null || _j === void 0 ? void 0 : _j.filter(x => matchExact ? x.name === varName : x.name.startsWith(varName))) === null || _k === void 0 ? void 0 : _k.forEach(def => (def.returnType || ['any']).forEach(x => types.add(x)));
191
+ (_m = (_l = type === null || type === void 0 ? void 0 : type.identifiers) === null || _l === void 0 ? void 0 : _l.filter(x => matchExact ? x.name === varName : x.name.startsWith(varName))) === null || _m === void 0 ? void 0 : _m.forEach(def => (def.type || ['any']).forEach(x => types.add(x)));
192
+ }
193
+ }
194
+ if (types.size === 0) {
195
+ types.add('any');
196
+ }
197
+ return types;
198
+ }
199
+ function completeMember(state, config, tree, from, to, explicit) {
200
+ var _a, _b, _c, _d;
201
+ if (((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.name) != 'ObjectAccess' || !tree.parent.firstChild) {
202
+ return null;
203
+ }
204
+ const types = resolveTypes(state, tree.parent.firstChild.node, config, false);
205
+ if (!(types === null || types === void 0 ? void 0 : types.size)) {
206
+ return null;
207
+ }
208
+ const varName = state.sliceDoc(from, to);
209
+ let options = [];
210
+ for (const type of types) {
211
+ const typeDeclaration = (_b = config.types) === null || _b === void 0 ? void 0 : _b[type];
212
+ options.push(...(((_c = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.identifiers) === null || _c === void 0 ? void 0 : _c.filter(x => x.name.startsWith(varName)).map(autocompleteIdentifier)) || []), ...(((_d = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.functions) === null || _d === void 0 ? void 0 : _d.filter(x => x.name.startsWith(varName)).map(autocompleteFunction)) || []));
213
+ }
122
214
  return {
123
215
  from,
124
216
  to,
125
- options: [
126
- ...((_e = identifiers.map(({ name, info, detail }) => ({ label: name, info, detail, type: 'variable' }))) !== null && _e !== void 0 ? _e : []),
127
- ...((_f = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _f !== void 0 ? _f : []),
128
- ],
129
- validFor: identifier,
217
+ options,
218
+ filter: false,
130
219
  };
131
220
  }
132
221
  function expressionLanguageCompletionFor(config, context) {
133
- let { state, pos } = context;
134
- let tree = language.syntaxTree(state).resolveInner(pos, -1);
222
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
223
+ const { state, pos, explicit } = context;
224
+ const tree = language.syntaxTree(state).resolveInner(pos, -1);
225
+ const isOperator = (node) => node && ['Operator', 'OperatorKeyword'].includes(node.name);
226
+ const isIdentifier = (node) => (node === null || node === void 0 ? void 0 : node.name) === 'Identifier';
227
+ const prevNode = ((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.node.type.isError) ? tree.parent.prevSibling : tree.prevSibling;
135
228
  if (tree.name == 'String') {
136
229
  return null;
137
230
  }
138
- if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(tree.prevSibling.name)) {
139
- return completeOperatorKeyword(state, config, tree, tree.from, pos);
231
+ if (((_b = tree.parent) === null || _b === void 0 ? void 0 : _b.name) == 'ObjectAccess' && ['ObjectAccess', 'ArrayAccess', 'Identifier', 'FunctionCall'].includes(((_c = tree.parent.firstChild) === null || _c === void 0 ? void 0 : _c.name) || '')) {
232
+ return completeMember(state, config, tree, isIdentifier(tree.node) ? tree.from : pos, pos);
140
233
  }
141
- if (tree.name == "Identifier") {
142
- return completeIdentifier(state, config, tree, tree.from, pos);
234
+ // No idea what's going on here, just added conditions until all the tests passed :)
235
+ if (prevNode && !isOperator(prevNode.node) && (((_d = tree.parent) === null || _d === void 0 ? void 0 : _d.node.type.isError) || ((_e = tree.node.parent) === null || _e === void 0 ? void 0 : _e.name) === 'BinaryExpression') || (tree.name === 'Expression' && !((_g = (_f = tree.lastChild) === null || _f === void 0 ? void 0 : _f.type) === null || _g === void 0 ? void 0 : _g.isError) && !isOperator((_h = tree.lastChild) === null || _h === void 0 ? void 0 : _h.node))) {
236
+ return completeOperatorKeyword(state, config, tree, tree.name !== 'Expression' ? tree.from : pos, pos, explicit);
237
+ }
238
+ if (tree.name === 'Expression' || isIdentifier(tree.node) || (tree.name === 'BinaryExpression' && isOperator((_k = (_j = tree.lastChild) === null || _j === void 0 ? void 0 : _j.prevSibling) === null || _k === void 0 ? void 0 : _k.node) && explicit)) {
239
+ return completeIdentifier(state, config, tree, isIdentifier(tree.node) ? tree.from : pos, pos, explicit);
143
240
  }
144
241
  return null;
145
242
  }
146
243
  function expressionLanguageCompletionSourceWith(config) {
147
244
  var _a;
148
- (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = ['starts with', 'ends with', 'contains', 'matches', 'not in', 'in', 'not', 'or', 'and']);
245
+ (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = [
246
+ { name: 'starts with' },
247
+ { name: 'ends with' },
248
+ { name: 'contains' },
249
+ { name: 'matches' },
250
+ { name: 'not in' },
251
+ { name: 'in' },
252
+ { name: 'not' },
253
+ { name: 'or' },
254
+ { name: 'and' },
255
+ ]);
149
256
  return (context) => expressionLanguageCompletionFor(config, context);
150
257
  }
151
258
  function expressionlanguage(config = {}, extensions = []) {
package/dist/index.d.cts CHANGED
@@ -1,18 +1,34 @@
1
1
  import { LRLanguage, LanguageSupport } from "@codemirror/language";
2
+ interface ELIdentifier {
3
+ name: string;
4
+ detail?: string;
5
+ info?: string;
6
+ type?: string[];
7
+ }
8
+ interface ELFunction {
9
+ name: string;
10
+ args: string[];
11
+ info?: string;
12
+ returnType?: string[];
13
+ }
2
14
  interface ExpressionLanguageConfig {
3
- identifiers?: readonly {
15
+ types?: {
16
+ [key: string]: {
17
+ identifiers?: ELIdentifier[];
18
+ functions?: ELFunction[];
19
+ type?: string;
20
+ info?: string;
21
+ };
22
+ };
23
+ identifiers?: ELIdentifier[];
24
+ functions?: ELFunction[];
25
+ operatorKeywords?: readonly {
4
26
  name: string;
5
27
  detail?: string;
6
28
  info?: string;
7
29
  }[];
8
- functions?: readonly {
9
- name: string;
10
- args: string[];
11
- info?: string;
12
- }[];
13
- operatorKeywords?: readonly string[];
14
30
  }
15
31
  declare const keywordTooltip: (config: ExpressionLanguageConfig) => import("@codemirror/state").Extension;
16
32
  declare const ELLanguage: LRLanguage;
17
33
  declare function expressionlanguage(config?: ExpressionLanguageConfig, extensions?: Array<any>): LanguageSupport;
18
- export { ExpressionLanguageConfig, keywordTooltip, ELLanguage, expressionlanguage };
34
+ export { ELIdentifier, ELFunction, ExpressionLanguageConfig, keywordTooltip, ELLanguage, expressionlanguage };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,34 @@
1
1
  import { LRLanguage, LanguageSupport } from "@codemirror/language";
2
+ interface ELIdentifier {
3
+ name: string;
4
+ detail?: string;
5
+ info?: string;
6
+ type?: string[];
7
+ }
8
+ interface ELFunction {
9
+ name: string;
10
+ args: string[];
11
+ info?: string;
12
+ returnType?: string[];
13
+ }
2
14
  interface ExpressionLanguageConfig {
3
- identifiers?: readonly {
15
+ types?: {
16
+ [key: string]: {
17
+ identifiers?: ELIdentifier[];
18
+ functions?: ELFunction[];
19
+ type?: string;
20
+ info?: string;
21
+ };
22
+ };
23
+ identifiers?: ELIdentifier[];
24
+ functions?: ELFunction[];
25
+ operatorKeywords?: readonly {
4
26
  name: string;
5
27
  detail?: string;
6
28
  info?: string;
7
29
  }[];
8
- functions?: readonly {
9
- name: string;
10
- args: string[];
11
- info?: string;
12
- }[];
13
- operatorKeywords?: readonly string[];
14
30
  }
15
31
  declare const keywordTooltip: (config: ExpressionLanguageConfig) => import("@codemirror/state").Extension;
16
32
  declare const ELLanguage: LRLanguage;
17
33
  declare function expressionlanguage(config?: ExpressionLanguageConfig, extensions?: Array<any>): LanguageSupport;
18
- export { ExpressionLanguageConfig, keywordTooltip, ELLanguage, expressionlanguage };
34
+ export { ELIdentifier, ELFunction, ExpressionLanguageConfig, keywordTooltip, ELLanguage, expressionlanguage };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { LRParser } from '@lezer/lr';
2
- import { LRLanguage, indentNodeProp, delimitedIndent, foldNodeProp, foldInside, LanguageSupport, syntaxTree } from '@codemirror/language';
2
+ import { LRLanguage, indentNodeProp, delimitedIndent, foldNodeProp, foldInside, syntaxTree, LanguageSupport } from '@codemirror/language';
3
3
  import { styleTags, tags } from '@lezer/highlight';
4
4
  import { linter } from '@codemirror/lint';
5
5
  import { hoverTooltip } from '@codemirror/view';
@@ -7,30 +7,63 @@ import { hoverTooltip } from '@codemirror/view';
7
7
  // This file was generated by lezer-generator. You probably shouldn't edit it.
8
8
  const parser = LRParser.deserialize({
9
9
  version: 14,
10
- 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",
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~",
12
- goto: "!ghPPPPPPPPPPPPiPiPPis}PPP!TaSOQRUZ]^iQUOQZRT[UZQ_XRd_WTORUZQXQQb]Qc^Rji",
13
- nodeNames: "⚠ Program Number Identifier String Boolean Null Operator OperatorKeyword Punctuation NullSafe NullCoalescing Object ObjectKey Array ClosingBracket OpeningBracket Application",
14
- maxTerm: 29,
15
- nodeProps: [
16
- ["openedBy", 15,"OpeningBracket"],
17
- ["closedBy", 16,"ClosingBracket"]
18
- ],
10
+ states: "'[OVQPOOQtQQO'#C^O!YQQOOO#TQPO'#ChO#[QPO'#CiOVQPO'#CnOOQO'#Cu'#CuOVQPO'#CuO#cQPO,58xOVQPO,58|OVQPO,59UOVQPO,59VO#hQPO,58}O#oQQO,59SOOQO,59S,59SO#vQQO,59TOOQO,59T,59TO$QQQO,59YO$hQQO,59aOOQO1G.d1G.dO$oQQO1G.hO$vQQO1G.pO$}QQO1G.qO%eQQO1G.iOOQO1G.i1G.iOVQPO1G.nOVQPO'#CpO%oQPO1G.oOOQO1G.o1G.oOOQO1G.{1G.{OOQO7+$S7+$SOVQPO7+$[O%wQPO7+$TOOQO7+$T7+$TO&PQQO7+$YO&ZQQO,59[OOQO-E6n-E6nOOQO7+$Z7+$ZO&hQQO<<GvOOQO<<Go<<GoOVQPO'#CqO'OQPO<<GtOOQO<<Gt<<GtO'WQQO,59]OOQO-E6o-E6oOOQOAN=`AN=`OVQPO1G.wO'_QQO7+$c",
11
+ stateData: "'s~OhOS~OTQOWUOXUOYUOZUOcTOkSOmVOpRO~ORWOSWO`ZOaZOkXOrYO~Om[ORiXSiX`iXaiXfiXkiXriXqiXjiXniXliXoiX~Oo^O~PVOj`O~PVOTcO~OlhO~PVOqiO~PtOjlOnjO~PtOfbaqbajbanbalbaoba~PtOlmO~PtOjnO~PtOqoO~PtOf_iq_ij_in_il_io_i~PtOlqOnjO~PtOjuOnjO~OlwOnjO~OnxOozO~PtOjdandalda~PtOf^yq^yj^yn^yl^yo^y~PtOnxOo}O~Oq!OO~PtOneqoeq~PtORSc`YZWTY~",
12
+ goto: "#ijPPkPPP{{PPPP{{{{PP{P!]!gPPP!mmQORSTVXYZ[ijox!OmUORSTVXYZ[ijox!OQk_QpgTtkpQyrR|yQPOQ]RQ_SQaTQbVQdXQeYQfZQg[QriQsjQvoQ{xR!P!O",
13
+ nodeNames: "⚠ Expression ObjectAccess NullSafeMemberOf MemberOf Identifier ArrayAccess FunctionCall Number String Boolean Null Object Array TernaryExpression BinaryExpression OperatorKeyword Operator UnaryExpression UnaryOperator",
14
+ maxTerm: 34,
19
15
  skippedNodes: [0],
20
16
  repeatNodeCount: 2,
21
- 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~",
17
+ tokenData: "AZ~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Ruv$|vw&{wx'Txy(xyz(}z{)S{|)[|})c}!O)[!O!P)h!P!Q$|!Q![)p![!],V!^!_$t!_!`,[!`!a$t!a!b,b!c!},u!}#O-W#P#Q-]#Q#R$|#R#S,u#T#U-b#U#V,u#V#W.u#W#X,u#X#Y2Z#Y#Z4u#Z#],u#]#^7V#^#a,u#a#b7n#b#c:S#c#d=c#d#g,u#g#h=z#h#i?w#i#o,u#o#p@w#p#q@|#q#rAU#r#s$|~$^Sh~XY$XYZ$X]^$Xpq$XR$qPaQcP!_!`$tQ$yPaQ!_!`$|Q%ROaQ~%UXOr%Rrs%qs#O%R#O#P%v#P;'S%R;'S;=`&u<%l~%R~O%R~~%q~%vOX~~%yRO;'S%R;'S;=`&S;=`O%R~&VYOr%Rrs%qs#O%R#O#P%v#P;'S%R;'S;=`&u;=`<%l%R<%l~%R~O%R~~%q~&xP;=`<%l%RQ'QPaQvw$|~'WXOw'Twx%qx#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%q~'vRO;'S'T;'S;=`(P;=`O'T~(SYOw'Twx%qx#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%q~(uP;=`<%l'T~(}Om~~)SOl~Q)XPaQz{$|R)cOaQcP~)hOn~~)mPS~!O!P$|~)uTW~!O!P*U!Q![)p!g!h+U#R#S,P#X#Y+U~*XSO!O*e!P;'S*e;'S;=`+y<%lO*e~*jRW~!Q![*s!g!h+U#X#Y+U~*xSW~!Q![*s!g!h+U#R#S+s#X#Y+U~+XR{|+b}!O+b!Q![+h~+eP!Q![+h~+mQW~!Q![+h#R#S+b~+vP!Q![*s~+|P;=`<%l*e~,SP!Q![)p~,[Oq~Q,_P!_!`$t~,gRr~!O!P,p![!]$|!a!b$|~,uOR~~,zST~!Q![,u!c!},u#R#S,u#T#o,u~-]Ok~~-bOj~~-gUT~!Q![,u!c!},u#R#S,u#T#b,u#b#c-y#c#o,u~.OUT~!Q![,u!c!},u#R#S,u#T#W,u#W#X.b#X#o,u~.iS`~T~!Q![,u!c!},u#R#S,u#T#o,u~.zUT~!Q![,u!c!},u#R#S,u#T#c,u#c#d/^#d#o,u~/cUT~!Q![,u!c!},u#R#S,u#T#b,u#b#c/u#c#o,u~/zUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i0^#i#o,u~0cTT~!Q![,u!c!},u#R#S,u#T#U0r#U#o,u~0wUT~!Q![,u!c!},u#R#S,u#T#],u#]#^1Z#^#o,u~1`UT~!Q![,u!c!},u#R#S,u#T#b,u#b#c1r#c#o,u~1wUT~!Q![,u!c!},u#R#S,u#T#g,u#g#h.b#h#o,u~2`UT~!Q![,u!c!},u#R#S,u#T#b,u#b#c2r#c#o,u~2wUT~!Q![,u!c!},u#R#S,u#T#W,u#W#X3Z#X#o,u~3`UT~!Q![,u!c!},u#R#S,u#T#g,u#g#h3r#h#o,u~3wTT~pq4W!Q![,u!c!},u#R#S,u#T#o,u~4ZP#k#l4^~4aP#]#^4d~4gP#h#i4j~4mP#[#]4p~4uO`~~4zTT~!Q![,u!c!},u#R#S,u#T#U5Z#U#o,u~5`UT~!Q![,u!c!},u#R#S,u#T#`,u#`#a5r#a#o,u~5wUT~!Q![,u!c!},u#R#S,u#T#g,u#g#h6Z#h#o,u~6`UT~!Q![,u!c!},u#R#S,u#T#X,u#X#Y6r#Y#o,u~6ySY~T~!Q![,u!c!},u#R#S,u#T#o,u~7[UT~!Q![,u!c!},u#R#S,u#T#b,u#b#c.b#c#o,u~7sTT~!Q![,u!c!},u#R#S,u#T#U8S#U#o,u~8XUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i8k#i#o,u~8pUT~!Q![,u!c!},u#R#S,u#T#V,u#V#W9S#W#o,u~9XUT~!Q![,u!c!},u#R#S,u#T#[,u#[#]9k#]#o,u~9pUT~!Q![,u!c!},u#R#S,u#T#X,u#X#Y1r#Y#o,u~:XWT~!Q![,u!c!},u#R#S,u#T#c,u#c#d:q#d#i,u#i#j<O#j#o,u~:vUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i;Y#i#o,u~;cTcP`~T~pq;r!Q![,u!c!},u#R#S,u#T#o,u~;uP#]#^;x~;{P#b#c4p~<TUT~!Q![,u!c!},u#R#S,u#T#`,u#`#a<g#a#o,u~<lUT~!Q![,u!c!},u#R#S,u#T#`,u#`#a=O#a#o,u~=VSZ~T~!Q![,u!c!},u#R#S,u#T#o,u~=hUT~!Q![,u!c!},u#R#S,u#T#f,u#f#g.b#g#o,u~>PUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i>c#i#o,u~>hTT~!Q![,u!c!},u#R#S,u#T#U>w#U#o,u~>|UT~!Q![,u!c!},u#R#S,u#T#f,u#f#g?`#g#o,u~?eUT~!Q![,u!c!},u#R#S,u#T#h,u#h#i3Z#i#o,u~?|UT~!Q![,u!c!},u#R#S,u#T#f,u#f#g@`#g#o,u~@eUT~!Q![,u!c!},u#R#S,u#T#i,u#i#j6Z#j#o,u~@|Op~QARPaQ#p#q$|~AZOo~",
22
18
  tokenizers: [0, 1],
23
- topRules: {"Program":[0,1]},
24
- tokenPrec: 118
19
+ topRules: {"Expression":[0,1]},
20
+ tokenPrec: 301
25
21
  });
26
22
 
27
- const identifier = /^[a-zA-Z_]+[a-zA-Z_0-9]*$/;
28
23
  const isFunction = (identifier, config) => { var _a; return (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier); };
29
24
  const isVariable = (identifier, config) => { var _a; return (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.find(variable => variable.name === identifier); };
25
+ const autocompleteFunction = (x) => { var _a, _b, _c; return ({ label: `${x.name}(${((_a = x.args) === null || _a === void 0 ? void 0 : _a.join(',')) || ''})`, apply: `${x.name}(${!((_b = x.args) === null || _b === void 0 ? void 0 : _b.length) ? ')' : ''}`, detail: (_c = x.returnType) === null || _c === void 0 ? void 0 : _c.join('|'), info: x.info, type: "function" }); };
26
+ const autocompleteIdentifier = (x) => { var _a; return ({ label: x.name, apply: x.name, info: x.info, detail: x.detail || ((_a = x.type) === null || _a === void 0 ? void 0 : _a.join('|')), type: 'variable' }); };
30
27
  const expressionLanguageLinter = (config) => linter(view => {
31
28
  let diagnostics = [];
29
+ let previousNode = null;
32
30
  syntaxTree(view.state).cursor().iterate(node => {
31
+ var _a, _b, _c, _d, _e;
32
+ if (((_a = node.node.parent) === null || _a === void 0 ? void 0 : _a.name) == 'ObjectAccess' && ((_b = node.node.parent) === null || _b === void 0 ? void 0 : _b.firstChild) && node.name == "Identifier" && node.node.prevSibling) {
33
+ const leftArgument = node.node.parent.firstChild.node;
34
+ const types = resolveTypes(view.state, leftArgument, config, true);
35
+ const identifier = view.state.sliceDoc(node.from, node.to);
36
+ if (((_d = (_c = node.node.parent) === null || _c === void 0 ? void 0 : _c.parent) === null || _d === void 0 ? void 0 : _d.name) === 'FunctionCall') {
37
+ if (!Array.from(types).find(type => { var _a, _b, _c; return (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.functions) === null || _c === void 0 ? void 0 : _c.find(x => x.name === identifier); })) {
38
+ diagnostics.push({
39
+ from: node.from,
40
+ to: node.to,
41
+ severity: 'error',
42
+ message: `Method '${identifier}' not found in ${Array.from(types).join('|')}`,
43
+ });
44
+ }
45
+ }
46
+ else if (types.size > 0) {
47
+ if (!Array.from(types).find(type => { var _a, _b, _c; return (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.identifiers) === null || _c === void 0 ? void 0 : _c.find(x => x.name === identifier); })) {
48
+ diagnostics.push({
49
+ from: node.from,
50
+ to: node.to,
51
+ severity: 'warning',
52
+ message: `Property '${identifier}' not found in ${Array.from(types).join('|')}`,
53
+ });
54
+ }
55
+ }
56
+ return;
57
+ }
33
58
  if (node.name == "Identifier") {
59
+ if ((previousNode === null || previousNode === void 0 ? void 0 : previousNode.name) == "Identifier" && !['Array', 'FunctionCall'].includes(((_e = node.node.parent) === null || _e === void 0 ? void 0 : _e.name) || '')) {
60
+ diagnostics.push({
61
+ from: node.from,
62
+ to: node.to,
63
+ severity: 'error',
64
+ message: `Unexpected identifier after another identifier`,
65
+ });
66
+ }
34
67
  const identifier = view.state.sliceDoc(node.from, node.to);
35
68
  if (!isFunction(identifier, config) && !isVariable(identifier, config)) {
36
69
  diagnostics.push({
@@ -41,28 +74,40 @@ const expressionLanguageLinter = (config) => linter(view => {
41
74
  });
42
75
  }
43
76
  }
77
+ previousNode = node.node;
44
78
  });
45
79
  return diagnostics;
46
80
  });
47
81
  const keywordTooltip = (config) => hoverTooltip((view, pos, side) => {
48
- var _a, _b;
49
- let { from, to, text } = view.state.doc.lineAt(pos);
50
- let start = pos, end = pos;
51
- while (start > from && /\w/.test(text[start - from - 1]))
52
- start--;
53
- while (end < to && /\w/.test(text[end - from]))
54
- end++;
55
- if (start == pos && side < 0 || end == pos && side > 0) {
82
+ var _a, _b, _c, _d, _e, _f;
83
+ const tree = syntaxTree(view.state).resolveInner(pos, side);
84
+ if (tree.name !== 'Identifier') {
56
85
  return null;
57
86
  }
58
- const keyword = text.slice(start - from, end - from);
59
- const info = (_b = ((_a = isFunction(keyword, config)) !== null && _a !== void 0 ? _a : isVariable(keyword, config))) === null || _b === void 0 ? void 0 : _b.info;
87
+ const skipEmpty = (x) => x;
88
+ let info;
89
+ if (((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.firstChild) && ((_b = tree.parent) === null || _b === void 0 ? void 0 : _b.name) === 'ObjectAccess' && tree.prevSibling) {
90
+ const node = tree.parent.firstChild;
91
+ const types = resolveTypes(view.state, node, config, true);
92
+ const name = view.state.sliceDoc(tree.from, tree.to);
93
+ info = [
94
+ ...Array.from(types).map(type => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.identifiers) === null || _c === void 0 ? void 0 : _c.find(x => x.name === name)) === null || _d === void 0 ? void 0 : _d.info; }).filter(skipEmpty),
95
+ ...Array.from(types).map(type => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b.functions) === null || _c === void 0 ? void 0 : _c.find(x => x.name === name)) === null || _d === void 0 ? void 0 : _d.info; }).filter(skipEmpty),
96
+ ].join('\n');
97
+ }
98
+ else {
99
+ const name = view.state.sliceDoc(tree.from, tree.to);
100
+ info = [
101
+ ...[(_d = (_c = config.identifiers) === null || _c === void 0 ? void 0 : _c.find(x => x.name === name)) === null || _d === void 0 ? void 0 : _d.info].filter(skipEmpty),
102
+ ...[(_f = (_e = config.functions) === null || _e === void 0 ? void 0 : _e.find(x => x.name === name)) === null || _f === void 0 ? void 0 : _f.info].filter(skipEmpty),
103
+ ].join('\n');
104
+ }
60
105
  if (!info) {
61
106
  return null;
62
107
  }
63
108
  return {
64
- pos: start,
65
- end,
109
+ pos: tree.from,
110
+ end: tree.to,
66
111
  above: true,
67
112
  create(view) {
68
113
  let dom = document.createElement("div");
@@ -86,62 +131,124 @@ const ELLanguage = LRLanguage.define({
86
131
  Boolean: tags.bool,
87
132
  String: tags.string,
88
133
  Number: tags.number,
89
- OpeningBracket: tags.paren,
90
- ClosingBracket: tags.paren,
134
+ '(': tags.paren,
135
+ ')': tags.paren,
91
136
  '[': tags.squareBracket,
92
137
  ']': tags.squareBracket,
93
138
  OperatorKeyword: tags.operatorKeyword,
94
139
  Operator: tags.operator,
95
- NullSafe: tags.operator,
96
- NullCoalescing: tags.operator,
97
- Punctuation: tags.punctuation,
140
+ MemberOf: tags.operator,
141
+ NullSafeMemberOf: tags.operator,
98
142
  })
99
143
  ]
100
144
  }),
101
145
  languageData: {}
102
146
  });
103
- function completeOperatorKeyword(state, config, tree, from, to) {
147
+ function completeOperatorKeyword(state, config, tree, from, to, explicit) {
104
148
  var _a, _b;
105
149
  const text = state.sliceDoc(from, to);
106
150
  return {
107
151
  from,
108
152
  to,
109
- 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 : [],
110
- 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; },
153
+ 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 : [],
154
+ 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; },
111
155
  };
112
156
  }
113
- function completeIdentifier(state, config, tree, from, to) {
114
- var _a, _b, _c, _d, _e, _f;
115
- const text = state.sliceDoc(from, to);
116
- const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
117
- const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
157
+ function completeIdentifier(state, config, tree, from, to, explicit) {
158
+ var _a, _b, _c, _d;
159
+ const text = state.sliceDoc(from, to).toLowerCase();
160
+ const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => explicit || name.toLowerCase().startsWith(text))) !== null && _b !== void 0 ? _b : [];
161
+ const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => explicit || name.toLowerCase().startsWith(text))) !== null && _d !== void 0 ? _d : [];
162
+ return {
163
+ from,
164
+ to,
165
+ options: [...(identifiers.map(autocompleteIdentifier)), ...(functions.map(autocompleteFunction))],
166
+ // validFor: identifier,
167
+ filter: false,
168
+ };
169
+ }
170
+ function resolveTypes(state, node, config, matchExact) {
171
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
172
+ let types = new Set();
173
+ if (node.name === 'FunctionCall' && node.firstChild && node.lastChild) {
174
+ resolveTypes(state, node.firstChild, config, matchExact).forEach(x => types.add(x));
175
+ }
176
+ else if (node.name === 'Identifier') {
177
+ const varName = state.sliceDoc(node.from, node.to) || '';
178
+ (_c = (_b = (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(x => x.name == varName)) === null || _b === void 0 ? void 0 : _b.returnType) === null || _c === void 0 ? void 0 : _c.forEach(x => types.add(x));
179
+ (_f = (_e = (_d = config.identifiers) === null || _d === void 0 ? void 0 : _d.find(x => x.name == varName)) === null || _e === void 0 ? void 0 : _e.type) === null || _f === void 0 ? void 0 : _f.forEach(x => types.add(x));
180
+ }
181
+ else if (node.name === 'ObjectAccess' && node.firstChild && ((_g = node.lastChild) === null || _g === void 0 ? void 0 : _g.name) === 'Identifier') {
182
+ const baseTypes = resolveTypes(state, node.firstChild, config, matchExact);
183
+ const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
184
+ for (const baseType of baseTypes) {
185
+ const type = (_h = config.types) === null || _h === void 0 ? void 0 : _h[baseType];
186
+ (_k = (_j = type === null || type === void 0 ? void 0 : type.functions) === null || _j === void 0 ? void 0 : _j.filter(x => matchExact ? x.name === varName : x.name.startsWith(varName))) === null || _k === void 0 ? void 0 : _k.forEach(def => (def.returnType || ['any']).forEach(x => types.add(x)));
187
+ (_m = (_l = type === null || type === void 0 ? void 0 : type.identifiers) === null || _l === void 0 ? void 0 : _l.filter(x => matchExact ? x.name === varName : x.name.startsWith(varName))) === null || _m === void 0 ? void 0 : _m.forEach(def => (def.type || ['any']).forEach(x => types.add(x)));
188
+ }
189
+ }
190
+ if (types.size === 0) {
191
+ types.add('any');
192
+ }
193
+ return types;
194
+ }
195
+ function completeMember(state, config, tree, from, to, explicit) {
196
+ var _a, _b, _c, _d;
197
+ if (((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.name) != 'ObjectAccess' || !tree.parent.firstChild) {
198
+ return null;
199
+ }
200
+ const types = resolveTypes(state, tree.parent.firstChild.node, config, false);
201
+ if (!(types === null || types === void 0 ? void 0 : types.size)) {
202
+ return null;
203
+ }
204
+ const varName = state.sliceDoc(from, to);
205
+ let options = [];
206
+ for (const type of types) {
207
+ const typeDeclaration = (_b = config.types) === null || _b === void 0 ? void 0 : _b[type];
208
+ options.push(...(((_c = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.identifiers) === null || _c === void 0 ? void 0 : _c.filter(x => x.name.startsWith(varName)).map(autocompleteIdentifier)) || []), ...(((_d = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.functions) === null || _d === void 0 ? void 0 : _d.filter(x => x.name.startsWith(varName)).map(autocompleteFunction)) || []));
209
+ }
118
210
  return {
119
211
  from,
120
212
  to,
121
- options: [
122
- ...((_e = identifiers.map(({ name, info, detail }) => ({ label: name, info, detail, type: 'variable' }))) !== null && _e !== void 0 ? _e : []),
123
- ...((_f = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _f !== void 0 ? _f : []),
124
- ],
125
- validFor: identifier,
213
+ options,
214
+ filter: false,
126
215
  };
127
216
  }
128
217
  function expressionLanguageCompletionFor(config, context) {
129
- let { state, pos } = context;
130
- let tree = syntaxTree(state).resolveInner(pos, -1);
218
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
219
+ const { state, pos, explicit } = context;
220
+ const tree = syntaxTree(state).resolveInner(pos, -1);
221
+ const isOperator = (node) => node && ['Operator', 'OperatorKeyword'].includes(node.name);
222
+ const isIdentifier = (node) => (node === null || node === void 0 ? void 0 : node.name) === 'Identifier';
223
+ const prevNode = ((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.node.type.isError) ? tree.parent.prevSibling : tree.prevSibling;
131
224
  if (tree.name == 'String') {
132
225
  return null;
133
226
  }
134
- if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(tree.prevSibling.name)) {
135
- return completeOperatorKeyword(state, config, tree, tree.from, pos);
227
+ if (((_b = tree.parent) === null || _b === void 0 ? void 0 : _b.name) == 'ObjectAccess' && ['ObjectAccess', 'ArrayAccess', 'Identifier', 'FunctionCall'].includes(((_c = tree.parent.firstChild) === null || _c === void 0 ? void 0 : _c.name) || '')) {
228
+ return completeMember(state, config, tree, isIdentifier(tree.node) ? tree.from : pos, pos);
136
229
  }
137
- if (tree.name == "Identifier") {
138
- return completeIdentifier(state, config, tree, tree.from, pos);
230
+ // No idea what's going on here, just added conditions until all the tests passed :)
231
+ if (prevNode && !isOperator(prevNode.node) && (((_d = tree.parent) === null || _d === void 0 ? void 0 : _d.node.type.isError) || ((_e = tree.node.parent) === null || _e === void 0 ? void 0 : _e.name) === 'BinaryExpression') || (tree.name === 'Expression' && !((_g = (_f = tree.lastChild) === null || _f === void 0 ? void 0 : _f.type) === null || _g === void 0 ? void 0 : _g.isError) && !isOperator((_h = tree.lastChild) === null || _h === void 0 ? void 0 : _h.node))) {
232
+ return completeOperatorKeyword(state, config, tree, tree.name !== 'Expression' ? tree.from : pos, pos, explicit);
233
+ }
234
+ if (tree.name === 'Expression' || isIdentifier(tree.node) || (tree.name === 'BinaryExpression' && isOperator((_k = (_j = tree.lastChild) === null || _j === void 0 ? void 0 : _j.prevSibling) === null || _k === void 0 ? void 0 : _k.node) && explicit)) {
235
+ return completeIdentifier(state, config, tree, isIdentifier(tree.node) ? tree.from : pos, pos, explicit);
139
236
  }
140
237
  return null;
141
238
  }
142
239
  function expressionLanguageCompletionSourceWith(config) {
143
240
  var _a;
144
- (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = ['starts with', 'ends with', 'contains', 'matches', 'not in', 'in', 'not', 'or', 'and']);
241
+ (_a = config.operatorKeywords) !== null && _a !== void 0 ? _a : (config.operatorKeywords = [
242
+ { name: 'starts with' },
243
+ { name: 'ends with' },
244
+ { name: 'contains' },
245
+ { name: 'matches' },
246
+ { name: 'not in' },
247
+ { name: 'in' },
248
+ { name: 'not' },
249
+ { name: 'or' },
250
+ { name: 'and' },
251
+ ]);
145
252
  return (context) => expressionLanguageCompletionFor(config, context);
146
253
  }
147
254
  function expressionlanguage(config = {}, extensions = []) {
package/package.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "description": "Symfony Expression Language language support for CodeMirror",
4
4
  "scripts": {
5
5
  "test": "mocha test/test.js test/test-complete.js",
6
+ "dev": "rollup -c -w",
6
7
  "prepare": "rollup -c"
7
8
  },
8
9
  "type": "module",
@@ -41,5 +42,5 @@
41
42
  "access": "public",
42
43
  "registry": "https://registry.npmjs.org/"
43
44
  },
44
- "version": "0.2.3"
45
+ "version": "0.4.0"
45
46
  }