@valtzu/codemirror-lang-el 1.2.0 → 1.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/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  CHANGELOG
2
2
  =========
3
3
 
4
+ 1.4
5
+ ---
6
+
7
+ * Improve support for typed arrays
8
+ * Add support for null-safe array access
9
+ * Minor bugfixes & adjustments to autocomplete & linting
10
+
11
+ 1.3
12
+ ---
13
+
14
+ * Lint left- and right-side arguments of `contains`, `starts with`, `ends with` and `matches` operators
15
+ * Add eslint to make contributing/review easier
16
+ * Add type checking for left-side argument in `in` expression
17
+
4
18
  1.2
5
19
  ---
6
20
 
package/dist/index.cjs CHANGED
@@ -8,31 +8,31 @@ var autocomplete = require('@codemirror/autocomplete');
8
8
  var state = require('@codemirror/state');
9
9
  var view = require('@codemirror/view');
10
10
 
11
- // @ts-ignore
11
+ // @ts-expect-error TS2739
12
12
  const t = {
13
13
  deserialize: (str) => str,
14
14
  };
15
15
 
16
16
  // This file was generated by lezer-generator. You probably shouldn't edit it.
17
- const spec_word = {__proto__:null,true:90, TRUE:92, false:94, FALSE:96, null:98, NULL:100, starts:110, with:112, ends:114, contains:116, matches:118, not:120, in:122, and:124, or:126, xor:128};
18
- const spec_Operator = {__proto__:null,"?":109, "!":131, "~":133, "+":135, "-":137};
17
+ const spec_word = {__proto__:null,true:92, TRUE:94, false:96, FALSE:98, null:100, NULL:102, starts:114, with:116, ends:118, contains:120, matches:122, not:124, in:126, and:128, or:130, xor:132};
18
+ const spec_Operator = {__proto__:null,"?":113, "!":135, "~":137, "+":139, "-":141};
19
19
  const parser = lr.LRParser.deserialize({
20
20
  version: 14,
21
- states: ")YOYQPOOP!aOPOOQ!fQPOOO#dQPO'#CeO$wQPO'#CmO%OQPO'#CnOYQPO'#CtOYQPO'#CvOOQO'#DR'#DRO%VQPO'#CdOOQO'#Ck'#CkOOQO'#Cl'#ClOOQO'#Cu'#CuP%[OQO'#C]POOO)C>q)C>qO%gQPO,58yOYQPO,58}OYQPO,59^OYQPO,59[O%lQPO'#CsOOQO'#Cs'#CsO%qQPO'#CsO&{QPO,59XOOQO,59X,59XO'SQPO'#DXO'aQPO,59YO'fQPO,59`O'|QPO,59bO(TQPO'#CfOOQO,59O,59OPOOO'#Cw'#CwP([OQO,58wPOOO,58w,58wO(gQPO'#CbOOQO1G.e1G.eOOQO1G.m1G.mO)zQPO1G.iO*RQPO1G.xO*iQPO1G.vOOQO,59_,59_OYQPO1G.sOYQPO'#CxO*pQPO,59sOOQO1G.t1G.tOOQO1G.|1G.|O*{QPO,59QPOOO-E6u-E6uPOOO1G.c1G.cOOQO7+$T7+$TOYQPO7+$bO+QQPO7+$_O+[QPO,59dOOQO-E6v-E6vOOQO1G.l1G.lO+iQPO<<G|OYQPO'#CyO,PQPO<<GyOOQO<<Gy<<GyO,XQPO,59eOOQO-E6w-E6wOOQOAN=eAN=eOYQPO1G/PO,`QPO7+$k",
22
- stateData: ",r~OpOSqPQ~O]WO^WOvROxTOzVO}YO!OYO!PYO!QYO!RZO!SZO!USO!^[O!c[O!d[O!e[O!f[O~Oq]O~OS_OT_OeaOx`O!WbO!XcO!ZcO![dO!]dO!^eO!_dO!`dO!adO!bdO~OScXTcXecXncXxcXzXX!WcX!XcX!ZcX![cX!]cX!^cX!_cX!`cX!acX!bcX!VcXwcX|cXycX!TcX~O!TgO~PYOw{P~PYOzlO~OrnOsnOtpO~OvqO~O!YwO~O!_wO]gX^gXvgXxgXzgX}gX!OgX!PgX!QgX!RgX!SgX!UgX!^gX!cgX!dgX!egX!fgX~O!VxO~P!fO|yOw{Xy{X~P!fOw{O~Onha!Vhawha|hayha!Tha~P!fOy|O~P!fOy{P~PYOrnOsnOt!PO~OSUXTUXeUXnUXxUXz[X!WUX!XUX!ZUX![UX!]UX!^UX!_UX!`UX!aUX!bUX!VUXwUX|UXyUX!TUX~Ow!QO~P!fOnfi!Vfiwfi|fiyfi!Tfi~P!fO!V!RO~P!fO|yOw{ay{a~Oy!VO~O|!XO!T!ZO~P!fOwla|layla~P!fOndy!Vdywdy|dyydy!Tdy~P!fO|!XO!T!^O~O!V!_O~P!fO|mq!Tmq~P!fO^TeS]vS~",
23
- goto: "%O|P}P!QPP!b!Q!Q!e!u!e!xPP!Q!Q!Q!Q!Q!QP!Q!{!Q#[!Q#l#r#xPPPPPPP$OPPPPP$xR^PmWOSTUV`ablxy!R!X!_Rr_mXOSTUV`ablxy!R!X!_RmXRs_kaQfhjktuv!S!T!W![!`mUOSTUV`ablxy!R!X!_Qo]R!OoQzhR!UzQ!Y!SR!]!YQQOQfSShTlQjUQkVQt`QuaQvbQ!SxQ!TyQ!W!RQ![!XR!`!_QiTR}l",
24
- nodeNames: "⚠ BlockComment Expression PropertyAccess MemberOf NullSafeMemberOf Property ArrayAccess Call Function Arguments MethodAccess Method Number String Boolean Null Object Array Variable TernaryExpression Operator BinaryExpression OperatorKeyword UnaryExpression UnaryOperator Application",
25
- maxTerm: 68,
21
+ states: ")YOYQPOOP!aOPOOQ!fQQOOO#gQQO'#CgO$}QPO'#CoO%UQPO'#CpOYQPO'#CvOYQPO'#CxOOQO'#DT'#DTO%]QPO'#CfOOQO'#Cm'#CmOOQO'#Cn'#CnOOQO'#Cw'#CwP%bOSO'#C]POOO)C>s)C>sO%mQPO,58yOYQPO,58}OYQPO,59`OYQPO,59^O%rQPO'#CuOOQO'#Cu'#CuO%wQPO'#CuO'RQQO,59ZOOQO,59Z,59ZO'YQQO'#DYO'gQPO,59[O'lQQO,59bO(SQQO,59dO(ZQPO'#ChOOQO,59Q,59QPOOO'#Cy'#CyP(bOSO,58wPOOO,58w,58wO(mQQO'#CbOOQO1G.e1G.eOOQO1G.o1G.oO*TQQO1G.iO*[QQO1G.zO*rQQO1G.xOOQO,59a,59aOYQPO1G.uOYQPO'#CzO*yQPO,59tOOQO1G.v1G.vOOQO1G/O1G/OO+UQPO,59SPOOO-E6w-E6wPOOO1G.c1G.cOOQO7+$T7+$TOYQPO7+$dO+ZQQO7+$aO+eQQO,59fOOQO-E6x-E6xOOQO1G.n1G.nO+rQQO<<HOOYQPO'#C{O,YQPO<<G{OOQO<<G{<<G{O,bQQO,59gOOQO-E6y-E6yOOQOAN=gAN=gOYQPO1G/RO,iQQO7+$m",
22
+ stateData: ",{~OrOSsPQ~O_WO`WOxRO{VO!OYO!PYO!QYO!RYO!SZO!TZO!VSO!XTO!`[O!e[O!f[O!g[O!h[O~Os]O~OS_OT_OW`OX`OgaO!YbO!ZcO!]cO!^dO!_dO!`eO!adO!bdO!cdO!ddO~OSeXTeXWeXXeXgeXpeX{ZX!YeX!ZeX!]eX!^eX!_eX!`eX!aeX!beX!ceX!deX!WeXyeX}eXzeX!UeX~O!UgO~PYOy|P~PYO{lO~OtnOunOvpO~OxqO~O![wO~O!awO_iX`iXxiX{iX!OiX!PiX!QiX!RiX!SiX!TiX!ViX!XiX!`iX!eiX!fiX!giX!hiX~O!WxO~P!fO}yOy|Xz|X~P!fOy{O~Opja!Wjayja}jazja!Uja~P!fOz|O~P!fOz|P~PYOtnOunOv!PO~OSUXTUXWUXXUXgUXpUX{^X!YUX!ZUX!]UX!^UX!_UX!`UX!aUX!bUX!cUX!dUX!WUXyUX}UXzUX!UUX~Oy!QO~P!fOphi!Whiyhi}hizhi!Uhi~P!fO!W!RO~P!fO}yOy|az|a~Oz!VO~O}!XO!U!ZO~P!fOyna}nazna~P!fOpfy!Wfyyfy}fyzfy!Ufy~P!fO}!XO!U!^O~O!W!_O~P!fO}oq!Uoq~P!fO`TgS_xS~",
23
+ goto: "%P}P!OP!RPP!c!RPP!R!f!v!f!yPP!R!R!R!R!R!RP!R!|!R#]!R#m#s#yPPPPPPP$PPPPP$yR^PmWOSTUV`ablxy!R!X!_Rr_mXOSTUV`ablxy!R!X!_RmXRs_kaQfhjktuv!S!T!W![!`mUOSTUV`ablxy!R!X!_Qo]R!OoQzhR!UzQ!Y!SR!]!YQQOQfSShTlQjUQkVQt`QuaQvbQ!SxQ!TyQ!W!RQ![!XR!`!_QiTR}l",
24
+ nodeNames: "⚠ BlockComment Expression PropertyAccess MemberOf NullSafeMemberOf Property ArrayAccess ArrayAccessor NullSafeArrayAccessor Call Function Arguments MethodAccess Method Number String Boolean Null Object Array Variable TernaryExpression Operator BinaryExpression OperatorKeyword UnaryExpression UnaryOperator Application",
25
+ maxTerm: 70,
26
26
  nodeProps: [
27
- [t, 13,"number",14,"string",15,"bool",16,"null",17,"object",18,"array"]
27
+ [t, 15,"number",16,"string",17,"bool",18,"null",19,"object",20,"array"]
28
28
  ],
29
- skippedNodes: [0,1,27],
29
+ skippedNodes: [0,1,29],
30
30
  repeatNodeCount: 3,
31
- tokenData: "-a~RqXY#YYZ#Y]^#Ypq#Yqr#krs$Quv#{vw%zwx&Sxy'wyz'|z{(R{|#{|}(Z}!O#{!O!P(`!P!Q(h!Q![(u![!]+[!^!_+a!_!`+l!`!a+r!a!b+}!c!},b!}#O,s#P#Q,x#Q#R#{#R#S,b#T#o,b#o#p,}#p#q-S#q#r-[#r#s#{~#_Sp~XY#YYZ#Y]^#Ypq#Y~#pPe~!_!`#s~#xPe~!_!`#{~$QOe~~$TXOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t<%l~$Q~O$Q~~$p~$uO^~~$xRO;'S$Q;'S;=`%R;=`O$Q~%UYOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t;=`<%l$Q<%l~$Q~O$Q~~$p~%wP;=`<%l$Q~&PPe~vw#{~&VXOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q<%l~&S~O&S~~$p~&uRO;'S&S;'S;=`'O;=`O&S~'RYOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q;=`<%l&S<%l~&S~O&S~~$p~'tP;=`<%l&S~'|Oz~~(ROy~~(WPe~z{#{~(`O|~~(ePS~!O!P#{~(mPe~z{(p~(uOq~~(zT]~!O!P)Z!Q![(u!g!h*Z#R#S+U#X#Y*Z~)^SO!O)j!P;'S)j;'S;=`+O<%lO)j~)oR]~!Q![)x!g!h*Z#X#Y*Z~)}S]~!Q![)x!g!h*Z#R#S*x#X#Y*Z~*^R{|*g}!O*g!Q![*m~*jP!Q![*m~*rQ]~!Q![*m#R#S*g~*{P!Q![)x~+RP;=`<%l)j~+XP!Q![(u~+aO!V~~+fQe~!^!_#{!_!`#{~+oP!_!`#s~+wQe~!_!`#{!`!a#{~,SRe~!O!P,]![!]#{!a!b#{~,bOT~~,gSv~!Q![,b!c!},b#R#S,b#T#o,b~,xOx~~,}Ow~~-SO!U~~-XPe~#p#q#{~-aO!T~",
32
- tokenizers: [1, new lr.LocalTokenGroup("j~RQYZXz{^~^Os~~aP!P!Qd~iOt~~", 25, 34)],
31
+ tokenData: "-k~RqXY#YYZ#Y]^#Ypq#Yqr#krs$Quv#{vw%zwx&Sxy'wyz'|z{(R{|#{|}(Z}!O#{!O!P(`!P!Q(h!Q![(u![!]+[!^!_+a!_!`+l!`!a+r!a!b+}!c!},j!}#O,{#P#Q-S#Q#R#{#R#S,j#T#o,j#o#p-X#p#q-^#q#r-f#r#s#{~#_Sr~XY#YYZ#Y]^#Ypq#Y~#pPg~!_!`#s~#xPg~!_!`#{~$QOg~~$TXOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t<%l~$Q~O$Q~~$p~$uO`~~$xRO;'S$Q;'S;=`%R;=`O$Q~%UYOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t;=`<%l$Q<%l~$Q~O$Q~~$p~%wP;=`<%l$Q~&PPg~vw#{~&VXOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q<%l~&S~O&S~~$p~&uRO;'S&S;'S;=`'O;=`O&S~'RYOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q;=`<%l&S<%l~&S~O&S~~$p~'tP;=`<%l&S~'|O{~~(ROz~~(WPg~z{#{~(`O}~~(ePS~!O!P#{~(mPg~z{(p~(uOs~~(zT_~!O!P)Z!Q![(u!g!h*Z#R#S+U#X#Y*Z~)^SO!O)j!P;'S)j;'S;=`+O<%lO)j~)oR_~!Q![)x!g!h*Z#X#Y*Z~)}S_~!Q![)x!g!h*Z#R#S*x#X#Y*Z~*^R{|*g}!O*g!Q![*m~*jP!Q![*m~*rQ_~!Q![*m#R#S*g~*{P!Q![)x~+RP;=`<%l)j~+XP!Q![(u~+aO!W~~+fQg~!^!_#{!_!`#{~+oP!_!`#s~+wQg~!_!`#{!`!a#{~,SRg~!O!P,]![!]#{!a!b#{~,bPT~!}#O,e~,jOX~~,oSx~!Q![,j!c!},j#R#S,j#T#o,jU-SO!XQWS~-XOy~~-^O!V~~-cPg~#p#q#{~-kO!U~",
32
+ tokenizers: [1, 2, new lr.LocalTokenGroup("j~RQYZXz{^~^Ou~~aP!P!Qd~iOv~~", 25, 36)],
33
33
  topRules: {"Expression":[0,2]},
34
- specialized: [{term: 38, get: (value) => spec_word[value] || -1},{term: 21, get: (value) => spec_Operator[value] || -1}],
35
- tokenPrec: 532
34
+ specialized: [{term: 40, get: (value) => spec_word[value] || -1},{term: 23, get: (value) => spec_Operator[value] || -1}],
35
+ tokenPrec: 541
36
36
  });
