@valtzu/codemirror-lang-el 0.5.3 → 0.6.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/.github/workflows/publish.yml +1 -1
- package/.github/workflows/test.yml +16 -0
- package/README.md +47 -7
- package/dist/complete.cjs +177 -0
- package/dist/complete.d.cts +3 -0
- package/dist/complete.d.ts +3 -0
- package/dist/complete.js +173 -0
- package/dist/index.cjs +274 -239
- package/dist/index.d.cts +16 -19
- package/dist/index.d.ts +16 -19
- package/dist/index.js +276 -239
- package/dist/linter.cjs +149 -0
- package/dist/linter.d.cts +8 -0
- package/dist/linter.d.ts +8 -0
- package/dist/linter.js +144 -0
- package/dist/tooltip.cjs +224 -0
- package/dist/tooltip.d.cts +6 -0
- package/dist/tooltip.d.ts +6 -0
- package/dist/tooltip.js +218 -0
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Run tests
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
types: [opened, synchronize, reopened]
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v3
|
|
12
|
+
- uses: actions/setup-node@v3
|
|
13
|
+
with:
|
|
14
|
+
node-version: '20.x'
|
|
15
|
+
- run: npm install
|
|
16
|
+
- run: npm run test
|
package/README.md
CHANGED
|
@@ -1,14 +1,50 @@
|
|
|
1
|
-
|
|
1
|
+
## Symfony Expression Language support for CodeMirror 6
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> :warning: **This is unstable**: Expect breaking changes until v1 is out.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
2. Hover tooltip
|
|
7
|
-
3. Linting
|
|
5
|
+
### Features
|
|
8
6
|
|
|
9
|
-
####
|
|
7
|
+
#### Linting
|
|
10
8
|
|
|
11
|
-

