@valtzu/codemirror-lang-el 0.1.13 → 0.2.2

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,18 +1,44 @@
1
1
  # Symfony Expression Language support for CodeMirror
2
2
 
3
- ```javascript
3
+ ### Example
4
+
5
+ ![image](https://github.com/valtzu/codemirror-lang-el/assets/652734/928ea2e8-6061-46c9-8ac1-16f95fb5661c)
6
+
7
+
8
+ ```html
9
+ <div id="editor"></div>
10
+ <script type="importmap">
11
+ {
12
+ "imports": {
13
+ "codemirror": "https://esm.sh/codemirror@6.0.1",
14
+ "@codemirror/autocomplete": "https://esm.sh/@codemirror/autocomplete@6.9.0",
15
+ "@codemirror/view": "https://esm.sh/@codemirror/view@6.17.1",
16
+ "@valtzu/codemirror-lang-el": "https://esm.sh/@valtzu/codemirror-lang-el@0.2.1"
17
+ }
18
+ }
19
+ </script>
20
+ <script type="module">
4
21
  import {EditorView, basicSetup} from "codemirror";
5
22
  import { expressionlanguage } from "@valtzu/codemirror-lang-el";
6
23
  import {acceptCompletion} from "@codemirror/autocomplete";
7
- import {keymap} from "@codemirror/view"
24
+ import {keymap} from "@codemirror/view";
8
25
 
9
26
  let editor = new EditorView({
10
27
  extensions: [
11
- keymap.of([{key: "Tab", run: acceptCompletion}]),
12
28
  basicSetup,
13
- expressionlanguage({ identifiers: ['foo', 'bar'], functions: {'smh': [], smash_my_head: ['object']} })
14
- ],
15
- parent: document.body,
29
+ keymap.of([{key: "Tab", run: acceptCompletion}]),
30
+ expressionlanguage({
31
+ identifiers: [
32
+ { name: 'foo' },
33
+ { name: 'bar' }
34
+ ],
35
+ functions: [
36
+ {name: 'smh'},
37
+ {name: 'smash_my_head', args: ['object']},
38
+ ],
39
+ })
40
+ ],
41
+ parent: document.getElementById('editor'),
16
42
  });