37
37
 
38
38
  // generate CONFIGURATION.md from this file by running "tsdoc --src=src/types.ts --dest=CONFIGURATION.md --noemoji --types"
@@ -56,21 +56,23 @@ exports.ELScalar = void 0;
56
56
  const BlockComment = 1,
57
57
  Expression = 2,
58
58
  PropertyAccess = 3,
59
+ NullSafeMemberOf = 5,
59
60
  Property = 6,
60
61
  ArrayAccess = 7,
61
- Call = 8,
62
- Function = 9,
63
- Arguments = 10,
64
- MethodAccess = 11,
65
- Method = 12,
66
- String = 14,
67
- Array$1 = 18,
68
- Variable = 19,
69
- TernaryExpression = 20,
70
- BinaryExpression = 22,
71
- OperatorKeyword = 23,
72
- UnaryExpression = 24,
73
- Application = 26;
62
+ NullSafeArrayAccessor = 9,
63
+ Call = 10,
64
+ Function = 11,
65
+ Arguments = 12,
66
+ MethodAccess = 13,
67
+ Method = 14,
68
+ String = 16,
69
+ Array$1 = 20,
70
+ Variable = 21,
71
+ TernaryExpression = 22,
72
+ BinaryExpression = 24,
73
+ OperatorKeyword = 25,
74
+ UnaryExpression = 26,
75
+ Application = 28;
74
76
 
