@valtzu/codemirror-lang-el 0.6.2 → 0.8.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/dist/index.d.cts CHANGED
@@ -14,10 +14,16 @@ interface ELIdentifier {
14
14
  */
15
15
  interface ELFunction {
16
16
  name: string;
17
- args: string[]; // maybe these could be ELIdentifier[] ?
17
+ args: ELParameter[];
18
18
  info?: string;
19
19
  returnType?: string[];
20
20
  }
21
+ interface ELParameter {
22
+ name: string;
23
+ type?: string[];
24
+ info?: string;
25
+ optional?: boolean;
26
+ }
21
27
  interface ELType {
22
28
  identifiers?: ELIdentifier[];
23
29
  functions?: ELFunction[];
package/dist/index.d.ts CHANGED
@@ -14,10 +14,16 @@ interface ELIdentifier {
14
14
  */
15
15
  interface ELFunction {
16
16
  name: string;
17
- args: string[]; // maybe these could be ELIdentifier[] ?
17
+ args: ELParameter[];
18
18
  info?: string;
19
19
  returnType?: string[];
20
20
  }
21
+ interface ELParameter {
22
+ name: string;
23
+ type?: string[];
24
+ info?: string;
25
+ optional?: boolean;
26
+ }
21
27
  interface ELType {
22
28
  identifiers?: ELIdentifier[];
23
29
  functions?: ELFunction[];
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { LRParser } from '@lezer/lr';
1
+ import { LRParser, LocalTokenGroup } from '@lezer/lr';
2
2
  import { syntaxTree, LRLanguage, indentNodeProp, delimitedIndent, foldNodeProp, LanguageSupport } from '@codemirror/language';
3
3
  import { styleTags, tags } from '@lezer/highlight';
4
4
  import { linter } from '@codemirror/lint';
@@ -6,25 +6,42 @@ 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
10
+ const t = {
11
+ deserialize: (str) => str,
12
+ };
13
+
9
14
  // This file was generated by lezer-generator. You probably shouldn't edit it.
10
- const spec_word = {__proto__:null,true:80, false:82, null:84, starts:94, with:96, ends:98, contains:100, matches:102, not:104, in:106, and:108, or:110};
11
- const spec_Operator = {__proto__:null,"?":93, "!":113, "+":115, "-":117};
15
+ const spec_word = {__proto__:null,true:92, TRUE:94, false:96, FALSE:98, null:100, NULL:102, starts:112, with:114, ends:116, contains:118, matches:120, not:122, in:124, and:126, or:128, xor:130};
16
+ const spec_Operator = {__proto__:null,"?":111, "!":133, "~":135, "+":137, "-":139};
12
17
  const parser = LRParser.deserialize({
13
18
  version: 14,
14
- states: "(pOVQPOOQ!QQPOOO!{QPO'#CdO$]QPO'#CmO$dQPO'#CnOVQPO'#CtOVQPO'#CvOOQO'#C|'#C|O$kQPO'#CcO$pQPO'#CcOOQO'#Ck'#CkOOQO'#Cl'#ClOOQO'#Cu'#CuO$uQPO,58xOVQPO,58|OVQPO,59^OVQPO,59[O$zQPO'#CsOOQO'#Cs'#CsO%PQPO'#CsO%}QPO,59XOOQO,59X,59XO&UQPO'#DSO&cQPO,59YO&hQPO,59`O'OQPO,59bO'VQPO'#CeOOQO,58},58}O'VQPO'#ChO'^QPO'#CaOOQO1G.d1G.dOOQO1G.l1G.lO(nQPO1G.hO(uQPO1G.xO)]QPO1G.vOOQO,59_,59_OVQPO1G.sOVQPO'#CwO)dQPO,59nOOQO1G.t1G.tOOQO1G.|1G.|O)oQPO,59PO)tQPO,59SOOQO7+$S7+$SOVQPO7+$bO)yQPO7+$_O*TQPO,59cOOQO-E6u-E6uOOQO1G.k1G.kOOQO1G.n1G.nO*bQPO<<G|OVQPO'#CxO*xQPO<<GyOOQO<<Gy<<GyO+QQPO,59dOOQO-E6v-E6vOOQOAN=eAN=eOVQPO1G/OO+XQPO7+$j",
15
- stateData: "+k~OoOS~O]VO^VOqQOsSOuUOxYOyYOzZO|RO!U[O!Y[O!Z[O![[O~OR]OS]Oe_Os^O!O`O!PaO!RaO!SbO!TbO!UcO!VbO!WbO!XbO~ORcXScXecXmcXscXuWX!OcX!PcX!RcX!ScX!TcX!UcX!VcX!WcX!XcX}cXrcXwcXtcX{cX~O{eO~PVOrvP~PVOujO~OulO~OqmO~O!QsO~O!VsO]gX^gXqgXsgXugXxgXygXzgX|gX!UgX!YgX!ZgX![gX~O}tO~P!QOwuOrvXtvX~P!QOrwO~Omha}harhawhatha{ha~P!QOtxO~P!QOtvP~PVORTXSTXeTXmTXsTXuZX!OTX!PTX!RTX!STX!TTX!UTX!VTX!WTX!XTX}TXrTXwTXtTX{TX~Or{O~P!QOmfi}firfiwfitfi{fi~P!QO}|O~P!QOwuOrvatva~Ot!QO~Ot!RO~Ow!TO{!VO~P!QOrkawkatka~P!QOmdy}dyrdywdytdy{dy~P!QOw!TO{!YO~O}!ZO~P!QOwlq{lq~P!QO^SeR]qR~",
16
- goto: "%]wPPxPP!Zxx!^!o!r#T#WPPxxxxxxPx#Zx#jx#{$RPPP$XPPPPP%SoVORSTU^_`jltu|!T!ZRn]oWORSTU^_`jltu|!T!ZRkWoXORSTU^_`jltu|!T!ZRo]RkXk_Pdfhipqr}!O!S!W![oTORSTU^_`jltu|!T!ZQvfR!PvQ!U}R!X!UQPOQdRUfSjlQhTQiUQp^Qq_Qr`Q}tQ!OuQ!S|Q!W!TR![!ZQgSQyjRzl",
17
- nodeNames: "⚠ Expression ObjectAccess MemberOf NullSafeMemberOf Property ArrayAccess Call Function Arguments ObjectAccess Method Arguments Number String Boolean Null Object Array Variable TernaryExpression Operator BinaryExpression OperatorKeyword UnaryExpression UnaryOperator Application",
18
- maxTerm: 58,
19
- skippedNodes: [0],
20
- repeatNodeCount: 2,
21
- tokenData: ",l~RqXY#YYZ#Y]^#Ypq#Yqr#krs$Quv#{vw%zwx&Sxy'wyz'|z{(R{|#{|}(Z}!O#{!O!P(`!P!Q#{!Q![(h![!]*}!^!_#s!_!`+S!`!a#s!a!b+Y!c!}+m!}#O,O#P#Q,T#Q#R#{#R#S+m#T#o+m#o#p,Y#p#q,_#q#r,g#r#s#{~#_So~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~'|Ou~~(ROt~~(WPe~z{#{~(`Ow~~(ePR~!O!P#{~(mT]~!O!P(|!Q![(h!g!h)|#R#S*w#X#Y)|~)PSO!O)]!P;'S)];'S;=`*q<%lO)]~)bR]~!Q![)k!g!h)|#X#Y)|~)pS]~!Q![)k!g!h)|#R#S*k#X#Y)|~*PR{|*Y}!O*Y!Q![*`~*]P!Q![*`~*eQ]~!Q![*`#R#S*Y~*nP!Q![)k~*tP;=`<%l)]~*zP!Q![(h~+SO}~~+VP!_!`#s~+_Re~!O!P+h![!]#{!a!b#{~+mOS~~+rSq~!Q![+m!c!}+m#R#S+m#T#o+m~,TOs~~,YOr~~,_O|~~,dPe~#p#q#{~,lO{~",
22
- tokenizers: [0],
23
- topRules: {"Expression":[0,1]},
24
- specialized: [{term: 33, get: (value) => spec_word[value] || -1},{term: 21, get: (value) => spec_Operator[value] || -1}],
25
- tokenPrec: 479
19
+ states: ")rOYQPOOP!aOPOOQ!fQPOOO#dQPO'#CeO$wQPO'#CnO%OQPO'#CoOYQPO'#CuOYQPO'#CwOOQO'#DS'#DSO%VQPO'#CdO%[QPO'#CdOOQO'#Cl'#ClOOQO'#Cm'#CmOOQO'#Cv'#CvP%aOQO'#C]POOO)C>r)C>rO%lQPO,58yOYQPO,58}OYQPO,59_OYQPO,59]O%qQPO'#CtOOQO'#Ct'#CtO%vQPO'#CtO'QQPO,59YOOQO,59Y,59YO'XQPO'#DYO'fQPO,59ZO'kQPO,59aO(RQPO,59cO(YQPO'#CfOOQO,59O,59OO(YQPO'#CiPOOO'#Cx'#CxP(aOQO,58wPOOO,58w,58wO(lQPO'#CbOOQO1G.e1G.eOOQO1G.m1G.mO*PQPO1G.iO*WQPO1G.yO*nQPO1G.wOOQO,59`,59`OYQPO1G.tOYQPO'#CyO*uQPO,59tOOQO1G.u1G.uOOQO1G.}1G.}O+QQPO,59QO+VQPO,59TPOOO-E6v-E6vPOOO1G.c1G.cOOQO7+$T7+$TOYQPO7+$cO+[QPO7+$`O+fQPO,59eOOQO-E6w-E6wOOQO1G.l1G.lOOQO1G.o1G.oO+sQPO<<G}OYQPO'#CzO,ZQPO<<GzOOQO<<Gz<<GzO,cQPO,59fOOQO-E6x-E6xOOQOAN=fAN=fOYQPO1G/QO,jQPO7+$l",
20
+ stateData: ",|~OqOSrPQ~O^WO_WOwROyTO{VO!OZO!PZO!QZO!RZO!S[O!T[O!VSO!_]O!d]O!e]O!f]O!g]O~Or^O~OS`OT`OfbOyaO!XcO!YdO![dO!]eO!^eO!_fO!`eO!aeO!beO!ceO~OSdXTdXfdXodXydX{XX!XdX!YdX![dX!]dX!^dX!_dX!`dX!adX!bdX!cdX!WdXxdX}dXzdX!UdX~O!UhO~PYOx|P~PYO{mO~O{oO~OspOtpOurO~OwsO~O!ZyO~O!`yO^hX_hXwhXyhX{hX!OhX!PhX!QhX!RhX!ShX!ThX!VhX!_hX!dhX!ehX!fhX!ghX~O!WzO~P!fO}{Ox|Xz|X~P!fOx}O~Ooia!Wiaxia}iazia!Uia~P!fOz!OO~P!fOz|P~PYOspOtpOu!SO~OSUXTUXfUXoUXyUX{[X!XUX!YUX![UX!]UX!^UX!_UX!`UX!aUX!bUX!cUX!WUXxUX}UXzUX!UUX~Ox!TO~P!fOogi!Wgixgi}gizgi!Ugi~P!fO!W!UO~P!fO}{Ox|az|a~Oz!YO~Oz!ZO~O}!]O!U!_O~P!fOxma}mazma~P!fOoey!Weyxey}eyzey!Uey~P!fO}!]O!U!bO~O!W!cO~P!fO}nq!Unq~P!fO_TfS^wS~",
21
+ goto: "%l}P!OP!RPP!d!R!R!g!x!{#^#aPP!R!R!R!R!R!RP!R#d!R#s!R$U$[$bPPPPPPP$hPPPPP%cR_PoWOSTUVabcmoz{!U!]!cRt`oXOSTUVabcmoz{!U!]!cRnXoYOSTUVabcmoz{!U!]!cRu`RnYkbQgiklvwx!V!W![!`!doUOSTUVabcmoz{!U!]!cQq^R!RqQ|iR!X|Q!^!VR!a!^QQOQgSUiTmoQkUQlVQvaQwbQxcQ!VzQ!W{Q![!UQ!`!]R!d!cQjTQ!PmR!Qo",
22
+ nodeNames: "⚠ BlockComment Expression ObjectAccess MemberOf NullSafeMemberOf Property ArrayAccess Call Function Arguments ObjectAccess Method Arguments Number String Boolean Null Object Array Variable TernaryExpression Operator BinaryExpression OperatorKeyword UnaryExpression UnaryOperator Application",
23
+ maxTerm: 69,
24
+ nodeProps: [
25
+ [t, 14,"number",15,"string",16,"bool",17,"null",18,"object",19,"array"]
26
+ ],
27
+ skippedNodes: [0,1,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#{~#_Sq~XY#YYZ#Y]^#Ypq#Y~#pPf~!_!`#s~#xPf~!_!`#{~$QOf~~$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~&PPf~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~~(WPf~z{#{~(`O}~~(ePS~!O!P#{~(mPf~z{(p~(uOr~~(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~~+fQf~!^!_#{!_!`#{~+oP!_!`#s~+wQf~!_!`#{!`!a#{~,SRf~!O!P,]![!]#{!a!b#{~,bOT~~,gSw~!Q![,b!c!},b#R#S,b#T#o,b~,xOy~~,}Ox~~-SO!V~~-XPf~#p#q#{~-aO!U~",
30
+ tokenizers: [1, new LocalTokenGroup("j~RQYZXz{^~^Ot~~aP!P!Qd~iOu~~", 25, 35)],
31
+ topRules: {"Expression":[0,2]},
32
+ specialized: [{term: 39, get: (value) => spec_word[value] || -1},{term: 22, get: (value) => spec_Operator[value] || -1}],
33
+ tokenPrec: 542
26
34
  });
27
35
 
36
+ var ELScalar;
37
+ (function (ELScalar) {
38
+ ELScalar["Bool"] = "bool";
39
+ ELScalar["Number"] = "number";
40
+ ELScalar["String"] = "string";
41
+ ELScalar["Null"] = "null";
42
+ ELScalar["Any"] = "any";
43
+ })(ELScalar || (ELScalar = {}));
44
+
28
45
  const createInfoElement = (html) => {
29
46
  const dom = document.createElement("div");
30
47
  dom.innerHTML = html;
@@ -61,12 +78,16 @@ const resolveIdentifier = (nodeName, identifier, config) => {
61
78
  }
62
79
  };
63
80
  function resolveTypes(state, node, config, matchExact) {
64
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
81
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
65
82
  let types = new Set();
66
83
  if (!node) {
67
84
  return types;
68
85
  }
69
- if (node.name === 'Call' && node.firstChild && node.lastChild) {
86
+ let type;
87
+ if (typeof (type = node.type.prop(t)) !== "undefined") {
88
+ types.add(type);
89
+ }
90
+ else if (node.name === 'Call' && node.firstChild && node.lastChild) {
70
91
  resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
71
92
  }
72
93
  else if (node.name === 'Variable') {
@@ -102,17 +123,32 @@ function resolveTypes(state, node, config, matchExact) {
102
123
  resolveTypes(state, node.firstChild.nextSibling, config).forEach(x => types.add(x));
103
124
  resolveTypes(state, node.firstChild.nextSibling.nextSibling, config).forEach(x => types.add(x));
104
125
  }
105
- else if (node.name === 'BinaryExpression' && ((_k = (_j = node.firstChild) === null || _j === void 0 ? void 0 : _j.nextSibling) === null || _k === void 0 ? void 0 : _k.name) == 'Operator' && node.lastChild) {
126
+ else if (node.name === 'BinaryExpression' && ((_j = node.firstChild) === null || _j === void 0 ? void 0 : _j.nextSibling) && ((_l = (_k = node.firstChild) === null || _k === void 0 ? void 0 : _k.nextSibling) === null || _l === void 0 ? void 0 : _l.nextSibling)) {
106
127
  const operator = state.sliceDoc(node.firstChild.nextSibling.from, node.firstChild.nextSibling.to);
107
- if (operator == '?:' || operator == '??') {
108
- resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
109
- }
110
128
  if (operator == '?:' || operator == '??' || operator == '?') {
111
- resolveTypes(state, node.lastChild, config).forEach(x => types.add(x));
129
+ if (operator == '?:' || operator == '??') {
130
+ resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
131
+ }
132
+ resolveTypes(state, node.firstChild.nextSibling.nextSibling, config).forEach(x => types.add(x));
133
+ }
134
+ else if (['||', '&&', '==', '!=', '===', '!==', '>=', '<=', '>', '<'].includes(operator) || keywords.find(x => x.name == operator)) {
135
+ types.add(ELScalar.Bool);
136
+ }
137
+ else if (['**', '|', '^', '&', '<<', '>>', '+', '-', '*', '/', '%'].includes(operator)) {
138
+ types.add(ELScalar.Number);
139
+ }
140
+ }
141
+ else if (node.name === 'UnaryExpression' && node.firstChild) {
142
+ const operator = state.sliceDoc(node.firstChild.from, node.firstChild.to);
143
+ if (['not', '!'].includes(operator)) {
144
+ types.add(ELScalar.Bool);
145
+ }
146
+ else if (['+', '-'].includes(operator)) {
147
+ types.add(ELScalar.Number);
112
148
  }
113
149
  }
114
150
  if (types.size === 0) {
115
- types.add('any');
151
+ types.add(ELScalar.Any);
116
152
  }
117
153
  return types;
118
154
  }
@@ -129,6 +165,7 @@ const keywords = [
129
165
  { name: 'not' },
130
166
  { name: 'or' },
131
167
  { name: 'and' },
168
+ { name: 'xor' },
132
169
  ];
133
170
 
134
171
  /**
@@ -161,13 +198,20 @@ const expressionLanguageLinterSource = (state) => {
161
198
  if (!args) {
162
199
  return;
163
200
  }
164
- let i = 0;
165
- let n = node.node.firstChild;
166
- while (n) {
167
- if (++i > args.length) {
201
+ for (let n = node.node.firstChild, i = 0; n != null; n = n.nextSibling) {
202
+ if (n.name === 'BlockComment') {
203
+ continue;
204
+ }
205
+ if (i > args.length - 1) {
168
206
  diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `Unexpected argument` });
207
+ continue;
208
+ }
209
+ const typesUsed = Array.from(resolveTypes(state, n, config));
210
+ const typesExpected = args[i].type;
211
+ if (typesExpected && !typesExpected.includes(ELScalar.Any) && !typesUsed.some(x => typesExpected.includes(x))) {
212
+ diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>` });
169
213
  }
170
- n = n.nextSibling;
214
+ i++;
171
215
  }
172
216
  break;
173
217
  case 'Property':
@@ -191,20 +235,27 @@ const expressionLanguageLinterSource = (state) => {
191
235
  diagnostics.push({ from, to, severity: 'error', message: `Unexpected identifier "${identifier}"` });
192
236
  }
193
237
  });
238
+ diagnostics.forEach(d => {
239
+ d.renderMessage = () => {
240
+ const span = document.createElement('span');
241
+ span.innerHTML = d.message;
242
+ return span;
243
+ };
244
+ });
194
245
  return diagnostics;
195
246
  };
196
247
  const expressionLanguageLinter = linter(view => expressionLanguageLinterSource(view.state));
197
248
 
198
249
  const autocompleteFunction = (x) => {
199
- var _a, _b;
250
+ var _a, _b, _c;
200
251
  return ({
201
- label: `${x.name}(${((_a = x.args) === null || _a === void 0 ? void 0 : _a.join(',')) || ''})`,
252
+ label: `${x.name}(${((_b = (_a = x.args) === null || _a === void 0 ? void 0 : _a.map(x => x.name)) === null || _b === void 0 ? void 0 : _b.join(',')) || ''})`,
202
253
  apply: (view, completion, from, to) => {
203
254
  var _a;
204
255
  view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, `${x.name}()`, from, to)), { selection: { anchor: from + x.name.length + (((_a = x.args) === null || _a === void 0 ? void 0 : _a.length) > 0 ? 1 : 2) } }));
205
256
  },
206
- detail: (_b = x.returnType) === null || _b === void 0 ? void 0 : _b.join('|'),
207
- info: () => { var _a; return createInfoElement((_a = x.info) !== null && _a !== void 0 ? _a : ''); },
257
+ detail: (_c = x.returnType) === null || _c === void 0 ? void 0 : _c.join('|'),
258
+ info: x.info,
208
259
  type: "function",
209
260
  });
210
261
  };
@@ -213,7 +264,7 @@ const autocompleteIdentifier = (x) => {
213
264
  return ({
214
265
  label: x.name,
215
266
  apply: x.name,
216
- info: () => { var _a; return createInfoElement((_a = x.info) !== null && _a !== void 0 ? _a : ''); },
267
+ info: x.info,
217
268
  detail: x.detail || ((_a = x.type) === null || _a === void 0 ? void 0 : _a.join('|')),
218
269
  type: 'variable',
219
270
  });
@@ -223,7 +274,13 @@ function completeOperatorKeyword(state, config, tree, from, to, explicit) {
223
274
  return {
224
275
  from,
225
276
  to,
226
- options: (_a = keywords.map(({ name, info, detail }) => ({ label: name, apply: `${name} `, info, detail, type: "keyword" }))) !== null && _a !== void 0 ? _a : [],
277
+ options: (_a = keywords.map(({ name, info, detail }) => ({
278
+ label: name,
279
+ apply: `${name} `,
280
+ info: info,
281
+ detail,
282
+ type: "keyword"
283
+ }))) !== null && _a !== void 0 ? _a : [],
227
284
  validFor: (text) => { var _a; return (_a = keywords.some(({ name }) => explicit || name.includes(text))) !== null && _a !== void 0 ? _a : false; },
228
285
  };
229
286
  }
@@ -268,7 +325,7 @@ function expressionLanguageCompletion(context) {
268
325
  const config = getExpressionLanguageConfig(state);
269
326
  const isIdentifier = (node) => { var _a; return ['Variable', 'Function'].includes((_a = node === null || node === void 0 ? void 0 : node.name) !== null && _a !== void 0 ? _a : ''); };
270
327
  const isMember = (node) => { var _a; return ['Property', 'Method'].includes((_a = node === null || node === void 0 ? void 0 : node.name) !== null && _a !== void 0 ? _a : ''); };
271
- if (prevNode.name == 'String') {
328
+ if (prevNode.name == 'String' || prevNode.name == 'BlockComment') {
272
329
  return null;
273
330
  }
274
331
  if (((_a = prevNode.parent) === null || _a === void 0 ? void 0 : _a.name) == 'ObjectAccess' && ['ObjectAccess', 'ArrayAccess', 'Variable', 'Call', 'Application'].includes(((_b = prevNode.parent.firstChild) === null || _b === void 0 ? void 0 : _b.name) || '')) {
@@ -308,7 +365,7 @@ function getCursorTooltips(state) {
308
365
  return state.selection.ranges
309
366
  .filter(range => range.empty)
310
367
  .map(range => {
311
- var _a;
368
+ var _a, _b;
312
369
  const tree = syntaxTree(state);
313
370
  const node = tree.resolveInner(range.from, 0);
314
371
  const args = resolveArguments(node);
@@ -320,7 +377,7 @@ function getCursorTooltips(state) {
320
377
  return null;
321
378
  }
322
379
  const n = args.childAfter(range.from - 1);
323
- const argName = (_a = fn.args) === null || _a === void 0 ? void 0 : _a[n ? getNodeOrdinal(n) : 0];
380
+ const argName = (_b = (_a = fn.args) === null || _a === void 0 ? void 0 : _a[n ? getNodeOrdinal(n) : 0]) === null || _b === void 0 ? void 0 : _b.name;
324
381
  if (n && n.from !== range.from || !argName) {
325
382
  return null;
326
383
  }
@@ -395,17 +452,34 @@ const keywordTooltip = hoverTooltip((view, pos, side) => {
395
452
  create: () => ({ dom: createInfoElement(info) }),
396
453
  };
397
454
  });
398
- const cursorTooltipBaseTheme = EditorView.baseTheme({
455
+
456
+ const baseTheme = EditorView.baseTheme({
399
457
  ".cm-tooltip.cm-tooltip-cursor": {
400
458
  boxShadow: 'rgba(0, 0, 0, .15) 0 1px 2px',
401
459
  border: "1px solid rgba(127, 127, 127, .2)",
402
- padding: "2px 7px",
460
+ fontSize: ".85rem",
461
+ padding: ".4rem .5rem",
403
462
  borderRadius: "4px",
404
463
  "& .cm-tooltip-arrow:before": {},
405
464
  "& .cm-tooltip-arrow:after": {
406
465
  borderTopColor: "transparent"
407
- }
408
- }
466
+ },
467
+ },
468
+ ".cm-tooltip.cm-completionInfo, .cm-diagnostic": {
469
+ boxShadow: "rgba(0, 0, 0, .15) 0 2px 5px",
470
+ fontSize: ".85rem",
471
+ padding: ".8rem !important", // Couldn't figure out other means to override https://github.com/codemirror/autocomplete/blob/6.18.1/src/theme.ts#L65
472
+ },
473
+ ".cm-completionDetail": {
474
+ float: "right",
475
+ opacity: 0.5,
476
+ fontStyle: "inherit !important",
477
+ },
478
+ "code": {
479
+ fontSize: ".8em",
480
+ fontStyle: "monospace",
481
+ backgroundColor: "rgba(127, 127, 127, .3)",
482
+ },
409
483
  });
410
484
 
411
485
  const ELLanguage = LRLanguage.define({
@@ -439,6 +513,7 @@ const ELLanguage = LRLanguage.define({
439
513
  OperatorKeyword: tags.operatorKeyword,
440
514
  UnaryOperator: tags.operator,
441
515
  Operator: tags.operator,
516
+ BlockComment: tags.comment,
442
517
  })
443
518
  ]
444
519
  }),
@@ -453,7 +528,7 @@ function expressionlanguage(config = {}, extensions = []) {
453
528
  expressionLanguageLinter,
454
529
  keywordTooltip,
455
530
  cursorTooltipField,
456
- cursorTooltipBaseTheme,
531
+ baseTheme,
457
532
  ...extensions,
458
533
  ]);
459
534
  }
package/dist/linter.cjs CHANGED
@@ -5,6 +5,20 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var lint = require('@codemirror/lint');
6
6
  var language = require('@codemirror/language');
7
7
 
8
+ var ELScalar;
9
+ (function (ELScalar) {
10
+ ELScalar["Bool"] = "bool";
11
+ ELScalar["Number"] = "number";
12
+ ELScalar["String"] = "string";
13
+ ELScalar["Null"] = "null";
14
+ ELScalar["Any"] = "any";
15
+ })(ELScalar || (ELScalar = {}));
16
+
17
+ // @ts-ignore
18
+ const t = {
19
+ deserialize: (str) => str,
20
+ };
21
+
8
22
  function resolveFunctionDefinition(node, state, config) {
9
23
  var _a;
10
24
  if (!node) {
@@ -35,12 +49,16 @@ const resolveIdentifier = (nodeName, identifier, config) => {
35
49
  }
36
50
  };
37
51
  function resolveTypes(state, node, config, matchExact) {
38
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
52
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
39
53
  let types = new Set();
40
54
  if (!node) {
41
55
  return types;
42
56
  }
43
- if (node.name === 'Call' && node.firstChild && node.lastChild) {
57
+ let type;
58
+ if (typeof (type = node.type.prop(t)) !== "undefined") {
59
+ types.add(type);
60
+ }
61
+ else if (node.name === 'Call' && node.firstChild && node.lastChild) {
44
62
  resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
45
63
  }
46
64
  else if (node.name === 'Variable') {
@@ -76,23 +94,50 @@ function resolveTypes(state, node, config, matchExact) {
76
94
  resolveTypes(state, node.firstChild.nextSibling, config).forEach(x => types.add(x));
77
95
  resolveTypes(state, node.firstChild.nextSibling.nextSibling, config).forEach(x => types.add(x));
78
96
  }
79
- else if (node.name === 'BinaryExpression' && ((_k = (_j = node.firstChild) === null || _j === void 0 ? void 0 : _j.nextSibling) === null || _k === void 0 ? void 0 : _k.name) == 'Operator' && node.lastChild) {
97
+ else if (node.name === 'BinaryExpression' && ((_j = node.firstChild) === null || _j === void 0 ? void 0 : _j.nextSibling) && ((_l = (_k = node.firstChild) === null || _k === void 0 ? void 0 : _k.nextSibling) === null || _l === void 0 ? void 0 : _l.nextSibling)) {
80
98
  const operator = state.sliceDoc(node.firstChild.nextSibling.from, node.firstChild.nextSibling.to);
81
- if (operator == '?:' || operator == '??') {
82
- resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
83
- }
84
99
  if (operator == '?:' || operator == '??' || operator == '?') {
85
- resolveTypes(state, node.lastChild, config).forEach(x => types.add(x));
100
+ if (operator == '?:' || operator == '??') {
101
+ resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
102
+ }
103
+ resolveTypes(state, node.firstChild.nextSibling.nextSibling, config).forEach(x => types.add(x));
104
+ }
105
+ else if (['||', '&&', '==', '!=', '===', '!==', '>=', '<=', '>', '<'].includes(operator) || keywords.find(x => x.name == operator)) {
106
+ types.add(ELScalar.Bool);
107
+ }
108
+ else if (['**', '|', '^', '&', '<<', '>>', '+', '-', '*', '/', '%'].includes(operator)) {
109
+ types.add(ELScalar.Number);
110
+ }
111
+ }
112
+ else if (node.name === 'UnaryExpression' && node.firstChild) {
113
+ const operator = state.sliceDoc(node.firstChild.from, node.firstChild.to);
114
+ if (['not', '!'].includes(operator)) {
115
+ types.add(ELScalar.Bool);
116
+ }
117
+ else if (['+', '-'].includes(operator)) {
118
+ types.add(ELScalar.Number);
86
119
  }
87
120
  }
88
121
  if (types.size === 0) {
89
- types.add('any');
122
+ types.add(ELScalar.Any);
90
123
  }
91
124
  return types;
92
125
  }
93
126
  function getExpressionLanguageConfig(state) {
94
127
  return state.languageDataAt('expressionLanguageConfig', 0)[0];
95
128
  }
129
+ const keywords = [
130
+ { name: 'starts with', info: 'Check if a string starts with a specific string' },
131
+ { name: 'ends with', info: 'Check if a string ends with a specific string' },
132
+ { name: 'contains', info: 'Check if a string is not included in another string' },
133
+ { name: 'matches', info: 'Check if a string matches a regex pattern' },
134
+ { name: 'not in', info: 'Check if a value is not included in an array' },
135
+ { name: 'in', info: 'Check if a value is included in an array' },
136
+ { name: 'not' },
137
+ { name: 'or' },
138
+ { name: 'and' },
139
+ { name: 'xor' },
140
+ ];
96
141
 
97
142
  /**
98
143
  * @internal
@@ -124,13 +169,20 @@ const expressionLanguageLinterSource = (state) => {
124
169
  if (!args) {
125
170
  return;
126
171
  }
127
- let i = 0;
128
- let n = node.node.firstChild;
129
- while (n) {
130
- if (++i > args.length) {
172
+ for (let n = node.node.firstChild, i = 0; n != null; n = n.nextSibling) {
173
+ if (n.name === 'BlockComment') {
174
+ continue;
175
+ }
176
+ if (i > args.length - 1) {
131
177
  diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `Unexpected argument` });
178
+ continue;
179
+ }
180
+ const typesUsed = Array.from(resolveTypes(state, n, config));
181
+ const typesExpected = args[i].type;
182
+ if (typesExpected && !typesExpected.includes(ELScalar.Any) && !typesUsed.some(x => typesExpected.includes(x))) {
183
+ diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>` });
132
184
  }
133
- n = n.nextSibling;
185
+ i++;
134
186
  }
135
187
  break;
136
188
  case 'Property':
@@ -154,6 +206,13 @@ const expressionLanguageLinterSource = (state) => {
154
206
  diagnostics.push({ from, to, severity: 'error', message: `Unexpected identifier "${identifier}"` });
155
207
  }
156
208
  });
209
+ diagnostics.forEach(d => {
210
+ d.renderMessage = () => {
211
+ const span = document.createElement('span');
212
+ span.innerHTML = d.message;
213
+ return span;
214
+ };
215
+ });
157
216
  return diagnostics;
158
217
  };
159
218
  const expressionLanguageLinter = lint.linter(view => expressionLanguageLinterSource(view.state));
package/dist/linter.js CHANGED
@@ -1,6 +1,20 @@
1
1
  import { linter } from '@codemirror/lint';
2
2
  import { syntaxTree } from '@codemirror/language';
3
3
 
4
+ var ELScalar;
5
+ (function (ELScalar) {
6
+ ELScalar["Bool"] = "bool";
7
+ ELScalar["Number"] = "number";
8
+ ELScalar["String"] = "string";
9
+ ELScalar["Null"] = "null";
10
+ ELScalar["Any"] = "any";
11
+ })(ELScalar || (ELScalar = {}));
12
+
13
+ // @ts-ignore
14
+ const t = {
15
+ deserialize: (str) => str,
16
+ };
17
+
4
18
  function resolveFunctionDefinition(node, state, config) {
5
19
  var _a;
6
20
  if (!node) {
@@ -31,12 +45,16 @@ const resolveIdentifier = (nodeName, identifier, config) => {
31
45
  }
32
46
  };
33
47
  function resolveTypes(state, node, config, matchExact) {
34
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
48
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
35
49
  let types = new Set();
36
50
  if (!node) {
37
51
  return types;
38
52
  }
39
- if (node.name === 'Call' && node.firstChild && node.lastChild) {
53
+ let type;
54
+ if (typeof (type = node.type.prop(t)) !== "undefined") {
55
+ types.add(type);
56
+ }
57
+ else if (node.name === 'Call' && node.firstChild && node.lastChild) {
40
58
  resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
41
59
  }
42
60
  else if (node.name === 'Variable') {
@@ -72,23 +90,50 @@ function resolveTypes(state, node, config, matchExact) {
72
90
  resolveTypes(state, node.firstChild.nextSibling, config).forEach(x => types.add(x));
73
91
  resolveTypes(state, node.firstChild.nextSibling.nextSibling, config).forEach(x => types.add(x));
74
92
  }
75
- else if (node.name === 'BinaryExpression' && ((_k = (_j = node.firstChild) === null || _j === void 0 ? void 0 : _j.nextSibling) === null || _k === void 0 ? void 0 : _k.name) == 'Operator' && node.lastChild) {
93
+ else if (node.name === 'BinaryExpression' && ((_j = node.firstChild) === null || _j === void 0 ? void 0 : _j.nextSibling) && ((_l = (_k = node.firstChild) === null || _k === void 0 ? void 0 : _k.nextSibling) === null || _l === void 0 ? void 0 : _l.nextSibling)) {
76
94
  const operator = state.sliceDoc(node.firstChild.nextSibling.from, node.firstChild.nextSibling.to);
77
- if (operator == '?:' || operator == '??') {
78
- resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
79
- }
80
95
  if (operator == '?:' || operator == '??' || operator == '?') {
81
- resolveTypes(state, node.lastChild, config).forEach(x => types.add(x));
96
+ if (operator == '?:' || operator == '??') {
97
+ resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
98
+ }
99
+ resolveTypes(state, node.firstChild.nextSibling.nextSibling, config).forEach(x => types.add(x));
100
+ }
101
+ else if (['||', '&&', '==', '!=', '===', '!==', '>=', '<=', '>', '<'].includes(operator) || keywords.find(x => x.name == operator)) {
102
+ types.add(ELScalar.Bool);
103
+ }
104
+ else if (['**', '|', '^', '&', '<<', '>>', '+', '-', '*', '/', '%'].includes(operator)) {
105
+ types.add(ELScalar.Number);
106
+ }
107
+ }
108
+ else if (node.name === 'UnaryExpression' && node.firstChild) {
109
+ const operator = state.sliceDoc(node.firstChild.from, node.firstChild.to);
110
+ if (['not', '!'].includes(operator)) {
111
+ types.add(ELScalar.Bool);
112
+ }
113
+ else if (['+', '-'].includes(operator)) {
114
+ types.add(ELScalar.Number);
82
115
  }
83
116
  }
84
117
  if (types.size === 0) {
85
- types.add('any');
118
+ types.add(ELScalar.Any);
86
119
  }
87
120
  return types;
88
121
  }
89
122
  function getExpressionLanguageConfig(state) {
90
123
  return state.languageDataAt('expressionLanguageConfig', 0)[0];
91
124
  }
125
+ const keywords = [
126
+ { name: 'starts with', info: 'Check if a string starts with a specific string' },
127
+ { name: 'ends with', info: 'Check if a string ends with a specific string' },
128
+ { name: 'contains', info: 'Check if a string is not included in another string' },
129
+ { name: 'matches', info: 'Check if a string matches a regex pattern' },
130
+ { name: 'not in', info: 'Check if a value is not included in an array' },
131
+ { name: 'in', info: 'Check if a value is included in an array' },
132
+ { name: 'not' },
133
+ { name: 'or' },
134
+ { name: 'and' },
135
+ { name: 'xor' },
136
+ ];
92
137
 
93
138
  /**
94
139
  * @internal
@@ -120,13 +165,20 @@ const expressionLanguageLinterSource = (state) => {
120
165
  if (!args) {
121
166
  return;
122
167
  }
123
- let i = 0;
124
- let n = node.node.firstChild;
125
- while (n) {
126
- if (++i > args.length) {
168
+ for (let n = node.node.firstChild, i = 0; n != null; n = n.nextSibling) {
169
+ if (n.name === 'BlockComment') {
170
+ continue;
171
+ }
172
+ if (i > args.length - 1) {
127
173
  diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `Unexpected argument` });
174
+ continue;
175
+ }
176
+ const typesUsed = Array.from(resolveTypes(state, n, config));
177
+ const typesExpected = args[i].type;
178
+ if (typesExpected && !typesExpected.includes(ELScalar.Any) && !typesUsed.some(x => typesExpected.includes(x))) {
179
+ diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>` });
128
180
  }
129
- n = n.nextSibling;
181
+ i++;
130
182
  }
131
183
  break;
132
184
  case 'Property':
@@ -150,6 +202,13 @@ const expressionLanguageLinterSource = (state) => {
150
202
  diagnostics.push({ from, to, severity: 'error', message: `Unexpected identifier "${identifier}"` });
151
203
  }
152
204
  });
205
+ diagnostics.forEach(d => {
206
+ d.renderMessage = () => {
207
+ const span = document.createElement('span');
208
+ span.innerHTML = d.message;
209
+ return span;
210
+ };
211
+ });
153
212
  return diagnostics;
154
213
  };
155
214
  const expressionLanguageLinter = linter(view => expressionLanguageLinterSource(view.state));