|
|
10
|
+
|
|
11
|
+
#### Autocompletion
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
#### Hover tooltip
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
#### Function argument hints
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### Installation
|
|
26
|
+
|
|
27
|
+
#### Symfony AssetMapper
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
bin/console importmap:require @valtzu/codemirror-lang-el
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### npm
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
npm install @valtzu/codemirror-lang-el
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Yarn
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
yarn add @valtzu/codemirror-lang-el
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
### Example
|
|
12
48
|
|
|
13
49
|
[Live demo](https://jsfiddle.net/9xhezam4/)
|
|
14
50
|
|
|
@@ -84,3 +120,7 @@
|
|
|
84
120
|
}
|
|
85
121
|
</style>
|
|
86
122
|
```
|
|
123
|
+
|
|
124
|
+
### Contributing
|
|
125
|
+
|
|
126
|
+
Contributions are accepted.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var autocomplete = require('@codemirror/autocomplete');
|
|
6
|
+
var language = require('@codemirror/language');
|
|
7
|
+
|
|
8
|
+
const createInfoElement = (html) => {
|
|
9
|
+
const dom = document.createElement("div");
|
|
10
|
+
dom.innerHTML = html;
|
|
11
|
+
dom.className = 'cm-diagnostic';
|
|
12
|
+
return dom;
|
|
13
|
+
};
|
|
14
|
+
const resolveCallable = (identifier, config) => { var _a; return (_a = config === null || config === void 0 ? void 0 : config.functions) === null || _a === void 0 ? void 0 : _a.find(x => x.name === identifier); };
|
|
15
|
+
const resolveIdentifier = (nodeName, identifier, config) => {
|
|
16
|
+
var _a;
|
|
17
|
+
switch (nodeName) {
|
|
18
|
+
case 'Method':
|
|
19
|
+
case 'Function':
|
|
20
|
+
return resolveCallable(identifier, config);
|
|
21
|
+
case 'Property':
|
|
22
|
+
case 'Variable':
|
|
23
|
+
return (_a = config === null || config === void 0 ? void 0 : config.identifiers) === null || _a === void 0 ? void 0 : _a.find(x => x.name === identifier);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
function resolveTypes(state, node, config, matchExact) {
|
|
27
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
28
|
+
let types = new Set();
|
|
29
|
+
if (!node) {
|
|
30
|
+
return types;
|
|
31
|
+
}
|
|
32
|
+
if (node.name === 'Call' && node.firstChild && node.lastChild) {
|
|
33
|
+
resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
|
|
34
|
+
}
|
|
35
|
+
else if (node.name === 'Variable') {
|
|
36
|
+
const varName = state.sliceDoc(node.from, node.to) || '';
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
(_b = (_a = resolveIdentifier(node.name, varName, config)) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.forEach((x) => types.add(x));
|
|
39
|
+
}
|
|
40
|
+
else if (node.name === 'Function') {
|
|
41
|
+
const varName = state.sliceDoc(node.from, node.to) || '';
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
(_d = (_c = resolveIdentifier(node.name, varName, config)) === null || _c === void 0 ? void 0 : _c.returnType) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
|
|
44
|
+
}
|
|
45
|
+
else if (node.name === 'ObjectAccess' && node.firstChild && ((_e = node.lastChild) === null || _e === void 0 ? void 0 : _e.name) === 'Property') {
|
|
46
|
+
const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
|
|
47
|
+
(_f = resolveTypes(state, node.firstChild, config)) === null || _f === void 0 ? void 0 : _f.forEach(baseType => {
|
|
48
|
+
var _a, _b, _c, _d;
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
(_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.name, 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));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else if (node.name === 'ObjectAccess' && node.firstChild && ((_g = node.lastChild) === null || _g === void 0 ? void 0 : _g.name) === 'Method') {
|
|
54
|
+
const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
|
|
55
|
+
(_h = resolveTypes(state, node.firstChild, config)) === null || _h === void 0 ? void 0 : _h.forEach(baseType => {
|
|
56
|
+
var _a, _b, _c, _d;
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
(_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.name, 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));
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else if (node.name === 'Application' && node.firstChild) {
|
|
62
|
+
resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
|
|
63
|
+
}
|
|
64
|
+
if (types.size === 0) {
|
|
65
|
+
types.add('any');
|
|
66
|
+
}
|
|
67
|
+
return types;
|
|
68
|
+
}
|
|
69
|
+
function getExpressionLanguageConfig(state) {
|
|
70
|
+
return state.languageDataAt('expressionLanguageConfig', 0)[0];
|
|
71
|
+
}
|
|
72
|
+
const keywords = [
|
|
73
|
+
{ name: 'starts with', info: 'Check if a string starts with a specific string' },
|
|
74
|
+
{ name: 'ends with', info: 'Check if a string ends with a specific string' },
|
|
75
|
+
{ name: 'contains', info: 'Check if a string is not included in another string' },
|
|
76
|
+
{ name: 'matches', info: 'Check if a string matches a regex pattern' },
|
|
77
|
+
{ name: 'not in', info: 'Check if a value is not included in an array' },
|
|
78
|
+
{ name: 'in', info: 'Check if a value is included in an array' },
|
|
79
|
+
{ name: 'not' },
|
|
80
|
+
{ name: 'or' },
|
|
81
|
+
{ name: 'and' },
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const autocompleteFunction = (x) => {
|
|
85
|
+
var _a, _b;
|
|
86
|
+
return ({
|
|
87
|
+
label: `${x.name}(${((_a = x.args) === null || _a === void 0 ? void 0 : _a.join(',')) || ''})`,
|
|
88
|
+
apply: (view, completion, from, to) => {
|
|
89
|
+
var _a;
|
|
90
|
+
view.dispatch(Object.assign(Object.assign({}, autocomplete.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) } }));
|
|
91
|
+
},
|
|
92
|
+
detail: (_b = x.returnType) === null || _b === void 0 ? void 0 : _b.join('|'),
|
|
93
|
+
info: () => { var _a; return createInfoElement((_a = x.info) !== null && _a !== void 0 ? _a : ''); },
|
|
94
|
+
type: "function",
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const autocompleteIdentifier = (x) => {
|
|
98
|
+
var _a;
|
|
99
|
+
return ({
|
|
100
|
+
label: x.name,
|
|
101
|
+
apply: x.name,
|
|
102
|
+
info: () => { var _a; return createInfoElement((_a = x.info) !== null && _a !== void 0 ? _a : ''); },
|
|
103
|
+
detail: x.detail || ((_a = x.type) === null || _a === void 0 ? void 0 : _a.join('|')),
|
|
104
|
+
type: 'variable',
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
function completeOperatorKeyword(state, config, tree, from, to, explicit) {
|
|
108
|
+
var _a;
|
|
109
|
+
return {
|
|
110
|
+
from,
|
|
111
|
+
to,
|
|
112
|
+
options: (_a = keywords.map(({ name, info, detail }) => ({ label: name, apply: `${name} `, info, detail, type: "keyword" }))) !== null && _a !== void 0 ? _a : [],
|
|
113
|
+
validFor: (text) => { var _a; return (_a = keywords.some(({ name }) => explicit || name.includes(text))) !== null && _a !== void 0 ? _a : false; },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function completeIdentifier(state, config, tree, from, to) {
|
|
117
|
+
var _a, _b;
|
|
118
|
+
const identifiers = (_a = config.identifiers) !== null && _a !== void 0 ? _a : []; //?.filter(({ name }) => explicit || name.toLowerCase().startsWith(text)) ?? [];
|
|
119
|
+
const functions = (_b = config.functions) !== null && _b !== void 0 ? _b : []; //?.filter(({ name }) => explicit || name.toLowerCase().startsWith(text)) ?? [];
|
|
120
|
+
return {
|
|
121
|
+
from,
|
|
122
|
+
to,
|
|
123
|
+
options: [...(identifiers.map(autocompleteIdentifier)), ...(functions.map(autocompleteFunction))],
|
|
124
|
+
validFor: /^[a-zA-Z_]+[a-zA-Z_0-9]*$/,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function completeMember(state, config, tree, from, to, explicit) {
|
|
128
|
+
var _a, _b, _c, _d;
|
|
129
|
+
if (((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.name) != 'ObjectAccess' || !tree.parent.firstChild) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const types = resolveTypes(state, tree.parent.firstChild.node, config);
|
|
133
|
+
if (!(types === null || types === void 0 ? void 0 : types.size)) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
let options = [];
|
|
137
|
+
for (const type of types) {
|
|
138
|
+
const typeDeclaration = (_b = config.types) === null || _b === void 0 ? void 0 : _b[type];
|
|
139
|
+
options.push(...(((_c = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.identifiers) === null || _c === void 0 ? void 0 : _c.map(autocompleteIdentifier)) || []), ...(((_d = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.functions) === null || _d === void 0 ? void 0 : _d.map(autocompleteFunction)) || []));
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
from,
|
|
143
|
+
to,
|
|
144
|
+
options,
|
|
145
|
+
validFor: /^[a-zA-Z_]+[a-zA-Z_0-9]*$/,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function expressionLanguageCompletion(context) {
|
|
149
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
150
|
+
const { state, pos, explicit } = context;
|
|
151
|
+
const tree = language.syntaxTree(state);
|
|
152
|
+
const lastChar = state.sliceDoc(pos - 1, pos);
|
|
153
|
+
const prevNode = tree.resolveInner(pos, lastChar === ')' ? 0 : -1);
|
|
154
|
+
const config = getExpressionLanguageConfig(state);
|
|
155
|
+
const isIdentifier = (node) => { var _a; return ['Variable', 'Function'].includes((_a = node === null || node === void 0 ? void 0 : node.name) !== null && _a !== void 0 ? _a : ''); };
|
|
156
|
+
const isMember = (node) => { var _a; return ['Property', 'Method'].includes((_a = node === null || node === void 0 ? void 0 : node.name) !== null && _a !== void 0 ? _a : ''); };
|
|
157
|
+
if (prevNode.name == 'String') {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
if (((_a = prevNode.parent) === null || _a === void 0 ? void 0 : _a.name) == 'ObjectAccess' && ['ObjectAccess', 'ArrayAccess', 'Variable', 'Call'].includes(((_b = prevNode.parent.firstChild) === null || _b === void 0 ? void 0 : _b.name) || '')) {
|
|
161
|
+
return completeMember(state, config, prevNode, isIdentifier(prevNode) || isMember(prevNode) ? prevNode.from : pos, pos);
|
|
162
|
+
}
|
|
163
|
+
if (['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression'].includes(prevNode.name) && prevNode.lastChild && !((_c = prevNode.lastChild) === null || _c === void 0 ? void 0 : _c.type.isError)
|
|
164
|
+
|| ['Arguments', 'Array'].includes(prevNode.name) && prevNode.lastChild && !((_d = prevNode.lastChild) === null || _d === void 0 ? void 0 : _d.type.isError)
|
|
165
|
+
|| ['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression'].includes((_f = (_e = prevNode.parent) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : '') && prevNode.type.isError
|
|
166
|
+
|| ['Variable', 'Function'].includes((_h = (_g = prevNode.parent) === null || _g === void 0 ? void 0 : _g.name) !== null && _h !== void 0 ? _h : '') && prevNode.type.isError) {
|
|
167
|
+
return completeOperatorKeyword(state, config, prevNode, !['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression', 'Arguments'].includes(prevNode.name) ? prevNode.from : pos, pos, explicit);
|
|
168
|
+
}
|
|
169
|
+
if (!/[0-9]/.test(lastChar) && !['OperatorKeyword'].includes((_j = prevNode.name) !== null && _j !== void 0 ? _j : '') && (['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression'].includes(prevNode.name) && ((_k = prevNode.lastChild) === null || _k === void 0 ? void 0 : _k.type.isError)
|
|
170
|
+
|| ['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression', 'Arguments'].includes((_m = (_l = prevNode.parent) === null || _l === void 0 ? void 0 : _l.name) !== null && _m !== void 0 ? _m : '') && !prevNode.type.isError
|
|
171
|
+
|| ['Arguments', 'Array'].includes((_o = prevNode.name) !== null && _o !== void 0 ? _o : ''))) {
|
|
172
|
+
return completeIdentifier(state, config, prevNode, isIdentifier(prevNode) ? prevNode.from : pos, pos);
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
exports.expressionLanguageCompletion = expressionLanguageCompletion;
|
package/dist/complete.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { insertCompletionText } from '@codemirror/autocomplete';
|
|
2
|
+
import { syntaxTree } from '@codemirror/language';
|
|
3
|
+
|
|
4
|
+
const createInfoElement = (html) => {
|
|
5
|
+
const dom = document.createElement("div");
|
|
6
|
+
dom.innerHTML = html;
|
|
7
|
+
dom.className = 'cm-diagnostic';
|
|
8
|
+
return dom;
|
|
9
|
+
};
|
|
10
|
+
const resolveCallable = (identifier, config) => { var _a; return (_a = config === null || config === void 0 ? void 0 : config.functions) === null || _a === void 0 ? void 0 : _a.find(x => x.name === identifier); };
|
|
11
|
+
const resolveIdentifier = (nodeName, identifier, config) => {
|
|
12
|
+
var _a;
|
|
13
|
+
switch (nodeName) {
|
|
14
|
+
case 'Method':
|
|
15
|
+
case 'Function':
|
|
16
|
+
return resolveCallable(identifier, config);
|
|
17
|
+
case 'Property':
|
|
18
|
+
case 'Variable':
|
|
19
|
+
return (_a = config === null || config === void 0 ? void 0 : config.identifiers) === null || _a === void 0 ? void 0 : _a.find(x => x.name === identifier);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function resolveTypes(state, node, config, matchExact) {
|
|
23
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
24
|
+
let types = new Set();
|
|
25
|
+
if (!node) {
|
|
26
|
+
return types;
|
|
27
|
+
}
|
|
28
|
+
if (node.name === 'Call' && node.firstChild && node.lastChild) {
|
|
29
|
+
resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
|
|
30
|
+
}
|
|
31
|
+
else if (node.name === 'Variable') {
|
|
32
|
+
const varName = state.sliceDoc(node.from, node.to) || '';
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
(_b = (_a = resolveIdentifier(node.name, varName, config)) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.forEach((x) => types.add(x));
|
|
35
|
+
}
|
|
36
|
+
else if (node.name === 'Function') {
|
|
37
|
+
const varName = state.sliceDoc(node.from, node.to) || '';
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
(_d = (_c = resolveIdentifier(node.name, varName, config)) === null || _c === void 0 ? void 0 : _c.returnType) === null || _d === void 0 ? void 0 : _d.forEach((x) => types.add(x));
|
|
40
|
+
}
|
|
41
|
+
else if (node.name === 'ObjectAccess' && node.firstChild && ((_e = node.lastChild) === null || _e === void 0 ? void 0 : _e.name) === 'Property') {
|
|
42
|
+
const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
|
|
43
|
+
(_f = resolveTypes(state, node.firstChild, config)) === null || _f === void 0 ? void 0 : _f.forEach(baseType => {
|
|
44
|
+
var _a, _b, _c, _d;
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
(_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.name, 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));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else if (node.name === 'ObjectAccess' && node.firstChild && ((_g = node.lastChild) === null || _g === void 0 ? void 0 : _g.name) === 'Method') {
|
|
50
|
+
const varName = state.sliceDoc(node.lastChild.from, node.lastChild.to) || '';
|
|
51
|
+
(_h = resolveTypes(state, node.firstChild, config)) === null || _h === void 0 ? void 0 : _h.forEach(baseType => {
|
|
52
|
+
var _a, _b, _c, _d;
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
(_d = (_c = resolveIdentifier((_a = node.lastChild) === null || _a === void 0 ? void 0 : _a.name, 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));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else if (node.name === 'Application' && node.firstChild) {
|
|
58
|
+
resolveTypes(state, node.firstChild, config).forEach(x => types.add(x));
|
|
59
|
+
}
|
|
60
|
+
if (types.size === 0) {
|
|
61
|
+
types.add('any');
|
|
62
|
+
}
|
|
63
|
+
return types;
|
|
64
|
+
}
|
|
65
|
+
function getExpressionLanguageConfig(state) {
|
|
66
|
+
return state.languageDataAt('expressionLanguageConfig', 0)[0];
|
|
67
|
+
}
|
|
68
|
+
const keywords = [
|
|
69
|
+
{ name: 'starts with', info: 'Check if a string starts with a specific string' },
|
|
70
|
+
{ name: 'ends with', info: 'Check if a string ends with a specific string' },
|
|
71
|
+
{ name: 'contains', info: 'Check if a string is not included in another string' },
|
|
72
|
+
{ name: 'matches', info: 'Check if a string matches a regex pattern' },
|
|
73
|
+
{ name: 'not in', info: 'Check if a value is not included in an array' },
|
|
74
|
+
{ name: 'in', info: 'Check if a value is included in an array' },
|
|
75
|
+
{ name: 'not' },
|
|
76
|
+
{ name: 'or' },
|
|
77
|
+
{ name: 'and' },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const autocompleteFunction = (x) => {
|
|
81
|
+
var _a, _b;
|
|
82
|
+
return ({
|
|
83
|
+
label: `${x.name}(${((_a = x.args) === null || _a === void 0 ? void 0 : _a.join(',')) || ''})`,
|
|
84
|
+
apply: (view, completion, from, to) => {
|
|
85
|
+
var _a;
|
|
86
|
+
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) } }));
|
|
87
|
+
},
|
|
88
|
+
detail: (_b = x.returnType) === null || _b === void 0 ? void 0 : _b.join('|'),
|
|
89
|
+
info: () => { var _a; return createInfoElement((_a = x.info) !== null && _a !== void 0 ? _a : ''); },
|
|
90
|
+
type: "function",
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const autocompleteIdentifier = (x) => {
|
|
94
|
+
var _a;
|
|
95
|
+
return ({
|
|
96
|
+
label: x.name,
|
|
97
|
+
apply: x.name,
|
|
98
|
+
info: () => { var _a; return createInfoElement((_a = x.info) !== null && _a !== void 0 ? _a : ''); },
|
|
99
|
+
detail: x.detail || ((_a = x.type) === null || _a === void 0 ? void 0 : _a.join('|')),
|
|
100
|
+
type: 'variable',
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
function completeOperatorKeyword(state, config, tree, from, to, explicit) {
|
|
104
|
+
var _a;
|
|
105
|
+
return {
|
|
106
|
+
from,
|
|
107
|
+
to,
|
|
108
|
+
options: (_a = keywords.map(({ name, info, detail }) => ({ label: name, apply: `${name} `, info, detail, type: "keyword" }))) !== null && _a !== void 0 ? _a : [],
|
|
109
|
+
validFor: (text) => { var _a; return (_a = keywords.some(({ name }) => explicit || name.includes(text))) !== null && _a !== void 0 ? _a : false; },
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function completeIdentifier(state, config, tree, from, to) {
|
|
113
|
+
var _a, _b;
|
|
114
|
+
const identifiers = (_a = config.identifiers) !== null && _a !== void 0 ? _a : []; //?.filter(({ name }) => explicit || name.toLowerCase().startsWith(text)) ?? [];
|
|
115
|
+
const functions = (_b = config.functions) !== null && _b !== void 0 ? _b : []; //?.filter(({ name }) => explicit || name.toLowerCase().startsWith(text)) ?? [];
|
|
116
|
+
return {
|
|
117
|
+
from,
|
|
118
|
+
to,
|
|
119
|
+
options: [...(identifiers.map(autocompleteIdentifier)), ...(functions.map(autocompleteFunction))],
|
|
120
|
+
validFor: /^[a-zA-Z_]+[a-zA-Z_0-9]*$/,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function completeMember(state, config, tree, from, to, explicit) {
|
|
124
|
+
var _a, _b, _c, _d;
|
|
125
|
+
if (((_a = tree.parent) === null || _a === void 0 ? void 0 : _a.name) != 'ObjectAccess' || !tree.parent.firstChild) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
const types = resolveTypes(state, tree.parent.firstChild.node, config);
|
|
129
|
+
if (!(types === null || types === void 0 ? void 0 : types.size)) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
let options = [];
|
|
133
|
+
for (const type of types) {
|
|
134
|
+
const typeDeclaration = (_b = config.types) === null || _b === void 0 ? void 0 : _b[type];
|
|
135
|
+
options.push(...(((_c = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.identifiers) === null || _c === void 0 ? void 0 : _c.map(autocompleteIdentifier)) || []), ...(((_d = typeDeclaration === null || typeDeclaration === void 0 ? void 0 : typeDeclaration.functions) === null || _d === void 0 ? void 0 : _d.map(autocompleteFunction)) || []));
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
from,
|
|
139
|
+
to,
|
|
140
|
+
options,
|
|
141
|
+
validFor: /^[a-zA-Z_]+[a-zA-Z_0-9]*$/,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function expressionLanguageCompletion(context) {
|
|
145
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
146
|
+
const { state, pos, explicit } = context;
|
|
147
|
+
const tree = syntaxTree(state);
|
|
148
|
+
const lastChar = state.sliceDoc(pos - 1, pos);
|
|
149
|
+
const prevNode = tree.resolveInner(pos, lastChar === ')' ? 0 : -1);
|
|
150
|
+
const config = getExpressionLanguageConfig(state);
|
|
151
|
+
const isIdentifier = (node) => { var _a; return ['Variable', 'Function'].includes((_a = node === null || node === void 0 ? void 0 : node.name) !== null && _a !== void 0 ? _a : ''); };
|
|
152
|
+
const isMember = (node) => { var _a; return ['Property', 'Method'].includes((_a = node === null || node === void 0 ? void 0 : node.name) !== null && _a !== void 0 ? _a : ''); };
|
|
153
|
+
if (prevNode.name == 'String') {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (((_a = prevNode.parent) === null || _a === void 0 ? void 0 : _a.name) == 'ObjectAccess' && ['ObjectAccess', 'ArrayAccess', 'Variable', 'Call'].includes(((_b = prevNode.parent.firstChild) === null || _b === void 0 ? void 0 : _b.name) || '')) {
|
|
157
|
+
return completeMember(state, config, prevNode, isIdentifier(prevNode) || isMember(prevNode) ? prevNode.from : pos, pos);
|
|
158
|
+
}
|
|
159
|
+
if (['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression'].includes(prevNode.name) && prevNode.lastChild && !((_c = prevNode.lastChild) === null || _c === void 0 ? void 0 : _c.type.isError)
|
|
160
|
+
|| ['Arguments', 'Array'].includes(prevNode.name) && prevNode.lastChild && !((_d = prevNode.lastChild) === null || _d === void 0 ? void 0 : _d.type.isError)
|
|
161
|
+
|| ['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression'].includes((_f = (_e = prevNode.parent) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : '') && prevNode.type.isError
|
|
162
|
+
|| ['Variable', 'Function'].includes((_h = (_g = prevNode.parent) === null || _g === void 0 ? void 0 : _g.name) !== null && _h !== void 0 ? _h : '') && prevNode.type.isError) {
|
|
163
|
+
return completeOperatorKeyword(state, config, prevNode, !['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression', 'Arguments'].includes(prevNode.name) ? prevNode.from : pos, pos, explicit);
|
|
164
|
+
}
|
|
165
|
+
if (!/[0-9]/.test(lastChar) && !['OperatorKeyword'].includes((_j = prevNode.name) !== null && _j !== void 0 ? _j : '') && (['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression'].includes(prevNode.name) && ((_k = prevNode.lastChild) === null || _k === void 0 ? void 0 : _k.type.isError)
|
|
166
|
+
|| ['Expression', 'UnaryExpression', 'BinaryExpression', 'TernaryExpression', 'Arguments'].includes((_m = (_l = prevNode.parent) === null || _l === void 0 ? void 0 : _l.name) !== null && _m !== void 0 ? _m : '') && !prevNode.type.isError
|
|
167
|
+
|| ['Arguments', 'Array'].includes((_o = prevNode.name) !== null && _o !== void 0 ? _o : ''))) {
|
|
168
|
+
return completeIdentifier(state, config, prevNode, isIdentifier(prevNode) ? prevNode.from : pos, pos);
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export { expressionLanguageCompletion };
|