75
77
  const createInfoElement = (html) => {
76
78
  const dom = document.createElement("div");
@@ -94,7 +96,7 @@ function resolveFunctionDefinition(node, state, config) {
94
96
  let identifier;
95
97
  if ((node.type.is(PropertyAccess) || node.type.is(MethodAccess)) && node.lastChild) {
96
98
  const leftArgument = (_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.node;
97
- const types = Array.from(resolveTypes(state, leftArgument, config));
99
+ const types = window.Array.from(resolveTypes(state, leftArgument, config));
98
100
  identifier = state.sliceDoc(node.lastChild.from, node.lastChild.to);
99
101
  return types.map(type => { var _a; return resolveCallable(identifier, (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]); }).find(x => x);
100
102
  }
@@ -117,42 +119,70 @@ const resolveIdentifier = (nodeTypeId, identifier, config) => {
117
119
  };
118
120
  function resolveTypes(state, node, config) {
119
121
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
120
- let types = new Set();
122
+ const types = new Set();
121
123
  if (!node) {
122
124
  return types;
123
125
  }
124
126
  let type;
125
127
  if (typeof (type = node.type.prop(t)) !== "undefined") {
126
- types.add(type);
128
+ if (type === exports.ELScalar.Array) {
129
+ // For array literals, infer element types from array contents
130
+ const elementTypes = new Set();
131
+ for (let child = node.firstChild; child; child = child.nextSibling) {
132
+ if (!child.type.isError) {
133
+ resolveTypes(state, child, config).forEach(elementType => {
134
+ elementTypes.add(elementType);
135
+ });
136
+ }
137
+ }
138
+ if (elementTypes.size > 0) {
139
+ // Build typed array notation (e.g., "string[]")
140
+ elementTypes.forEach(elementType => {
141
+ types.add(`${elementType}[]`);
142
+ });
143
+ }
144
+ else {
145
+ types.add(type);
146
+ }
147
+ }
148
+ else {
149
+ types.add(type);
150
+ }
127
151
  }
128
152
  else if (node.type.is(Call) && node.firstChild && node.lastChild) {
129
153
  resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
130
154
  }
131
155
  else if (node.type.is(Variable)) {
132
156
  const varName = state.sliceDoc(node.from, node.to) || '';
133
- // @ts-ignore
157
+ // @ts-expect-error TS2339
134
158
  (_b = (_a = resolveIdentifier(node.type.id, varName, config)) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.forEach((x) => types.add(x));
135
159
  }
136
160
  else if (node.type.is(Function)) {
137
161
  const varName = state.sliceDoc(node.from, node.to) || '';
138
- // @ts-ignore
162
+ // @ts-expect-error TS2339
139
163
  (_d = (_c = resolveIdentifier(node.type.id, varName, config)) === null || _c === void 0 ? void 0 : _c.returnType) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
140
164
  }
141
165
  else if (node.type.is(PropertyAccess) && node.firstChild && ((_e = node.lastChild) === null || _e === void 0 ? void 0 : _e.type.is(Property))) {
142
166
  const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
143
167
  (_f = resolveTypes(state, node.firstChild, config)) === null || _f === void 0 ? void 0 : _f.forEach(baseType => {
144
168
  var _a, _b, _c, _d;
145
- // @ts-ignore
169
+ // @ts-expect-error TS2339
146
170
  (_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.type.id, varName, (_b = config.types) === null || _b === void 0 ? void 0 : _b[baseType])) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
147
171
  });
172
+ if (node.getChild(NullSafeMemberOf)) {
173
+ types.add(exports.ELScalar.Null);
174
+ }
148
175
  }
149
176
  else if (node.type.is(MethodAccess) && node.firstChild && ((_g = node.lastChild) === null || _g === void 0 ? void 0 : _g.type.is(Method))) {
150
177
  const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
151
178
  (_h = resolveTypes(state, node.firstChild, config)) === null || _h === void 0 ? void 0 : _h.forEach(baseType => {
152
179
  var _a, _b, _c, _d;
153
- // @ts-ignore
180
+ // @ts-expect-error TS2339
154
181
  (_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.type.id, varName, (_b = config.types) === null || _b === void 0 ? void 0 : _b[baseType])) === null || _c === void 0 ? void 0 : _c.returnType) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
155
182
  });
183
+ if (node.getChild(NullSafeMemberOf)) {
184
+ types.add(exports.ELScalar.Null);
185
+ }
156
186
  }
157
187
  // Array indexing: for typed arrays (e.g. Foo[]) return element type, for generic arrays return any
158
188
  else if (node.type.is(ArrayAccess) && node.firstChild) {
@@ -165,6 +195,9 @@ function resolveTypes(state, node, config) {
165
195
  types.add(exports.ELScalar.Any);
166
196
  }
167
197
  });
198
+ if (node.getChild(NullSafeArrayAccessor)) {
199
+ types.add(exports.ELScalar.Null);
200
+ }
168
201
  }