17
-
43
+ </script>
18
44
  ```
package/dist/index.cjs CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var lr = require('@lezer/lr');
6
6
  var language = require('@codemirror/language');
7
7
  var highlight = require('@lezer/highlight');
8
+ var lint = require('@codemirror/lint');
8
9
 
9
10
  // This file was generated by lezer-generator. You probably shouldn't edit it.
10
11
  const parser = lr.LRParser.deserialize({
@@ -20,13 +21,33 @@ const parser = lr.LRParser.deserialize({
20
21
  ],
21
22
  skippedNodes: [0],
22
23
  repeatNodeCount: 2,
23
- tokenData: "Ba~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&owx&wxy(`yz(ez{(j{|$z|}(r}!O$z!O!P(w!P!Q$z!Q![*Y![!]+i!^!_$r!_!`+p!`!a$r!a!b+v!c!},]!}#O,p#P#Q,u#Q#R$z#R#S,]#T#U,z#U#V,]#V#W.e#W#X,]#X#Y2X#Y#Z4{#Z#],]#]#^7g#^#a,]#a#b8Q#b#c:p#c#d>Z#d#g,]#g#h>t#h#i@y#i#o,]#o#pA}#p#qBS#q#rB[#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SVOr%Prs%is#O%P#O#P%p#P;'S%P;'S;=`&i<%lO%PR%pO]QSPR%sRO;'S%P;'S;=`%|;=`O%PR&PWOr%Prs%is#O%P#O#P%p#P;'S%P;'S;=`&i;=`<%l%P<%lO%PR&lP;=`<%l%P~&tPV~vw$zR&zVOw&wwx%ix#O&w#O#P'a#P;'S&w;'S;=`(Y<%lO&wR'dRO;'S&w;'S;=`'m;=`O&wR'pWOw&wwx%ix#O&w#O#P'a#P;'S&w;'S;=`(Y;=`<%l&w<%lO&wR(]P;=`<%l&w~(eO`~~(jO_~~(oPV~z{$z~(wOk~~(|QXP!O!P$z!Q![)S~)XSQ~!Q![)S!g!h)e#R#S*S#X#Y)e~)hR{|)q}!O)q!Q![)w~)tP!Q![)w~)|QQ~!Q![)w#R#S)q~*VP!Q![)S~*_TQ~!O!P*n!Q![*Y!g!h)e#R#S+c#X#Y)e~*qSO!O*}!P;'S*};'S;=`+]<%lO*}~+SRQ~!Q![)S!g!h)e#X#Y)e~+`P;=`<%l*}~+fP!Q![*YR+pOjQXP~+sP!_!`$r~+{QXP!O!P,R!a!b,W~,WOY~~,]OZ~R,dS]QRP!Q![,]!c!},]#R#S,]#T#o,]~,uOm~~,zOl~R-RU]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c-e#c#o,]R-lU]QRP!Q![,]!c!},]#R#S,]#T#W,]#W#X.O#X#o,]R.XS]QWPRP!Q![,]!c!},]#R#S,]#T#o,]R.lU]QRP!Q![,]!c!},]#R#S,]#T#c,]#c#d/O#d#o,]R/VU]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c/i#c#o,]R/pU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i0S#i#o,]R0ZT]QRP!Q![,]!c!},]#R#S,]#T#U0j#U#o,]R0qU]QRP!Q![,]!c!},]#R#S,]#T#],]#]#^1T#^#o,]R1[U]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c1n#c#o,]R1uU]QRP!Q![,]!c!},]#R#S,]#T#g,]#g#h.O#h#o,]R2`U]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c2r#c#o,]R2yU]QRP!Q![,]!c!},]#R#S,]#T#W,]#W#X3]#X#o,]R3dU]QRP!Q![,]!c!},]#R#S,]#T#g,]#g#h3v#h#o,]R3}T]QRPpq4^!Q![,]!c!},]#R#S,]#T#o,]P4aP#k#l4dP4gP#]#^4jP4mP#h#i4pP4sP#[#]4vP4{OWPR5ST]QRP!Q![,]!c!},]#R#S,]#T#U5c#U#o,]R5jU]QRP!Q![,]!c!},]#R#S,]#T#`,]#`#a5|#a#o,]R6TU]QRP!Q![,]!c!},]#R#S,]#T#g,]#g#h6g#h#o,]R6nU]QRP!Q![,]!c!},]#R#S,]#T#X,]#X#Y7Q#Y#o,]R7ZS]QTPRP!Q![,]!c!},]#R#S,]#T#o,]R7nU]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c.O#c#o,]R8XT]QRP!Q![,]!c!},]#R#S,]#T#U8h#U#o,]R8oU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i9R#i#o,]R9YU]QRP!Q![,]!c!},]#R#S,]#T#V,]#V#W9l#W#o,]R9sU]QRP!Q![,]!c!},]#R#S,]#T#[,]#[#]:V#]#o,]R:^U]QRP!Q![,]!c!},]#R#S,]#T#X,]#X#Y1n#Y#o,]R:wW]QRP!Q![,]!c!},]#R#S,]#T#c,]#c#d;a#d#i,]#i#j<p#j#o,]R;hU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i;z#i#o,]R<TT]QWPRPpq<d!Q![,]!c!},]#R#S,]#T#o,]P<gP#]#^<jP<mP#b#c4vR<wU]QRP!Q![,]!c!},]#R#S,]#T#`,]#`#a=Z#a#o,]R=bU]QRP!Q![,]!c!},]#R#S,]#T#`,]#`#a=t#a#o,]R=}S]QUPRP!Q![,]!c!},]#R#S,]#T#o,]R>bU]QRP!Q![,]!c!},]#R#S,]#T#f,]#f#g.O#g#o,]R>{U]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i?_#i#o,]R?fT]QRP!Q![,]!c!},]#R#S,]#T#U?u#U#o,]R?|U]QRP!Q![,]!c!},]#R#S,]#T#f,]#f#g@`#g#o,]R@gU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i3]#i#o,]RAQU]QRP!Q![,]!c!},]#R#S,]#T#f,]#f#gAd#g#o,]RAkU]QRP!Q![,]!c!},]#R#S,]#T#i,]#i#j6g#j#o,]~BSOi~~BXPV~#p#q$z~BaOh~",
24
+ tokenData: "By~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&{wx'Txy(xyz(}z{)S{|$z|})[}!O$z!O!P)a!P!Q$z!Q![*r![!],R!^!_$r!_!`,Y!`!a$r!a!b,`!c!},u!}#O-Y#P#Q-_#Q#R$z#R#S,u#T#U-d#U#V,u#V#W.}#W#X,u#X#Y2q#Y#Z5e#Z#],u#]#^8P#^#a,u#a#b8j#b#c;Y#c#d>s#d#g,u#g#h?^#h#iAc#i#o,u#o#pBg#p#qBl#q#rBt#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SXOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u<%l~%P~O%P~~%oR%vO]QSPR%yRO;'S%P;'S;=`&S;=`O%PR&VYOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u;=`<%l%P<%l~%P~O%P~~%oR&xP;=`<%l%P~'QPV~vw$zR'WXOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%oR'vRO;'S'T;'S;=`(P;=`O'TR(SYOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%oR(uP;=`<%l'T~(}O`~~)SO_~~)XPV~z{$z~)aOk~~)fQXP!O!P$z!Q![)l~)qSQ~!Q![)l!g!h)}#R#S*l#X#Y)}~*QR{|*Z}!O*Z!Q![*a~*^P!Q![*a~*fQQ~!Q![*a#R#S*Z~*oP!Q![)l~*wTQ~!O!P+W!Q![*r!g!h)}#R#S+{#X#Y)}~+ZSO!O+g!P;'S+g;'S;=`+u<%lO+g~+lRQ~!Q![)l!g!h)}#X#Y)}~+xP;=`<%l+g~,OP!Q![*rR,YOjQXP~,]P!_!`$r~,eQXP!O!P,k!a!b,p~,pOY~~,uOZ~R,|S]QRP!Q![,u!c!},u#R#S,u#T#o,u~-_Om~~-dOl~R-kU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c-}#c#o,uR.UU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X.h#X#o,uR.qS]QWPRP!Q![,u!c!},u#R#S,u#T#o,uR/UU]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d/h#d#o,uR/oU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c0R#c#o,uR0YU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i0l#i#o,uR0sT]QRP!Q![,u!c!},u#R#S,u#T#U1S#U#o,uR1ZU]QRP!Q![,u!c!},u#R#S,u#T#],u#]#^1m#^#o,uR1tU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c2W#c#o,uR2_U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h.h#h#o,uR2xU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c3[#c#o,uR3cU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X3u#X#o,uR3|U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h4`#h#o,uR4gT]QRPpq4v!Q![,u!c!},u#R#S,u#T#o,uP4yP#k#l4|P5PP#]#^5SP5VP#h#i5YP5]P#[#]5`P5eOWPR5lT]QRP!Q![,u!c!},u#R#S,u#T#U5{#U#o,uR6SU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a6f#a#o,uR6mU]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h7P#h#o,uR7WU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y7j#Y#o,uR7sS]QTPRP!Q![,u!c!},u#R#S,u#T#o,uR8WU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c.h#c#o,uR8qT]QRP!Q![,u!c!},u#R#S,u#T#U9Q#U#o,uR9XU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i9k#i#o,uR9rU]QRP!Q![,u!c!},u#R#S,u#T#V,u#V#W:U#W#o,uR:]U]QRP!Q![,u!c!},u#R#S,u#T#[,u#[#]:o#]#o,uR:vU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y2W#Y#o,uR;aW]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d;y#d#i,u#i#j=Y#j#o,uR<QU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i<d#i#o,uR<mT]QWPRPpq<|!Q![,u!c!},u#R#S,u#T#o,uP=PP#]#^=SP=VP#b#c5`R=aU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a=s#a#o,uR=zU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a>^#a#o,uR>gS]QUPRP!Q![,u!c!},u#R#S,u#T#o,uR>zU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g.h#g#o,uR?eU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i?w#i#o,uR@OT]QRP!Q![,u!c!},u#R#S,u#T#U@_#U#o,uR@fU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g@x#g#o,uRAPU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i3u#i#o,uRAjU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#gA|#g#o,uRBTU]QRP!Q![,u!c!},u#R#S,u#T#i,u#i#j7P#j#o,u~BlOi~~BqPV~#p#q$z~ByOh~",
24
25
  tokenizers: [0, 1],
25
26
  topRules: {"Program":[0,1]},
26
27
  tokenPrec: 118
27
28
  });
28
29
 
29
30
  const identifier = /^[a-zA-Z_]+[a-zA-Z_0-9]*$/;
31
+ const expressionLanguageLinter = (config) => lint.linter(view => {
32
+ let diagnostics = [];
33
+ language.syntaxTree(view.state).cursor().iterate(node => {
34
+ var _a, _b;
35
+ if (node.name == "Identifier") {
36
+ const identifier = view.state.sliceDoc(node.from, node.to);
37
+ const isFunction = (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier);
38
+ const isVariable = (_b = config.identifiers) === null || _b === void 0 ? void 0 : _b.filter(variable => variable.name === identifier);
39
+ if (!isFunction && !isVariable) {
40
+ diagnostics.push({
41
+ from: node.from,
42
+ to: node.to,
43
+ severity: 'warning',
44
+ message: `Identifier "${identifier}" not found`,
45
+ });
46
+ }
47
+ }
48
+ });
49
+ return diagnostics;
50
+ });
30
51
  const ELLanguage = language.LRLanguage.define({
31
52
  parser: parser.configure({
32
53
  props: [
@@ -66,16 +87,16 @@ function completeOperatorKeyword(state, config, tree, from, to) {
66
87
  };
67
88
  }
68
89
  function completeIdentifier(state, config, tree, from, to) {
69
- var _a, _b, _c, _d, _e;
90
+ var _a, _b, _c, _d, _e, _f;
70
91
  const text = state.sliceDoc(from, to);
71
- const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(i => i.startsWith(text))) !== null && _b !== void 0 ? _b : [];
72
- const functions = Object.entries((_c = config.functions) !== null && _c !== void 0 ? _c : {}).filter(([fn]) => fn.startsWith(text));
92
+ const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
93
+ const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
73
94
  return {
74
95
  from,
75
96
  to,
76
97
  options: [
77
- ...((_d = identifiers.map(identifier => ({ label: identifier, type: "property" }))) !== null && _d !== void 0 ? _d : []),
78
- ...((_e = functions.map(([fn, args]) => ({ label: `${fn}(${args.join(',')})`, apply: `${fn}(${args.length == 0 ? ')' : ''}`, type: "function" }))) !== null && _e !== void 0 ? _e : []),
98
+ ...((_e = identifiers.map(({ name, info, detail }) => ({ label: name, info, detail, type: 'variable' }))) !== null && _e !== void 0 ? _e : []),
99
+ ...((_f = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _f !== void 0 ? _f : []),
79
100
  ],
80
101
  validFor: identifier,
81
102
  };
@@ -83,14 +104,10 @@ function completeIdentifier(state, config, tree, from, to) {
83
104
  function expressionLanguageCompletionFor(config, context) {
84
105
  let { state, pos } = context;
85
106
  let tree = language.syntaxTree(state).resolveInner(pos, -1);
86
- // let around = tree.resolve(pos);
87
- // for (let scan = pos, before; around == tree && (before = tree.childBefore(scan));) {
88
- // let last = before.lastChild;
89
- // if (!last || !last.type.isError || last.from < last.to) break;
90
- // around = tree = before;
91
- // scan = last.from;
92
- // }
93
- if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing'].includes(tree.prevSibling.name)) {
107
+ if (tree.name == 'String') {
108
+ return null;
109
+ }
110
+ if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(tree.prevSibling.name)) {
94
111
  return completeOperatorKeyword(state, config, tree, tree.from, pos);
95
112
  }
96
113
  if (tree.name == "Identifier") {
@@ -104,7 +121,11 @@ function expressionLanguageCompletionSourceWith(config) {
104
121
  return (context) => expressionLanguageCompletionFor(config, context);
105
122
  }
106
123
  function expressionlanguage(config = {}, extensions = []) {
107
- return new language.LanguageSupport(ELLanguage, [ELLanguage.data.of({ autocomplete: expressionLanguageCompletionSourceWith(config) }), ...extensions]);
124
+ return new language.LanguageSupport(ELLanguage, [
125
+ ELLanguage.data.of({ autocomplete: expressionLanguageCompletionSourceWith(config) }),
126
+ expressionLanguageLinter(config),
127
+ ...extensions,
128
+ ]);
108
129
  }
109
130
 
110
131
  exports.ELLanguage = ELLanguage;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,15 @@
1
1
  import { LRLanguage, LanguageSupport } from "@codemirror/language";
2
2
  interface ExpressionLanguageConfig {
3
- identifiers?: readonly string[];
4
- functions?: Record<string, readonly string[]>;
3
+ identifiers?: readonly {
4
+ name: string;
5
+ detail?: string;
6
+ info?: string;
7
+ }[];
8
+ functions?: readonly {
9
+ name: string;
10
+ args: string[];
11
+ info?: string;
12
+ }[];
5
13
  operatorKeywords?: readonly string[];
6
14
  }
7
15
  declare const ELLanguage: LRLanguage;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,15 @@
1
1
  import { LRLanguage, LanguageSupport } from "@codemirror/language";
2
2
  interface ExpressionLanguageConfig {
3
- identifiers?: readonly string[];
4
- functions?: Record<string, readonly string[]>;
3
+ identifiers?: readonly {
4
+ name: string;
5
+ detail?: string;
6
+ info?: string;
7
+ }[];
8
+ functions?: readonly {
9
+ name: string;
10
+ args: string[];
11
+ info?: string;
12
+ }[];
5
13
  operatorKeywords?: readonly string[];
6
14
  }
7
15
  declare const ELLanguage: LRLanguage;
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { LRParser } from '@lezer/lr';
2
2
  import { LRLanguage, indentNodeProp, delimitedIndent, foldNodeProp, foldInside, LanguageSupport, syntaxTree } from '@codemirror/language';
3
3
  import { styleTags, tags } from '@lezer/highlight';
4
+ import { linter } from '@codemirror/lint';
4
5
 
5
6
  // This file was generated by lezer-generator. You probably shouldn't edit it.
6
7
  const parser = LRParser.deserialize({
@@ -16,13 +17,33 @@ const parser = LRParser.deserialize({
16
17
  ],
17
18
  skippedNodes: [0],
18
19
  repeatNodeCount: 2,
19
- tokenData: "Ba~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&owx&wxy(`yz(ez{(j{|$z|}(r}!O$z!O!P(w!P!Q$z!Q![*Y![!]+i!^!_$r!_!`+p!`!a$r!a!b+v!c!},]!}#O,p#P#Q,u#Q#R$z#R#S,]#T#U,z#U#V,]#V#W.e#W#X,]#X#Y2X#Y#Z4{#Z#],]#]#^7g#^#a,]#a#b8Q#b#c:p#c#d>Z#d#g,]#g#h>t#h#i@y#i#o,]#o#pA}#p#qBS#q#rB[#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SVOr%Prs%is#O%P#O#P%p#P;'S%P;'S;=`&i<%lO%PR%pO]QSPR%sRO;'S%P;'S;=`%|;=`O%PR&PWOr%Prs%is#O%P#O#P%p#P;'S%P;'S;=`&i;=`<%l%P<%lO%PR&lP;=`<%l%P~&tPV~vw$zR&zVOw&wwx%ix#O&w#O#P'a#P;'S&w;'S;=`(Y<%lO&wR'dRO;'S&w;'S;=`'m;=`O&wR'pWOw&wwx%ix#O&w#O#P'a#P;'S&w;'S;=`(Y;=`<%l&w<%lO&wR(]P;=`<%l&w~(eO`~~(jO_~~(oPV~z{$z~(wOk~~(|QXP!O!P$z!Q![)S~)XSQ~!Q![)S!g!h)e#R#S*S#X#Y)e~)hR{|)q}!O)q!Q![)w~)tP!Q![)w~)|QQ~!Q![)w#R#S)q~*VP!Q![)S~*_TQ~!O!P*n!Q![*Y!g!h)e#R#S+c#X#Y)e~*qSO!O*}!P;'S*};'S;=`+]<%lO*}~+SRQ~!Q![)S!g!h)e#X#Y)e~+`P;=`<%l*}~+fP!Q![*YR+pOjQXP~+sP!_!`$r~+{QXP!O!P,R!a!b,W~,WOY~~,]OZ~R,dS]QRP!Q![,]!c!},]#R#S,]#T#o,]~,uOm~~,zOl~R-RU]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c-e#c#o,]R-lU]QRP!Q![,]!c!},]#R#S,]#T#W,]#W#X.O#X#o,]R.XS]QWPRP!Q![,]!c!},]#R#S,]#T#o,]R.lU]QRP!Q![,]!c!},]#R#S,]#T#c,]#c#d/O#d#o,]R/VU]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c/i#c#o,]R/pU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i0S#i#o,]R0ZT]QRP!Q![,]!c!},]#R#S,]#T#U0j#U#o,]R0qU]QRP!Q![,]!c!},]#R#S,]#T#],]#]#^1T#^#o,]R1[U]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c1n#c#o,]R1uU]QRP!Q![,]!c!},]#R#S,]#T#g,]#g#h.O#h#o,]R2`U]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c2r#c#o,]R2yU]QRP!Q![,]!c!},]#R#S,]#T#W,]#W#X3]#X#o,]R3dU]QRP!Q![,]!c!},]#R#S,]#T#g,]#g#h3v#h#o,]R3}T]QRPpq4^!Q![,]!c!},]#R#S,]#T#o,]P4aP#k#l4dP4gP#]#^4jP4mP#h#i4pP4sP#[#]4vP4{OWPR5ST]QRP!Q![,]!c!},]#R#S,]#T#U5c#U#o,]R5jU]QRP!Q![,]!c!},]#R#S,]#T#`,]#`#a5|#a#o,]R6TU]QRP!Q![,]!c!},]#R#S,]#T#g,]#g#h6g#h#o,]R6nU]QRP!Q![,]!c!},]#R#S,]#T#X,]#X#Y7Q#Y#o,]R7ZS]QTPRP!Q![,]!c!},]#R#S,]#T#o,]R7nU]QRP!Q![,]!c!},]#R#S,]#T#b,]#b#c.O#c#o,]R8XT]QRP!Q![,]!c!},]#R#S,]#T#U8h#U#o,]R8oU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i9R#i#o,]R9YU]QRP!Q![,]!c!},]#R#S,]#T#V,]#V#W9l#W#o,]R9sU]QRP!Q![,]!c!},]#R#S,]#T#[,]#[#]:V#]#o,]R:^U]QRP!Q![,]!c!},]#R#S,]#T#X,]#X#Y1n#Y#o,]R:wW]QRP!Q![,]!c!},]#R#S,]#T#c,]#c#d;a#d#i,]#i#j<p#j#o,]R;hU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i;z#i#o,]R<TT]QWPRPpq<d!Q![,]!c!},]#R#S,]#T#o,]P<gP#]#^<jP<mP#b#c4vR<wU]QRP!Q![,]!c!},]#R#S,]#T#`,]#`#a=Z#a#o,]R=bU]QRP!Q![,]!c!},]#R#S,]#T#`,]#`#a=t#a#o,]R=}S]QUPRP!Q![,]!c!},]#R#S,]#T#o,]R>bU]QRP!Q![,]!c!},]#R#S,]#T#f,]#f#g.O#g#o,]R>{U]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i?_#i#o,]R?fT]QRP!Q![,]!c!},]#R#S,]#T#U?u#U#o,]R?|U]QRP!Q![,]!c!},]#R#S,]#T#f,]#f#g@`#g#o,]R@gU]QRP!Q![,]!c!},]#R#S,]#T#h,]#h#i3]#i#o,]RAQU]QRP!Q![,]!c!},]#R#S,]#T#f,]#f#gAd#g#o,]RAkU]QRP!Q![,]!c!},]#R#S,]#T#i,]#i#j6g#j#o,]~BSOi~~BXPV~#p#q$z~BaOh~",
20
+ tokenData: "By~R!QXY$XYZ$X]^$Xpq$Xqr$jrs%Puv$zvw&{wx'Txy(xyz(}z{)S{|$z|})[}!O$z!O!P)a!P!Q$z!Q![*r![!],R!^!_$r!_!`,Y!`!a$r!a!b,`!c!},u!}#O-Y#P#Q-_#Q#R$z#R#S,u#T#U-d#U#V,u#V#W.}#W#X,u#X#Y2q#Y#Z5e#Z#],u#]#^8P#^#a,u#a#b8j#b#c;Y#c#d>s#d#g,u#g#h?^#h#iAc#i#o,u#o#pBg#p#qBl#q#rBt#r#s$z~$^Sf~XY$XYZ$X]^$Xpq$X~$oPV~!_!`$r~$wPV~!_!`$z~%POV~R%SXOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u<%l~%P~O%P~~%oR%vO]QSPR%yRO;'S%P;'S;=`&S;=`O%PR&VYOr%Prs%os#O%P#O#P%v#P;'S%P;'S;=`&u;=`<%l%P<%l~%P~O%P~~%oR&xP;=`<%l%P~'QPV~vw$zR'WXOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r<%l~'T~O'T~~%oR'vRO;'S'T;'S;=`(P;=`O'TR(SYOw'Twx%ox#O'T#O#P's#P;'S'T;'S;=`(r;=`<%l'T<%l~'T~O'T~~%oR(uP;=`<%l'T~(}O`~~)SO_~~)XPV~z{$z~)aOk~~)fQXP!O!P$z!Q![)l~)qSQ~!Q![)l!g!h)}#R#S*l#X#Y)}~*QR{|*Z}!O*Z!Q![*a~*^P!Q![*a~*fQQ~!Q![*a#R#S*Z~*oP!Q![)l~*wTQ~!O!P+W!Q![*r!g!h)}#R#S+{#X#Y)}~+ZSO!O+g!P;'S+g;'S;=`+u<%lO+g~+lRQ~!Q![)l!g!h)}#X#Y)}~+xP;=`<%l+g~,OP!Q![*rR,YOjQXP~,]P!_!`$r~,eQXP!O!P,k!a!b,p~,pOY~~,uOZ~R,|S]QRP!Q![,u!c!},u#R#S,u#T#o,u~-_Om~~-dOl~R-kU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c-}#c#o,uR.UU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X.h#X#o,uR.qS]QWPRP!Q![,u!c!},u#R#S,u#T#o,uR/UU]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d/h#d#o,uR/oU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c0R#c#o,uR0YU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i0l#i#o,uR0sT]QRP!Q![,u!c!},u#R#S,u#T#U1S#U#o,uR1ZU]QRP!Q![,u!c!},u#R#S,u#T#],u#]#^1m#^#o,uR1tU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c2W#c#o,uR2_U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h.h#h#o,uR2xU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c3[#c#o,uR3cU]QRP!Q![,u!c!},u#R#S,u#T#W,u#W#X3u#X#o,uR3|U]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h4`#h#o,uR4gT]QRPpq4v!Q![,u!c!},u#R#S,u#T#o,uP4yP#k#l4|P5PP#]#^5SP5VP#h#i5YP5]P#[#]5`P5eOWPR5lT]QRP!Q![,u!c!},u#R#S,u#T#U5{#U#o,uR6SU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a6f#a#o,uR6mU]QRP!Q![,u!c!},u#R#S,u#T#g,u#g#h7P#h#o,uR7WU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y7j#Y#o,uR7sS]QTPRP!Q![,u!c!},u#R#S,u#T#o,uR8WU]QRP!Q![,u!c!},u#R#S,u#T#b,u#b#c.h#c#o,uR8qT]QRP!Q![,u!c!},u#R#S,u#T#U9Q#U#o,uR9XU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i9k#i#o,uR9rU]QRP!Q![,u!c!},u#R#S,u#T#V,u#V#W:U#W#o,uR:]U]QRP!Q![,u!c!},u#R#S,u#T#[,u#[#]:o#]#o,uR:vU]QRP!Q![,u!c!},u#R#S,u#T#X,u#X#Y2W#Y#o,uR;aW]QRP!Q![,u!c!},u#R#S,u#T#c,u#c#d;y#d#i,u#i#j=Y#j#o,uR<QU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i<d#i#o,uR<mT]QWPRPpq<|!Q![,u!c!},u#R#S,u#T#o,uP=PP#]#^=SP=VP#b#c5`R=aU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a=s#a#o,uR=zU]QRP!Q![,u!c!},u#R#S,u#T#`,u#`#a>^#a#o,uR>gS]QUPRP!Q![,u!c!},u#R#S,u#T#o,uR>zU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g.h#g#o,uR?eU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i?w#i#o,uR@OT]QRP!Q![,u!c!},u#R#S,u#T#U@_#U#o,uR@fU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#g@x#g#o,uRAPU]QRP!Q![,u!c!},u#R#S,u#T#h,u#h#i3u#i#o,uRAjU]QRP!Q![,u!c!},u#R#S,u#T#f,u#f#gA|#g#o,uRBTU]QRP!Q![,u!c!},u#R#S,u#T#i,u#i#j7P#j#o,u~BlOi~~BqPV~#p#q$z~ByOh~",
20
21
  tokenizers: [0, 1],
21
22
  topRules: {"Program":[0,1]},
22
23
  tokenPrec: 118
23
24
  });
24
25
 
25
26
  const identifier = /^[a-zA-Z_]+[a-zA-Z_0-9]*$/;
27
+ const expressionLanguageLinter = (config) => linter(view => {
28
+ let diagnostics = [];
29
+ syntaxTree(view.state).cursor().iterate(node => {
30
+ var _a, _b;
31
+ if (node.name == "Identifier") {
32
+ const identifier = view.state.sliceDoc(node.from, node.to);
33
+ const isFunction = (_a = config.functions) === null || _a === void 0 ? void 0 : _a.find(fn => fn.name === identifier);
34
+ const isVariable = (_b = config.identifiers) === null || _b === void 0 ? void 0 : _b.filter(variable => variable.name === identifier);
35
+ if (!isFunction && !isVariable) {
36
+ diagnostics.push({
37
+ from: node.from,
38
+ to: node.to,
39
+ severity: 'warning',
40
+ message: `Identifier "${identifier}" not found`,
41
+ });
42
+ }
43
+ }
44
+ });
45
+ return diagnostics;
46
+ });
26
47
  const ELLanguage = LRLanguage.define({
27
48
  parser: parser.configure({
28
49
  props: [
@@ -62,16 +83,16 @@ function completeOperatorKeyword(state, config, tree, from, to) {
62
83
  };
63
84
  }
64
85
  function completeIdentifier(state, config, tree, from, to) {
65
- var _a, _b, _c, _d, _e;
86
+ var _a, _b, _c, _d, _e, _f;
66
87
  const text = state.sliceDoc(from, to);
67
- const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(i => i.startsWith(text))) !== null && _b !== void 0 ? _b : [];
68
- const functions = Object.entries((_c = config.functions) !== null && _c !== void 0 ? _c : {}).filter(([fn]) => fn.startsWith(text));
88
+ const identifiers = (_b = (_a = config.identifiers) === null || _a === void 0 ? void 0 : _a.filter(({ name }) => name.startsWith(text))) !== null && _b !== void 0 ? _b : [];
89
+ const functions = (_d = (_c = config.functions) === null || _c === void 0 ? void 0 : _c.filter(({ name }) => name.startsWith(text))) !== null && _d !== void 0 ? _d : [];
69
90
  return {
70
91
  from,
71
92
  to,
72
93
  options: [
73
- ...((_d = identifiers.map(identifier => ({ label: identifier, type: "property" }))) !== null && _d !== void 0 ? _d : []),
74
- ...((_e = functions.map(([fn, args]) => ({ label: `${fn}(${args.join(',')})`, apply: `${fn}(${args.length == 0 ? ')' : ''}`, type: "function" }))) !== null && _e !== void 0 ? _e : []),
94
+ ...((_e = identifiers.map(({ name, info, detail }) => ({ label: name, info, detail, type: 'variable' }))) !== null && _e !== void 0 ? _e : []),
95
+ ...((_f = functions.map(({ name, args = [], info }) => ({ label: name, detail: `(${args.join(',')})`, apply: `${name}(${args.length == 0 ? ')' : ''}`, info, type: "function" }))) !== null && _f !== void 0 ? _f : []),
75
96
  ],
76
97
  validFor: identifier,
77
98
  };
@@ -79,14 +100,10 @@ function completeIdentifier(state, config, tree, from, to) {
79
100
  function expressionLanguageCompletionFor(config, context) {
80
101
  let { state, pos } = context;
81
102
  let tree = syntaxTree(state).resolveInner(pos, -1);
82
- // let around = tree.resolve(pos);
83
- // for (let scan = pos, before; around == tree && (before = tree.childBefore(scan));) {
84
- // let last = before.lastChild;
85
- // if (!last || !last.type.isError || last.from < last.to) break;
86
- // around = tree = before;
87
- // scan = last.from;
88
- // }
89
- if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing'].includes(tree.prevSibling.name)) {
103
+ if (tree.name == 'String') {
104
+ return null;
105
+ }
106
+ if (tree.prevSibling && !['Operator', 'OperatorKeyword', 'Punctuation', 'NullSafe', 'NullCoalescing', 'OpeningBracket'].includes(tree.prevSibling.name)) {
90
107
  return completeOperatorKeyword(state, config, tree, tree.from, pos);
91
108
  }
92
109
  if (tree.name == "Identifier") {
@@ -100,7 +117,11 @@ function expressionLanguageCompletionSourceWith(config) {
100
117
  return (context) => expressionLanguageCompletionFor(config, context);
101
118
  }
102
119
  function expressionlanguage(config = {}, extensions = []) {
103
- return new LanguageSupport(ELLanguage, [ELLanguage.data.of({ autocomplete: expressionLanguageCompletionSourceWith(config) }), ...extensions]);
120
+ return new LanguageSupport(ELLanguage, [
121
+ ELLanguage.data.of({ autocomplete: expressionLanguageCompletionSourceWith(config) }),
122
+ expressionLanguageLinter(config),
123
+ ...extensions,
124
+ ]);
104
125
  }
105
126
 
106
127
  export { ELLanguage, expressionlanguage };
package/package.json CHANGED
@@ -17,10 +17,12 @@
17
17
  "dependencies": {
18
18
  "@codemirror/autocomplete": "^6.9.0",
19
19
  "@codemirror/language": "^6.0.0",
20
+ "@codemirror/lint": "^6.4.2",
20
21
  "@lezer/highlight": "^1.0.0",
21
22
  "@lezer/lr": "^1.0.0"
22
23
  },
23
24
  "devDependencies": {
25
+ "@codemirror/state": "^6.2.1",
24
26
  "@lezer/generator": "^1.0.0",
25
27
  "@types/mocha": "^10.0.1",
26
28
  "ist": "^1.1.7",
@@ -39,5 +41,5 @@
39
41
  "access": "public",
40
42
  "registry": "https://registry.npmjs.org/"
41
43
  },
42
- "version": "0.1.13"
44
+ "version": "0.2.2"
43
45
  }