169
202
  else if (node.type.is(Application) && node.firstChild) {
170
203
  resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
@@ -234,13 +267,13 @@ var utils = /*#__PURE__*/Object.freeze({
234
267
  */
235
268
  const expressionLanguageLinterSource = (state) => {
236
269
  const config = getExpressionLanguageConfig(state);
237
- let diagnostics = [];
270
+ const diagnostics = [];
238
271
  language.syntaxTree(state).cursor().iterate(node => {
239
- var _a, _b, _c, _d, _e, _f;
272
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
240
273
  const { from, to, type: { id } } = node;
241
274
  let identifier;
242
275
  switch (id) {
243
- case 0:
276
+ case 0: {
244
277
  if (state.doc.length === 0 || from === 0) {
245
278
  // Don't show error on empty doc (even though it is an error)
246
279
  return;
@@ -254,7 +287,8 @@ const expressionLanguageLinterSource = (state) => {
254
287
  diagnostics.push({ from, to, severity: 'error', message: `Unexpected ${type} <code>${identifier}</code>` });
255
288
  }
256
289
  return;
257
- case Arguments:
290
+ }
291
+ case Arguments: {
258
292
  const fn = resolveFunctionDefinition(node.node.prevSibling, state, config);
259
293
  const args = fn === null || fn === void 0 ? void 0 : fn.args;
260
294
  if (!args) {
@@ -275,7 +309,12 @@ const expressionLanguageLinterSource = (state) => {
275
309
  const typesUsed = Array.from(resolveTypes(state, n, config));
276
310
  const typesExpected = args[i].type;
277
311
  if (typesExpected && !typesExpected.includes(exports.ELScalar.Any) && !typesUsed.some(x => typesExpected.includes(x))) {
278
- diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>` });
312
+ diagnostics.push({
313
+ from: n.from,
314
+ to: n.to,
315
+ severity: 'error',
316
+ message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>`
317
+ });
279
318
  }
280
319
  i++;
281
320
  }
@@ -283,37 +322,93 @@ const expressionLanguageLinterSource = (state) => {
283
322
  diagnostics.push({ from: node.from, to: node.to, severity: 'error', message: `Too few arguments – ${argumentCountHintFn()}` });
284
323
  }
285
324
  break;
325
+ }
286
326
  case Property:
287
- case Method:
288
- const leftArgument = (_e = (_d = node.node.parent) === null || _d === void 0 ? void 0 : _d.firstChild) === null || _e === void 0 ? void 0 : _e.node;
327
+ case Method: {
328
+ const parent = node.node.parent;
329
+ const isPropertyAccess = parent === null || parent === void 0 ? void 0 : parent.type.is(PropertyAccess);
330
+ const isMethodAccess = parent === null || parent === void 0 ? void 0 : parent.type.is(MethodAccess);
331
+ // Skip validation if this is part of a PropertyAccess/MethodAccess on array type
332
+ // (the PropertyAccess/MethodAccess case will handle the error)
333
+ if ((isPropertyAccess || isMethodAccess) && (parent === null || parent === void 0 ? void 0 : parent.firstChild)) {
334
+ const types = Array.from(resolveTypes(state, parent.firstChild.node, config));
335
+ const hasArrayType = types.includes(exports.ELScalar.Array) || types.some(x => x.endsWith('[]'));
336
+ if (hasArrayType && !types.includes(exports.ELScalar.Any)) {
337
+ break; // Skip property validation, error will be reported by PropertyAccess/MethodAccess case
338
+ }
339
+ }
340
+ const leftArgument = (_d = parent === null || parent === void 0 ? void 0 : parent.firstChild) === null || _d === void 0 ? void 0 : _d.node;
289
341
  const types = Array.from(resolveTypes(state, leftArgument, config));
290
342
  identifier = state.sliceDoc(from, to);
291
343
  if (!types.find(type => { var _a; return resolveIdentifier(id, identifier, (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]); })) {
292
344
  diagnostics.push({ from, to, severity: 'error', message: `${node.name} <code>${identifier}</code> not found in <code>${types.join('|')}</code>` });
293
345
  }
294
346
  break;
347
+ }
348
+ case PropertyAccess:
349
+ case MethodAccess: {
350
+ const leftArgument = (_e = node.node.firstChild) === null || _e === void 0 ? void 0 : _e.node;
351
+ const types = Array.from(resolveTypes(state, leftArgument, config));
352
+ const hasArrayType = types.includes(exports.ELScalar.Array) || types.some(x => x.endsWith('[]'));
353
+ if (hasArrayType && !types.includes(exports.ELScalar.Any)) {
354
+ const propertyOrMethod = node.node.lastChild;
355
+ const errorFrom = (_f = propertyOrMethod === null || propertyOrMethod === void 0 ? void 0 : propertyOrMethod.from) !== null && _f !== void 0 ? _f : from;
356
+ const errorTo = (_g = propertyOrMethod === null || propertyOrMethod === void 0 ? void 0 : propertyOrMethod.to) !== null && _g !== void 0 ? _g : to;
357
+ diagnostics.push({ from: errorFrom, to: errorTo, severity: 'error', message: `Unexpected object access on <code>${types.join('|')}</code>` });
358
+ }
359
+ break;
360
+ }
361
+ case ArrayAccess: {
362
+ const leftArgument = (_h = node.node.firstChild) === null || _h === void 0 ? void 0 : _h.node;
363
+ const arrayAccessor = leftArgument.nextSibling;
364
+ const types = Array.from(resolveTypes(state, leftArgument, config));
365
+ const allowsAny = types.includes(exports.ELScalar.Array) || types.includes(exports.ELScalar.Any);
366
+ if (!allowsAny && ![...types].some(x => x.endsWith('[]'))) {
367
+ diagnostics.push({ from: arrayAccessor.from, to, severity: 'error', message: `Unexpected array access on <code>${types.join('|')}</code>` });
368
+ }
369
+ break;
370
+ }
295
371
  case Variable:
296
- case Function:
372
+ case Function: {
297
373
  identifier = state.sliceDoc(from, node.node.firstChild ? node.node.firstChild.from - 1 : to);
298
374
  if (!resolveIdentifier(id, identifier, config)) {
299
375
  diagnostics.push({ from, to, severity: 'error', message: `${node.node.name} <code>${identifier}</code> not found` });
300
376
  }
301
377
  break;
302
- case BinaryExpression:
378
+ }
379
+ case BinaryExpression: {
303
380
  const operatorNode = node.node.getChild(OperatorKeyword);
304
381
  if (operatorNode) {
305
382
  const operator = state.sliceDoc(operatorNode.from, operatorNode.to);
383
+ const leftArgument = node.node.firstChild;
384
+ const rightArgument = node.node.lastChild;
306
385
  if (operator === 'in') {
307
- const rightArgument = node.node.lastChild;
308
- const types = resolveTypes(state, rightArgument, config);
309
- if (!types.has(exports.ELScalar.Array)) {
310
- diagnostics.push({ from: rightArgument.from, to: rightArgument.to, severity: 'error', message: `<code>${exports.ELScalar.Array}</code> expected, got <code>${[...types].join('|')}</code>` });
386
+ const leftTypes = resolveTypes(state, leftArgument, config);
387
+ const rightTypes = resolveTypes(state, rightArgument, config);
388
+ const allowsAny = rightTypes.has(exports.ELScalar.Array) || rightTypes.has(exports.ELScalar.Any);
389
+ if (!allowsAny && ![...rightTypes].some(x => x.endsWith('[]'))) {
390
+ diagnostics.push({ from: rightArgument.from, to: rightArgument.to, severity: 'error', message: `<code>${exports.ELScalar.Array}</code> expected, got <code>${[...rightTypes].join('|')}</code>` });
391
+ }
392
+ else if (!allowsAny && !rightTypes.has(`${exports.ELScalar.Any}[]`) && ![...leftTypes].some(type => rightTypes.has(`${type}[]`))) {
393
+ diagnostics.push({ from: leftArgument.from, to: rightArgument.to, severity: 'warning', message: `Expression is always <code>false</code> because <code>${[...leftTypes].join('|')}</code> not found in <code>${[...rightTypes].join('|')}</code>` });
394
+ }
395
+ }
396
+ else if (["contains", "starts with", "ends with", "matches"].includes(operator)) {
397
+ // Both sides must be string
398
+ const leftTypes = resolveTypes(state, leftArgument, config);
399
+ const rightTypes = resolveTypes(state, rightArgument, config);
400
+ if (!leftTypes.has(exports.ELScalar.String)) {
401
+ diagnostics.push({ from: leftArgument.from, to: leftArgument.to, severity: 'error', message: `<code>string</code> expected, got <code>${[...leftTypes].join('|')}</code>` });
402
+ }
403
+ if (!rightTypes.has(exports.ELScalar.String)) {
404
+ diagnostics.push({ from: rightArgument.from, to: rightArgument.to, severity: 'error', message: `<code>string</code> expected, got <code>${[...rightTypes].join('|')}</code>` });
311
405
  }
312
406
  }
313
407
  }
314
408
  break;
409
+ }
315
410
  }
316
- if (identifier && ((_f = node.node.parent) === null || _f === void 0 ? void 0 : _f.type.isError)) {
411
+ if (identifier && ((_j = node.node.parent) === null || _j === void 0 ? void 0 : _j.type.isError)) {
317
412
  diagnostics.push({ from, to, severity: 'error', message: `Unexpected identifier <code>${identifier}</code>` });
318
413
  }
319
414
  });
@@ -383,7 +478,7 @@ function completeIdentifier(state, config, tree, from, to) {
383
478
  validFor: /^[a-zA-Z_]+[a-zA-Z_0-9]*$/,
384
479
  };
385
480
  }
386
- function completeMember(state, config, tree, from, to, explicit) {
481
+ function completeMember(state, config, tree, from, to) {
387
482
  var _a, _b, _c, _d, _e, _f;
388
483
  if (!(((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.type.is(PropertyAccess)) || ((_b = tree.parent) === null || _b === void 0 ? void 0 : _b.type.is(MethodAccess))) || !((_c = tree.parent) === null || _c === void 0 ? void 0 : _c.firstChild)) {
389
484
  return null;
@@ -392,7 +487,7 @@ function completeMember(state, config, tree, from, to, explicit) {
392
487
  if (!(types === null || types === void 0 ? void 0 : types.size)) {
393
488
  return null;
394
489
  }
395
- let options = [];
490
+ const options = [];
396
491
  for (const type of types) {
397
492
  const typeDeclaration = (_d = config.types) === null || _d === void 0 ? void 0 : _d[type];
398
493
  options.push(...(((_e = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.identifiers) === null || _e === void 0 ? void 0 : _e.map(autocompleteIdentifier)) || []), ...(((_f = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.functions) === null || _f === void 0 ? void 0 : _f.map(autocompleteFunction)) || []));
@@ -409,7 +504,7 @@ function expressionLanguageCompletion(context) {
409
504
  const { state, pos, explicit } = context;
410
505
  const tree = language.syntaxTree(state);
411
506
  const lastChar = state.sliceDoc(pos - 1, pos);
412
- const prevNode = tree.resolveInner(pos, lastChar === ')' ? 0 : -1);
507
+ const prevNode = tree.resolveInner(pos, (lastChar === ')' || lastChar === ']') ? 0 : -1);
413
508
  const config = getExpressionLanguageConfig(state);
414
509
  const isIdentifier = (node) => (node === null || node === void 0 ? void 0 : node.type.is(Variable)) || (node === null || node === void 0 ? void 0 : node.type.is(Function));
415
510
  const isMember = (node) => (node === null || node === void 0 ? void 0 : node.type.is(Property)) || (node === null || node === void 0 ? void 0 : node.type.is(Method));
@@ -454,7 +549,6 @@ function resolveArguments(node) {
454
549
  }
455
550
  function getCursorTooltips(state) {
456
551
  const config = getExpressionLanguageConfig(state);
457
- // @ts-ignore
458
552
  return state.selection.ranges
459
553
  .filter(range => range.empty)
460
554
  .map(range => {
@@ -480,7 +574,7 @@ function getCursorTooltips(state) {
480
574
  strictSide: false,
481
575
  arrow: true,
482
576
  create: () => {
483
- let dom = document.createElement("div");
577
+ const dom = document.createElement("div");
484
578
  dom.className = "cm-tooltip-cursor";
485
579
  dom.textContent = `${argName}`;
486
580
  return { dom };
package/dist/index.js CHANGED
@@ -6,31 +6,31 @@ import { insertCompletionText } from '@codemirror/autocomplete';
6
6
  import { StateField } from '@codemirror/state';
7
7
  import { showTooltip, hoverTooltip, EditorView } from '@codemirror/view';
8
8
 
9
- // @ts-ignore
9
+ // @ts-expect-error TS2739
10
10
  const t = {
11
11
  deserialize: (str) => str,
12
12
  };
13
13
 
14
14
  // This file was generated by lezer-generator. You probably shouldn't edit it.
15
- const spec_word = {__proto__:null,true:90, TRUE:92, false:94, FALSE:96, null:98, NULL:100, starts:110, with:112, ends:114, contains:116, matches:118, not:120, in:122, and:124, or:126, xor:128};
16
- const spec_Operator = {__proto__:null,"?":109, "!":131, "~":133, "+":135, "-":137};
15
+ const spec_word = {__proto__:null,true:92, TRUE:94, false:96, FALSE:98, null:100, NULL:102, starts:114, with:116, ends:118, contains:120, matches:122, not:124, in:126, and:128, or:130, xor:132};
16
+ const spec_Operator = {__proto__:null,"?":113, "!":135, "~":137, "+":139, "-":141};
17
17
  const parser = /*@__PURE__*/LRParser.deserialize({
18
18
  version: 14,
19
- states: ")YOYQPOOP!aOPOOQ!fQPOOO#dQPO'#CeO$wQPO'#CmO%OQPO'#CnOYQPO'#CtOYQPO'#CvOOQO'#DR'#DRO%VQPO'#CdOOQO'#Ck'#CkOOQO'#Cl'#ClOOQO'#Cu'#CuP%[OQO'#C]POOO)C>q)C>qO%gQPO,58yOYQPO,58}OYQPO,59^OYQPO,59[O%lQPO'#CsOOQO'#Cs'#CsO%qQPO'#CsO&{QPO,59XOOQO,59X,59XO'SQPO'#DXO'aQPO,59YO'fQPO,59`O'|QPO,59bO(TQPO'#CfOOQO,59O,59OPOOO'#Cw'#CwP([OQO,58wPOOO,58w,58wO(gQPO'#CbOOQO1G.e1G.eOOQO1G.m1G.mO)zQPO1G.iO*RQPO1G.xO*iQPO1G.vOOQO,59_,59_OYQPO1G.sOYQPO'#CxO*pQPO,59sOOQO1G.t1G.tOOQO1G.|1G.|O*{QPO,59QPOOO-E6u-E6uPOOO1G.c1G.cOOQO7+$T7+$TOYQPO7+$bO+QQPO7+$_O+[QPO,59dOOQO-E6v-E6vOOQO1G.l1G.lO+iQPO<<G|OYQPO'#CyO,PQPO<<GyOOQO<<Gy<<GyO,XQPO,59eOOQO-E6w-E6wOOQOAN=eAN=eOYQPO1G/PO,`QPO7+$k",
20
- stateData: ",r~OpOSqPQ~O]WO^WOvROxTOzVO}YO!OYO!PYO!QYO!RZO!SZO!USO!^[O!c[O!d[O!e[O!f[O~Oq]O~OS_OT_OeaOx`O!WbO!XcO!ZcO![dO!]dO!^eO!_dO!`dO!adO!bdO~OScXTcXecXncXxcXzXX!WcX!XcX!ZcX![cX!]cX!^cX!_cX!`cX!acX!bcX!VcXwcX|cXycX!TcX~O!TgO~PYOw{P~PYOzlO~OrnOsnOtpO~OvqO~O!YwO~O!_wO]gX^gXvgXxgXzgX}gX!OgX!PgX!QgX!RgX!SgX!UgX!^gX!cgX!dgX!egX!fgX~O!VxO~P!fO|yOw{Xy{X~P!fOw{O~Onha!Vhawha|hayha!Tha~P!fOy|O~P!fOy{P~PYOrnOsnOt!PO~OSUXTUXeUXnUXxUXz[X!WUX!XUX!ZUX![UX!]UX!^UX!_UX!`UX!aUX!bUX!VUXwUX|UXyUX!TUX~Ow!QO~P!fOnfi!Vfiwfi|fiyfi!Tfi~P!fO!V!RO~P!fO|yOw{ay{a~Oy!VO~O|!XO!T!ZO~P!fOwla|layla~P!fOndy!Vdywdy|dyydy!Tdy~P!fO|!XO!T!^O~O!V!_O~P!fO|mq!Tmq~P!fO^TeS]vS~",
21
- goto: "%O|P}P!QPP!b!Q!Q!e!u!e!xPP!Q!Q!Q!Q!Q!QP!Q!{!Q#[!Q#l#r#xPPPPPPP$OPPPPP$xR^PmWOSTUV`ablxy!R!X!_Rr_mXOSTUV`ablxy!R!X!_RmXRs_kaQfhjktuv!S!T!W![!`mUOSTUV`ablxy!R!X!_Qo]R!OoQzhR!UzQ!Y!SR!]!YQQOQfSShTlQjUQkVQt`QuaQvbQ!SxQ!TyQ!W!RQ![!XR!`!_QiTR}l",
22
- nodeNames: "⚠ BlockComment Expression PropertyAccess MemberOf NullSafeMemberOf Property ArrayAccess Call Function Arguments MethodAccess Method Number String Boolean Null Object Array Variable TernaryExpression Operator BinaryExpression OperatorKeyword UnaryExpression UnaryOperator Application",
23
- maxTerm: 68,
19
+ states: ")YOYQPOOP!aOPOOQ!fQQOOO#gQQO'#CgO$}QPO'#CoO%UQPO'#CpOYQPO'#CvOYQPO'#CxOOQO'#DT'#DTO%]QPO'#CfOOQO'#Cm'#CmOOQO'#Cn'#CnOOQO'#Cw'#CwP%bOSO'#C]POOO)C>s)C>sO%mQPO,58yOYQPO,58}OYQPO,59`OYQPO,59^O%rQPO'#CuOOQO'#Cu'#CuO%wQPO'#CuO'RQQO,59ZOOQO,59Z,59ZO'YQQO'#DYO'gQPO,59[O'lQQO,59bO(SQQO,59dO(ZQPO'#ChOOQO,59Q,59QPOOO'#Cy'#CyP(bOSO,58wPOOO,58w,58wO(mQQO'#CbOOQO1G.e1G.eOOQO1G.o1G.oO*TQQO1G.iO*[QQO1G.zO*rQQO1G.xOOQO,59a,59aOYQPO1G.uOYQPO'#CzO*yQPO,59tOOQO1G.v1G.vOOQO1G/O1G/OO+UQPO,59SPOOO-E6w-E6wPOOO1G.c1G.cOOQO7+$T7+$TOYQPO7+$dO+ZQQO7+$aO+eQQO,59fOOQO-E6x-E6xOOQO1G.n1G.nO+rQQO<<HOOYQPO'#C{O,YQPO<<G{OOQO<<G{<<G{O,bQQO,59gOOQO-E6y-E6yOOQOAN=gAN=gOYQPO1G/RO,iQQO7+$m",
20
+ stateData: ",{~OrOSsPQ~O_WO`WOxRO{VO!OYO!PYO!QYO!RYO!SZO!TZO!VSO!XTO!`[O!e[O!f[O!g[O!h[O~Os]O~OS_OT_OW`OX`OgaO!YbO!ZcO!]cO!^dO!_dO!`eO!adO!bdO!cdO!ddO~OSeXTeXWeXXeXgeXpeX{ZX!YeX!ZeX!]eX!^eX!_eX!`eX!aeX!beX!ceX!deX!WeXyeX}eXzeX!UeX~O!UgO~PYOy|P~PYO{lO~OtnOunOvpO~OxqO~O![wO~O!awO_iX`iXxiX{iX!OiX!PiX!QiX!RiX!SiX!TiX!ViX!XiX!`iX!eiX!fiX!giX!hiX~O!WxO~P!fO}yOy|Xz|X~P!fOy{O~Opja!Wjayja}jazja!Uja~P!fOz|O~P!fOz|P~PYOtnOunOv!PO~OSUXTUXWUXXUXgUXpUX{^X!YUX!ZUX!]UX!^UX!_UX!`UX!aUX!bUX!cUX!dUX!WUXyUX}UXzUX!UUX~Oy!QO~P!fOphi!Whiyhi}hizhi!Uhi~P!fO!W!RO~P!fO}yOy|az|a~Oz!VO~O}!XO!U!ZO~P!fOyna}nazna~P!fOpfy!Wfyyfy}fyzfy!Ufy~P!fO}!XO!U!^O~O!W!_O~P!fO}oq!Uoq~P!fO`TgS_xS~",
21
+ goto: "%P}P!OP!RPP!c!RPP!R!f!v!f!yPP!R!R!R!R!R!RP!R!|!R#]!R#m#s#yPPPPPPP$PPPPP$yR^PmWOSTUV`ablxy!R!X!_Rr_mXOSTUV`ablxy!R!X!_RmXRs_kaQfhjktuv!S!T!W![!`mUOSTUV`ablxy!R!X!_Qo]R!OoQzhR!UzQ!Y!SR!]!YQQOQfSShTlQjUQkVQt`QuaQvbQ!SxQ!TyQ!W!RQ![!XR!`!_QiTR}l",
22
+ nodeNames: "⚠ BlockComment Expression PropertyAccess MemberOf NullSafeMemberOf Property ArrayAccess ArrayAccessor NullSafeArrayAccessor Call Function Arguments MethodAccess Method Number String Boolean Null Object Array Variable TernaryExpression Operator BinaryExpression OperatorKeyword UnaryExpression UnaryOperator Application",
23
+ maxTerm: 70,
24
24
  nodeProps: [
25
- [t, 13,"number",14,"string",15,"bool",16,"null",17,"object",18,"array"]
25
+ [t, 15,"number",16,"string",17,"bool",18,"null",19,"object",20,"array"]
26
26
  ],
27
- skippedNodes: [0,1,27],
27
+ skippedNodes: [0,1,29],
28
28
  repeatNodeCount: 3,
29
- tokenData: "-a~RqXY#YYZ#Y]^#Ypq#Yqr#krs$Quv#{vw%zwx&Sxy'wyz'|z{(R{|#{|}(Z}!O#{!O!P(`!P!Q(h!Q![(u![!]+[!^!_+a!_!`+l!`!a+r!a!b+}!c!},b!}#O,s#P#Q,x#Q#R#{#R#S,b#T#o,b#o#p,}#p#q-S#q#r-[#r#s#{~#_Sp~XY#YYZ#Y]^#Ypq#Y~#pPe~!_!`#s~#xPe~!_!`#{~$QOe~~$TXOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t<%l~$Q~O$Q~~$p~$uO^~~$xRO;'S$Q;'S;=`%R;=`O$Q~%UYOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t;=`<%l$Q<%l~$Q~O$Q~~$p~%wP;=`<%l$Q~&PPe~vw#{~&VXOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q<%l~&S~O&S~~$p~&uRO;'S&S;'S;=`'O;=`O&S~'RYOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q;=`<%l&S<%l~&S~O&S~~$p~'tP;=`<%l&S~'|Oz~~(ROy~~(WPe~z{#{~(`O|~~(ePS~!O!P#{~(mPe~z{(p~(uOq~~(zT]~!O!P)Z!Q![(u!g!h*Z#R#S+U#X#Y*Z~)^SO!O)j!P;'S)j;'S;=`+O<%lO)j~)oR]~!Q![)x!g!h*Z#X#Y*Z~)}S]~!Q![)x!g!h*Z#R#S*x#X#Y*Z~*^R{|*g}!O*g!Q![*m~*jP!Q![*m~*rQ]~!Q![*m#R#S*g~*{P!Q![)x~+RP;=`<%l)j~+XP!Q![(u~+aO!V~~+fQe~!^!_#{!_!`#{~+oP!_!`#s~+wQe~!_!`#{!`!a#{~,SRe~!O!P,]![!]#{!a!b#{~,bOT~~,gSv~!Q![,b!c!},b#R#S,b#T#o,b~,xOx~~,}Ow~~-SO!U~~-XPe~#p#q#{~-aO!T~",
30
- tokenizers: [1, /*@__PURE__*/new LocalTokenGroup("j~RQYZXz{^~^Os~~aP!P!Qd~iOt~~", 25, 34)],
29
+ tokenData: "-k~RqXY#YYZ#Y]^#Ypq#Yqr#krs$Quv#{vw%zwx&Sxy'wyz'|z{(R{|#{|}(Z}!O#{!O!P(`!P!Q(h!Q![(u![!]+[!^!_+a!_!`+l!`!a+r!a!b+}!c!},j!}#O,{#P#Q-S#Q#R#{#R#S,j#T#o,j#o#p-X#p#q-^#q#r-f#r#s#{~#_Sr~XY#YYZ#Y]^#Ypq#Y~#pPg~!_!`#s~#xPg~!_!`#{~$QOg~~$TXOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t<%l~$Q~O$Q~~$p~$uO`~~$xRO;'S$Q;'S;=`%R;=`O$Q~%UYOr$Qrs$ps#O$Q#O#P$u#P;'S$Q;'S;=`%t;=`<%l$Q<%l~$Q~O$Q~~$p~%wP;=`<%l$Q~&PPg~vw#{~&VXOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q<%l~&S~O&S~~$p~&uRO;'S&S;'S;=`'O;=`O&S~'RYOw&Swx$px#O&S#O#P&r#P;'S&S;'S;=`'q;=`<%l&S<%l~&S~O&S~~$p~'tP;=`<%l&S~'|O{~~(ROz~~(WPg~z{#{~(`O}~~(ePS~!O!P#{~(mPg~z{(p~(uOs~~(zT_~!O!P)Z!Q![(u!g!h*Z#R#S+U#X#Y*Z~)^SO!O)j!P;'S)j;'S;=`+O<%lO)j~)oR_~!Q![)x!g!h*Z#X#Y*Z~)}S_~!Q![)x!g!h*Z#R#S*x#X#Y*Z~*^R{|*g}!O*g!Q![*m~*jP!Q![*m~*rQ_~!Q![*m#R#S*g~*{P!Q![)x~+RP;=`<%l)j~+XP!Q![(u~+aO!W~~+fQg~!^!_#{!_!`#{~+oP!_!`#s~+wQg~!_!`#{!`!a#{~,SRg~!O!P,]![!]#{!a!b#{~,bPT~!}#O,e~,jOX~~,oSx~!Q![,j!c!},j#R#S,j#T#o,jU-SO!XQWS~-XOy~~-^O!V~~-cPg~#p#q#{~-kO!U~",
30
+ tokenizers: [1, 2, /*@__PURE__*/new LocalTokenGroup("j~RQYZXz{^~^Ou~~aP!P!Qd~iOv~~", 25, 36)],
31
31
  topRules: {"Expression":[0,2]},
32
- specialized: [{term: 38, get: (value) => spec_word[value] || -1},{term: 21, get: (value) => spec_Operator[value] || -1}],
33
- tokenPrec: 532
32
+ specialized: [{term: 40, get: (value) => spec_word[value] || -1},{term: 23, get: (value) => spec_Operator[value] || -1}],
33
+ tokenPrec: 541
34
34
  });
35
35
 
36
36
  // generate CONFIGURATION.md from this file by running "tsdoc --src=src/types.ts --dest=CONFIGURATION.md --noemoji --types"
@@ -53,21 +53,23 @@ return ELScalar})(ELScalar || (ELScalar = {}));
53
53
  const BlockComment = 1,
54
54
  Expression = 2,
55
55
  PropertyAccess = 3,
56
+ NullSafeMemberOf = 5,
56
57
  Property = 6,
57
58
  ArrayAccess = 7,
58
- Call = 8,
59
- Function = 9,
60
- Arguments = 10,
61
- MethodAccess = 11,
62
- Method = 12,
63
- String = 14,
64
- Array$1 = 18,
65
- Variable = 19,
66
- TernaryExpression = 20,
67
- BinaryExpression = 22,
68
- OperatorKeyword = 23,
69
- UnaryExpression = 24,
70
- Application = 26;
59
+ NullSafeArrayAccessor = 9,
60
+ Call = 10,
61
+ Function = 11,
62
+ Arguments = 12,
63
+ MethodAccess = 13,
64
+ Method = 14,
65
+ String = 16,
66
+ Array$1 = 20,
67
+ Variable = 21,
68
+ TernaryExpression = 22,
69
+ BinaryExpression = 24,
70
+ OperatorKeyword = 25,
71
+ UnaryExpression = 26,
72
+ Application = 28;
71
73
 
72
74
  const createInfoElement = (html) => {
73
75
  const dom = document.createElement("div");
@@ -91,7 +93,7 @@ function resolveFunctionDefinition(node, state, config) {
91
93
  let identifier;
92
94
  if ((node.type.is(PropertyAccess) || node.type.is(MethodAccess)) && node.lastChild) {
93
95
  const leftArgument = (_a = node.firstChild) === null || _a === void 0 ? void 0 : _a.node;
94
- const types = Array.from(resolveTypes(state, leftArgument, config));
96
+ const types = window.Array.from(resolveTypes(state, leftArgument, config));
95
97
  identifier = state.sliceDoc(node.lastChild.from, node.lastChild.to);
96
98
  return types.map(type => { var _a; return resolveCallable(identifier, (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]); }).find(x => x);
97
99
  }
@@ -114,42 +116,70 @@ const resolveIdentifier = (nodeTypeId, identifier, config) => {
114
116
  };
115
117
  function resolveTypes(state, node, config) {
116
118
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
117
- let types = new Set();
119
+ const types = new Set();
118
120
  if (!node) {
119
121
  return types;
120
122
  }
121
123
  let type;
122
124
  if (typeof (type = node.type.prop(t)) !== "undefined") {
123
- types.add(type);
125
+ if (type === ELScalar.Array) {
126
+ // For array literals, infer element types from array contents
127
+ const elementTypes = new Set();
128
+ for (let child = node.firstChild; child; child = child.nextSibling) {
129
+ if (!child.type.isError) {
130
+ resolveTypes(state, child, config).forEach(elementType => {
131
+ elementTypes.add(elementType);
132
+ });
133
+ }
134
+ }
135
+ if (elementTypes.size > 0) {
136
+ // Build typed array notation (e.g., "string[]")
137
+ elementTypes.forEach(elementType => {
138
+ types.add(`${elementType}[]`);
139
+ });
140
+ }
141
+ else {
142
+ types.add(type);
143
+ }
144
+ }
145
+ else {
146
+ types.add(type);
147
+ }
124
148
  }
125
149
  else if (node.type.is(Call) && node.firstChild && node.lastChild) {
126
150
  resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
127
151
  }
128
152
  else if (node.type.is(Variable)) {
129
153
  const varName = state.sliceDoc(node.from, node.to) || '';
130
- // @ts-ignore
154
+ // @ts-expect-error TS2339
131
155
  (_b = (_a = resolveIdentifier(node.type.id, varName, config)) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.forEach((x) => types.add(x));
132
156
  }
133
157
  else if (node.type.is(Function)) {
134
158
  const varName = state.sliceDoc(node.from, node.to) || '';
135
- // @ts-ignore
159
+ // @ts-expect-error TS2339
136
160
  (_d = (_c = resolveIdentifier(node.type.id, varName, config)) === null || _c === void 0 ? void 0 : _c.returnType) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
137
161
  }
138
162
  else if (node.type.is(PropertyAccess) && node.firstChild && ((_e = node.lastChild) === null || _e === void 0 ? void 0 : _e.type.is(Property))) {
139
163
  const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
140
164
  (_f = resolveTypes(state, node.firstChild, config)) === null || _f === void 0 ? void 0 : _f.forEach(baseType => {
141
165
  var _a, _b, _c, _d;
142
- // @ts-ignore
166
+ // @ts-expect-error TS2339
143
167
  (_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.type.id, varName, (_b = config.types) === null || _b === void 0 ? void 0 : _b[baseType])) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
144
168
  });
169
+ if (node.getChild(NullSafeMemberOf)) {
170
+ types.add(ELScalar.Null);
171
+ }
145
172
  }
146
173
  else if (node.type.is(MethodAccess) && node.firstChild && ((_g = node.lastChild) === null || _g === void 0 ? void 0 : _g.type.is(Method))) {
147
174
  const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
148
175
  (_h = resolveTypes(state, node.firstChild, config)) === null || _h === void 0 ? void 0 : _h.forEach(baseType => {
149
176
  var _a, _b, _c, _d;
150
- // @ts-ignore
177
+ // @ts-expect-error TS2339
151
178
  (_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.type.id, varName, (_b = config.types) === null || _b === void 0 ? void 0 : _b[baseType])) === null || _c === void 0 ? void 0 : _c.returnType) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
152
179
  });
180
+ if (node.getChild(NullSafeMemberOf)) {
181
+ types.add(ELScalar.Null);
182
+ }
153
183
  }
154
184
  // Array indexing: for typed arrays (e.g. Foo[]) return element type, for generic arrays return any
155
185
  else if (node.type.is(ArrayAccess) && node.firstChild) {
@@ -162,6 +192,9 @@ function resolveTypes(state, node, config) {
162
192
  types.add(ELScalar.Any);
163
193
  }
164
194
  });
195
+ if (node.getChild(NullSafeArrayAccessor)) {
196
+ types.add(ELScalar.Null);
197
+ }
165
198
  }
166
199
  else if (node.type.is(Application) && node.firstChild) {
167
200
  resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
@@ -231,13 +264,13 @@ var utils = /*#__PURE__*//*@__PURE__*/Object.freeze({
231
264
  */
232
265
  const expressionLanguageLinterSource = (state) => {
233
266
  const config = getExpressionLanguageConfig(state);
234
- let diagnostics = [];
267
+ const diagnostics = [];
235
268
  syntaxTree(state).cursor().iterate(node => {
236
- var _a, _b, _c, _d, _e, _f;
269
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
237
270
  const { from, to, type: { id } } = node;
238
271
  let identifier;
239
272
  switch (id) {
240
- case 0:
273
+ case 0: {
241
274
  if (state.doc.length === 0 || from === 0) {
242
275
  // Don't show error on empty doc (even though it is an error)
243
276
  return;
@@ -251,7 +284,8 @@ const expressionLanguageLinterSource = (state) => {
251
284
  diagnostics.push({ from, to, severity: 'error', message: `Unexpected ${type} <code>${identifier}</code>` });
252
285
  }
253
286
  return;
254
- case Arguments:
287
+ }
288
+ case Arguments: {
255
289
  const fn = resolveFunctionDefinition(node.node.prevSibling, state, config);
256
290
  const args = fn === null || fn === void 0 ? void 0 : fn.args;
257
291
  if (!args) {
@@ -272,7 +306,12 @@ const expressionLanguageLinterSource = (state) => {
272
306
  const typesUsed = Array.from(resolveTypes(state, n, config));
273
307
  const typesExpected = args[i].type;
274
308
  if (typesExpected && !typesExpected.includes(ELScalar.Any) && !typesUsed.some(x => typesExpected.includes(x))) {
275
- diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>` });
309
+ diagnostics.push({
310
+ from: n.from,
311
+ to: n.to,
312
+ severity: 'error',
313
+ message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>`
314
+ });
276
315
  }
277
316
  i++;
278
317
  }
@@ -280,37 +319,93 @@ const expressionLanguageLinterSource = (state) => {
280
319
  diagnostics.push({ from: node.from, to: node.to, severity: 'error', message: `Too few arguments – ${argumentCountHintFn()}` });
281
320
  }
282
321
  break;
322
+ }
283
323
  case Property:
284
- case Method:
285
- const leftArgument = (_e = (_d = node.node.parent) === null || _d === void 0 ? void 0 : _d.firstChild) === null || _e === void 0 ? void 0 : _e.node;
324
+ case Method: {
325
+ const parent = node.node.parent;
326
+ const isPropertyAccess = parent === null || parent === void 0 ? void 0 : parent.type.is(PropertyAccess);
327
+ const isMethodAccess = parent === null || parent === void 0 ? void 0 : parent.type.is(MethodAccess);
328
+ // Skip validation if this is part of a PropertyAccess/MethodAccess on array type
329
+ // (the PropertyAccess/MethodAccess case will handle the error)
330
+ if ((isPropertyAccess || isMethodAccess) && (parent === null || parent === void 0 ? void 0 : parent.firstChild)) {
331
+ const types = Array.from(resolveTypes(state, parent.firstChild.node, config));
332
+ const hasArrayType = types.includes(ELScalar.Array) || types.some(x => x.endsWith('[]'));
333
+ if (hasArrayType && !types.includes(ELScalar.Any)) {
334
+ break; // Skip property validation, error will be reported by PropertyAccess/MethodAccess case
335
+ }
336
+ }
337
+ const leftArgument = (_d = parent === null || parent === void 0 ? void 0 : parent.firstChild) === null || _d === void 0 ? void 0 : _d.node;
286
338
  const types = Array.from(resolveTypes(state, leftArgument, config));
287
339
  identifier = state.sliceDoc(from, to);
288
340
  if (!types.find(type => { var _a; return resolveIdentifier(id, identifier, (_a = config.types) === null || _a === void 0 ? void 0 : _a[type]); })) {
289
341
  diagnostics.push({ from, to, severity: 'error', message: `${node.name} <code>${identifier}</code> not found in <code>${types.join('|')}</code>` });
290
342
  }
291
343
  break;
344
+ }
345
+ case PropertyAccess:
346
+ case MethodAccess: {
347
+ const leftArgument = (_e = node.node.firstChild) === null || _e === void 0 ? void 0 : _e.node;
348
+ const types = Array.from(resolveTypes(state, leftArgument, config));
349
+ const hasArrayType = types.includes(ELScalar.Array) || types.some(x => x.endsWith('[]'));
350
+ if (hasArrayType && !types.includes(ELScalar.Any)) {
351
+ const propertyOrMethod = node.node.lastChild;
352
+ const errorFrom = (_f = propertyOrMethod === null || propertyOrMethod === void 0 ? void 0 : propertyOrMethod.from) !== null && _f !== void 0 ? _f : from;
353
+ const errorTo = (_g = propertyOrMethod === null || propertyOrMethod === void 0 ? void 0 : propertyOrMethod.to) !== null && _g !== void 0 ? _g : to;
354
+ diagnostics.push({ from: errorFrom, to: errorTo, severity: 'error', message: `Unexpected object access on <code>${types.join('|')}</code>` });
355
+ }
356
+ break;
357
+ }
358
+ case ArrayAccess: {
359
+ const leftArgument = (_h = node.node.firstChild) === null || _h === void 0 ? void 0 : _h.node;
360
+ const arrayAccessor = leftArgument.nextSibling;
361
+ const types = Array.from(resolveTypes(state, leftArgument, config));
362
+ const allowsAny = types.includes(ELScalar.Array) || types.includes(ELScalar.Any);
363
+ if (!allowsAny && ![...types].some(x => x.endsWith('[]'))) {
364
+ diagnostics.push({ from: arrayAccessor.from, to, severity: 'error', message: `Unexpected array access on <code>${types.join('|')}</code>` });
365
+ }
366
+ break;
367
+ }
292
368
  case Variable:
293
- case Function:
369
+ case Function: {
294
370
  identifier = state.sliceDoc(from, node.node.firstChild ? node.node.firstChild.from - 1 : to);
295
371
  if (!resolveIdentifier(id, identifier, config)) {
296
372
  diagnostics.push({ from, to, severity: 'error', message: `${node.node.name} <code>${identifier}</code> not found` });
297
373
  }
298
374
  break;
299
- case BinaryExpression:
375
+ }
376
+ case BinaryExpression: {
300
377
  const operatorNode = node.node.getChild(OperatorKeyword);
301
378
  if (operatorNode) {
302
379
  const operator = state.sliceDoc(operatorNode.from, operatorNode.to);
380
+ const leftArgument = node.node.firstChild;
381
+ const rightArgument = node.node.lastChild;
303
382
  if (operator === 'in') {
304
- const rightArgument = node.node.lastChild;
305
- const types = resolveTypes(state, rightArgument, config);
306
- if (!types.has(ELScalar.Array)) {
307
- diagnostics.push({ from: rightArgument.from, to: rightArgument.to, severity: 'error', message: `<code>${ELScalar.Array}</code> expected, got <code>${[...types].join('|')}</code>` });
383
+ const leftTypes = resolveTypes(state, leftArgument, config);
384
+ const rightTypes = resolveTypes(state, rightArgument, config);
385
+ const allowsAny = rightTypes.has(ELScalar.Array) || rightTypes.has(ELScalar.Any);
386
+ if (!allowsAny && ![...rightTypes].some(x => x.endsWith('[]'))) {
387
+ diagnostics.push({ from: rightArgument.from, to: rightArgument.to, severity: 'error', message: `<code>${ELScalar.Array}</code> expected, got <code>${[...rightTypes].join('|')}</code>` });
388
+ }
389
+ else if (!allowsAny && !rightTypes.has(`${ELScalar.Any}[]`) && ![...leftTypes].some(type => rightTypes.has(`${type}[]`))) {
390
+ diagnostics.push({ from: leftArgument.from, to: rightArgument.to, severity: 'warning', message: `Expression is always <code>false</code> because <code>${[...leftTypes].join('|')}</code> not found in <code>${[...rightTypes].join('|')}</code>` });
391
+ }
392
+ }
393
+ else if (["contains", "starts with", "ends with", "matches"].includes(operator)) {
394
+ // Both sides must be string
395
+ const leftTypes = resolveTypes(state, leftArgument, config);
396
+ const rightTypes = resolveTypes(state, rightArgument, config);
397
+ if (!leftTypes.has(ELScalar.String)) {
398
+ diagnostics.push({ from: leftArgument.from, to: leftArgument.to, severity: 'error', message: `<code>string</code> expected, got <code>${[...leftTypes].join('|')}</code>` });
399
+ }
400
+ if (!rightTypes.has(ELScalar.String)) {
401
+ diagnostics.push({ from: rightArgument.from, to: rightArgument.to, severity: 'error', message: `<code>string</code> expected, got <code>${[...rightTypes].join('|')}</code>` });
308
402
  }
309
403
  }
310
404
  }
311
405
  break;
406
+ }
312
407
  }
313
- if (identifier && ((_f = node.node.parent) === null || _f === void 0 ? void 0 : _f.type.isError)) {
408
+ if (identifier && ((_j = node.node.parent) === null || _j === void 0 ? void 0 : _j.type.isError)) {
314
409
  diagnostics.push({ from, to, severity: 'error', message: `Unexpected identifier <code>${identifier}</code>` });
315
410
  }
316
411
  });
@@ -380,7 +475,7 @@ function completeIdentifier(state, config, tree, from, to) {
380
475
  validFor: /^[a-zA-Z_]+[a-zA-Z_0-9]*$/,
381
476
  };
382
477
  }
383
- function completeMember(state, config, tree, from, to, explicit) {
478
+ function completeMember(state, config, tree, from, to) {
384
479
  var _a, _b, _c, _d, _e, _f;
385
480
  if (!(((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.type.is(PropertyAccess)) || ((_b = tree.parent) === null || _b === void 0 ? void 0 : _b.type.is(MethodAccess))) || !((_c = tree.parent) === null || _c === void 0 ? void 0 : _c.firstChild)) {
386
481
  return null;
@@ -389,7 +484,7 @@ function completeMember(state, config, tree, from, to, explicit) {
389
484
  if (!(types === null || types === void 0 ? void 0 : types.size)) {
390
485
  return null;
391
486
  }
392
- let options = [];
487
+ const options = [];
393
488
  for (const type of types) {
394
489
  const typeDeclaration = (_d = config.types) === null || _d === void 0 ? void 0 : _d[type];
395
490
  options.push(...(((_e = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.identifiers) === null || _e === void 0 ? void 0 : _e.map(autocompleteIdentifier)) || []), ...(((_f = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.functions) === null || _f === void 0 ? void 0 : _f.map(autocompleteFunction)) || []));
@@ -406,7 +501,7 @@ function expressionLanguageCompletion(context) {
406
501
  const { state, pos, explicit } = context;
407
502
  const tree = syntaxTree(state);
408
503
  const lastChar = state.sliceDoc(pos - 1, pos);
409
- const prevNode = tree.resolveInner(pos, lastChar === ')' ? 0 : -1);
504
+ const prevNode = tree.resolveInner(pos, (lastChar === ')' || lastChar === ']') ? 0 : -1);
410
505
  const config = getExpressionLanguageConfig(state);
411
506
  const isIdentifier = (node) => (node === null || node === void 0 ? void 0 : node.type.is(Variable)) || (node === null || node === void 0 ? void 0 : node.type.is(Function));
412
507
  const isMember = (node) => (node === null || node === void 0 ? void 0 : node.type.is(Property)) || (node === null || node === void 0 ? void 0 : node.type.is(Method));
@@ -451,7 +546,6 @@ function resolveArguments(node) {
451
546
  }
452
547
  function getCursorTooltips(state) {
453
548
  const config = getExpressionLanguageConfig(state);
454
- // @ts-ignore
455
549
  return state.selection.ranges
456
550
  .filter(range => range.empty)
457
551
  .map(range => {
@@ -477,7 +571,7 @@ function getCursorTooltips(state) {
477
571
  strictSide: false,
478
572
  arrow: true,
479
573
  create: () => {
480
- let dom = document.createElement("div");
574
+ const dom = document.createElement("div");
481
575
  dom.className = "cm-tooltip-cursor";
482
576
  dom.textContent = `${argName}`;
483
577
  return { dom };
package/package.json CHANGED
@@ -4,7 +4,9 @@
4
4
  "scripts": {
5
5
  "pretest": "npm run-script prepare",
6
6
  "test": "cm-runtests",
7
- "prepare": "cm-buildhelper src/index.ts"
7
+ "prepare": "cm-buildhelper src/index.ts",
8
+ "lint-fix": "eslint . --fix --ext .ts,.js",
9
+ "lint": "eslint . --ext .ts,.js"
8
10
  },
9
11
  "type": "module",
10
12
  "main": "dist/index.cjs",
@@ -28,7 +30,10 @@
28
30
  "@codemirror/buildhelper": "^1.0.0",
29
31
  "@types/mocha": "^10.0.10",
30
32
  "@types/node": "^22.10.5",
31
- "tsdoc-markdown": "^1.1.1"
33
+ "tsdoc-markdown": "^1.1.1",
34
+ "eslint": "^8.48.0",
35
+ "@typescript-eslint/parser": "^6.5.0",
36
+ "@typescript-eslint/eslint-plugin": "^6.5.0"
32
37
  },
33
38
  "license": "MIT",
34
39
  "repository": {
@@ -39,5 +44,5 @@
39
44
  "access": "public",
40
45
  "registry": "https://registry.npmjs.org/"
41
46
  },
42
- "version": "1.2.0"
47
+ "version": "1.4.0"
43
48
  }