@bpmn-io/feel-editor 1.5.0 → 1.6.1
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 +10 -1
- package/dist/index.es.js +545 -359
- package/dist/index.js +559 -373
- package/package.json +22 -22
package/dist/index.js
CHANGED
|
@@ -6,15 +6,113 @@ var language$1 = require('@codemirror/language');
|
|
|
6
6
|
var lint = require('@codemirror/lint');
|
|
7
7
|
var state = require('@codemirror/state');
|
|
8
8
|
var view = require('@codemirror/view');
|
|
9
|
-
var langFeel = require('lang-feel');
|
|
10
|
-
var minDom = require('min-dom');
|
|
11
9
|
var feelLint = require('@bpmn-io/feel-lint');
|
|
12
10
|
var highlight = require('@lezer/highlight');
|
|
11
|
+
var langFeel = require('lang-feel');
|
|
12
|
+
var minDom = require('min-dom');
|
|
13
|
+
|
|
14
|
+
var linter = [ lint.linter(feelLint.cmFeelLinter()) ];
|
|
15
|
+
|
|
16
|
+
const baseTheme = view.EditorView.theme({
|
|
17
|
+
'& .cm-content': {
|
|
18
|
+
padding: '0px',
|
|
19
|
+
},
|
|
20
|
+
'& .cm-line': {
|
|
21
|
+
padding: '0px',
|
|
22
|
+
},
|
|
23
|
+
'&.cm-editor.cm-focused': {
|
|
24
|
+
outline: 'none',
|
|
25
|
+
},
|
|
26
|
+
'& .cm-completionInfo': {
|
|
27
|
+
whiteSpace: 'pre-wrap',
|
|
28
|
+
overflow: 'hidden',
|
|
29
|
+
textOverflow: 'ellipsis'
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// Don't wrap whitespace for custom HTML
|
|
33
|
+
'& .cm-completionInfo > *': {
|
|
34
|
+
whiteSpace: 'normal'
|
|
35
|
+
},
|
|
36
|
+
'& .cm-completionInfo ul': {
|
|
37
|
+
margin: 0,
|
|
38
|
+
paddingLeft: '15px'
|
|
39
|
+
},
|
|
40
|
+
'& .cm-completionInfo pre': {
|
|
41
|
+
marginBottom: 0,
|
|
42
|
+
whiteSpace: 'pre-wrap'
|
|
43
|
+
},
|
|
44
|
+
'& .cm-completionInfo p': {
|
|
45
|
+
marginTop: 0,
|
|
46
|
+
},
|
|
47
|
+
'& .cm-completionInfo p:not(:last-of-type)': {
|
|
48
|
+
marginBottom: 0,
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const highlightTheme = view.EditorView.baseTheme({
|
|
53
|
+
'& .variableName': {
|
|
54
|
+
color: '#10f'
|
|
55
|
+
},
|
|
56
|
+
'& .number': {
|
|
57
|
+
color: '#164'
|
|
58
|
+
},
|
|
59
|
+
'& .string': {
|
|
60
|
+
color: '#a11'
|
|
61
|
+
},
|
|
62
|
+
'& .bool': {
|
|
63
|
+
color: '#219'
|
|
64
|
+
},
|
|
65
|
+
'& .function': {
|
|
66
|
+
color: '#aa3731',
|
|
67
|
+
fontWeight: 'bold'
|
|
68
|
+
},
|
|
69
|
+
'& .control': {
|
|
70
|
+
color: '#708'
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const syntaxClasses = language$1.syntaxHighlighting(
|
|
75
|
+
language$1.HighlightStyle.define([
|
|
76
|
+
{ tag: highlight.tags.variableName, class: 'variableName' },
|
|
77
|
+
{ tag: highlight.tags.name, class: 'variableName' },
|
|
78
|
+
{ tag: highlight.tags.number, class: 'number' },
|
|
79
|
+
{ tag: highlight.tags.string, class: 'string' },
|
|
80
|
+
{ tag: highlight.tags.bool, class: 'bool' },
|
|
81
|
+
{ tag: highlight.tags.function(highlight.tags.variableName), class: 'function' },
|
|
82
|
+
{ tag: highlight.tags.function(highlight.tags.special(highlight.tags.variableName)), class: 'function' },
|
|
83
|
+
{ tag: highlight.tags.controlKeyword, class: 'control' },
|
|
84
|
+
{ tag: highlight.tags.operatorKeyword, class: 'control' }
|
|
85
|
+
])
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
var theme = [ baseTheme, highlightTheme, syntaxClasses ];
|
|
13
89
|
|
|
14
90
|
// helpers ///////////////////////////////
|
|
15
91
|
|
|
16
|
-
function
|
|
17
|
-
return node.from === node.to;
|
|
92
|
+
function _isEmpty(node) {
|
|
93
|
+
return node && node.from === node.to;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {any} node
|
|
98
|
+
* @param {number} pos
|
|
99
|
+
*
|
|
100
|
+
* @return {boolean}
|
|
101
|
+
*/
|
|
102
|
+
function isEmpty(node, pos) {
|
|
103
|
+
|
|
104
|
+
// For the special case of empty nodes, we need to check the current node
|
|
105
|
+
// as well. The previous node could be part of another token, e.g.
|
|
106
|
+
// when typing functions "abs(".
|
|
107
|
+
const nextNode = node.nextSibling;
|
|
108
|
+
|
|
109
|
+
return _isEmpty(node) || (
|
|
110
|
+
nextNode && nextNode.from === pos && _isEmpty(nextNode)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isVariableName(node) {
|
|
115
|
+
return node && node.parent && node.parent.name === 'VariableName';
|
|
18
116
|
}
|
|
19
117
|
|
|
20
118
|
function isPathExpression(node) {
|
|
@@ -29,7 +127,382 @@ function isPathExpression(node) {
|
|
|
29
127
|
return isPathExpression(node.parent);
|
|
30
128
|
}
|
|
31
129
|
|
|
32
|
-
|
|
130
|
+
/**
|
|
131
|
+
* @typedef { import('../core').Variable } Variable
|
|
132
|
+
* @typedef { import('@codemirror/autocomplete').CompletionSource } CompletionSource
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @param { {
|
|
137
|
+
* variables?: Variable[],
|
|
138
|
+
* } } options
|
|
139
|
+
*
|
|
140
|
+
* @return { CompletionSource }
|
|
141
|
+
*/
|
|
142
|
+
function pathExpressionCompletion({ variables }) {
|
|
143
|
+
|
|
144
|
+
return (context) => {
|
|
145
|
+
|
|
146
|
+
const nodeBefore = language$1.syntaxTree(context.state).resolve(context.pos, -1);
|
|
147
|
+
|
|
148
|
+
if (!isPathExpression(nodeBefore)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const expression = findPathExpression(nodeBefore);
|
|
153
|
+
|
|
154
|
+
// if the cursor is directly after the `.`, variable starts at the cursor position
|
|
155
|
+
const from = nodeBefore === expression ? context.pos : nodeBefore.from;
|
|
156
|
+
|
|
157
|
+
const path = getPath(expression, context);
|
|
158
|
+
|
|
159
|
+
let options = variables;
|
|
160
|
+
for (var i = 0; i < path.length - 1; i++) {
|
|
161
|
+
var childVar = options.find(val => val.name === path[i].name);
|
|
162
|
+
|
|
163
|
+
if (!childVar) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// only suggest if variable type matches
|
|
168
|
+
if (
|
|
169
|
+
childVar.isList !== 'optional' &&
|
|
170
|
+
!!childVar.isList !== path[i].isList
|
|
171
|
+
) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
options = childVar.entries;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!options) return;
|
|
179
|
+
|
|
180
|
+
options = options.map(v => ({
|
|
181
|
+
label: v.name,
|
|
182
|
+
type: 'variable',
|
|
183
|
+
info: v.info,
|
|
184
|
+
detail: v.detail
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
const result = {
|
|
188
|
+
from: from,
|
|
189
|
+
options: options
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return result;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
function findPathExpression(node) {
|
|
198
|
+
while (node) {
|
|
199
|
+
if (node.name === 'PathExpression') {
|
|
200
|
+
return node;
|
|
201
|
+
}
|
|
202
|
+
node = node.parent;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// parses the path expression into a list of variable names with type information
|
|
207
|
+
// e.g. foo[0].bar => [ { name: 'foo', isList: true }, { name: 'bar', isList: false } ]
|
|
208
|
+
function getPath(node, context) {
|
|
209
|
+
let path = [];
|
|
210
|
+
|
|
211
|
+
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
212
|
+
if (child.name === 'PathExpression') {
|
|
213
|
+
path.push(...getPath(child, context));
|
|
214
|
+
} else if (child.name === 'FilterExpression') {
|
|
215
|
+
path.push(...getFilter(child, context));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
path.push({
|
|
219
|
+
name: getNodeContent(child, context),
|
|
220
|
+
isList: false
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return path;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getFilter(node, context) {
|
|
228
|
+
const list = node.firstChild;
|
|
229
|
+
|
|
230
|
+
if (list.name === 'PathExpression') {
|
|
231
|
+
const path = getPath(list, context);
|
|
232
|
+
const last = path[path.length - 1];
|
|
233
|
+
last.isList = true;
|
|
234
|
+
|
|
235
|
+
return path;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return [ {
|
|
239
|
+
name: getNodeContent(list, context),
|
|
240
|
+
isList: true
|
|
241
|
+
} ];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function getNodeContent(node, context) {
|
|
245
|
+
return context.state.sliceDoc(node.from, node.to);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @typedef { import('../core').Variable } Variable
|
|
250
|
+
* @typedef { import('@codemirror/autocomplete').CompletionSource } CompletionSource
|
|
251
|
+
*/
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @param { {
|
|
255
|
+
* variables?: Variable[],
|
|
256
|
+
* builtins?: Variable[]
|
|
257
|
+
* } } options
|
|
258
|
+
*
|
|
259
|
+
* @return { CompletionSource }
|
|
260
|
+
*/
|
|
261
|
+
function variableCompletion({ variables = [], builtins = [] }) {
|
|
262
|
+
|
|
263
|
+
const options = getVariableSuggestions(variables, builtins);
|
|
264
|
+
|
|
265
|
+
if (!options.length) {
|
|
266
|
+
return (context) => null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return (context) => {
|
|
270
|
+
|
|
271
|
+
const {
|
|
272
|
+
pos,
|
|
273
|
+
state
|
|
274
|
+
} = context;
|
|
275
|
+
|
|
276
|
+
// in most cases, use what is typed before the cursor
|
|
277
|
+
const nodeBefore = language$1.syntaxTree(state).resolve(pos, -1);
|
|
278
|
+
|
|
279
|
+
if (isEmpty(nodeBefore, pos)) {
|
|
280
|
+
return context.explicit ? {
|
|
281
|
+
from: pos,
|
|
282
|
+
options
|
|
283
|
+
} : null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// only auto-complete variables
|
|
287
|
+
if (!isVariableName(nodeBefore) || isPathExpression(nodeBefore)) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
from: nodeBefore.from,
|
|
293
|
+
options
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* @param { Variable[] } variables
|
|
300
|
+
* @param { Variable[] } builtins
|
|
301
|
+
*
|
|
302
|
+
* @returns {import('@codemirror/autocomplete').Completion[]}
|
|
303
|
+
*/
|
|
304
|
+
function getVariableSuggestions(variables, builtins) {
|
|
305
|
+
return [].concat(
|
|
306
|
+
variables.map(v => createVariableSuggestion(v)),
|
|
307
|
+
builtins.map(b => createVariableSuggestion(b))
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @param {import('..').Variable} variable
|
|
313
|
+
* @param {number} boost
|
|
314
|
+
|
|
315
|
+
* @returns {import('@codemirror/autocomplete').Completion}
|
|
316
|
+
*/
|
|
317
|
+
function createVariableSuggestion(variable, boost) {
|
|
318
|
+
if (variable.type === 'function') {
|
|
319
|
+
return createFunctionVariable(variable, boost);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
label: variable.name,
|
|
324
|
+
type: 'variable',
|
|
325
|
+
info: variable.info,
|
|
326
|
+
detail: variable.detail,
|
|
327
|
+
boost
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* @param {import('..').Variable} variable
|
|
333
|
+
* @param {number} boost
|
|
334
|
+
*
|
|
335
|
+
* @returns {import('@codemirror/autocomplete').Completion}
|
|
336
|
+
*/
|
|
337
|
+
function createFunctionVariable(variable, boost) {
|
|
338
|
+
const {
|
|
339
|
+
name,
|
|
340
|
+
info,
|
|
341
|
+
detail,
|
|
342
|
+
params = []
|
|
343
|
+
} = variable;
|
|
344
|
+
|
|
345
|
+
const paramsWithNames = params.map(({ name, type }, index) => ({
|
|
346
|
+
name: name || `param ${index + 1}`,
|
|
347
|
+
type
|
|
348
|
+
}));
|
|
349
|
+
|
|
350
|
+
const template = `${name}(${paramsWithNames.map(p => '${' + p.name + '}').join(', ')})`;
|
|
351
|
+
|
|
352
|
+
const paramsSignature = paramsWithNames.map(({ name, type }) => (
|
|
353
|
+
type ? `${name}: ${type}` : name
|
|
354
|
+
)).join(', ');
|
|
355
|
+
const label = `${name}(${paramsSignature})`;
|
|
356
|
+
|
|
357
|
+
return autocomplete.snippetCompletion(template, {
|
|
358
|
+
label,
|
|
359
|
+
type: 'function',
|
|
360
|
+
info,
|
|
361
|
+
detail,
|
|
362
|
+
boost
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @typedef { import('../core').Variable } Variable
|
|
368
|
+
* @typedef { import('@codemirror/autocomplete').CompletionSource } CompletionSource
|
|
369
|
+
*/
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* @param { {
|
|
373
|
+
* variables?: Variable[],
|
|
374
|
+
* builtins?: Variable[]
|
|
375
|
+
* } } options
|
|
376
|
+
*
|
|
377
|
+
* @return { CompletionSource[] }
|
|
378
|
+
*/
|
|
379
|
+
function completions({ variables = [], builtins = [] }) {
|
|
380
|
+
|
|
381
|
+
return [
|
|
382
|
+
pathExpressionCompletion({ variables }),
|
|
383
|
+
variableCompletion({ variables, builtins }),
|
|
384
|
+
langFeel.snippetCompletion(langFeel.snippets.map(snippet => ({ ...snippet, boost: -1 }))),
|
|
385
|
+
...langFeel.keywordCompletions
|
|
386
|
+
];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* @typedef { 'expression' | 'unaryTests' } Dialect
|
|
391
|
+
*/
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* @param { {
|
|
395
|
+
* dialect?: Dialect,
|
|
396
|
+
* context?: Record<string, any>,
|
|
397
|
+
* completions?: import('@codemirror/autocomplete').CompletionSource[]
|
|
398
|
+
* } } options
|
|
399
|
+
*
|
|
400
|
+
* @return { import('@codemirror/language').LanguageSupport }
|
|
401
|
+
*/
|
|
402
|
+
function language(options) {
|
|
403
|
+
return langFeel.feel(options);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @param { import('../core').Variable[] } variables
|
|
408
|
+
*
|
|
409
|
+
* @return {Record<string, any>}
|
|
410
|
+
*/
|
|
411
|
+
function createContext(variables, builtins) {
|
|
412
|
+
return variables.slice().reverse().reduce((context, builtin) => {
|
|
413
|
+
context[builtin.name] = () => {};
|
|
414
|
+
|
|
415
|
+
return context;
|
|
416
|
+
}, {});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @typedef { 'expression' | 'unaryTests' } Dialect
|
|
421
|
+
* @typedef { import('..').Variable } Variable
|
|
422
|
+
*/
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @type {Facet<Variable[]>}
|
|
426
|
+
*/
|
|
427
|
+
const builtinsFacet = state.Facet.define();
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* @type {Facet<Variable[]>}
|
|
431
|
+
*/
|
|
432
|
+
const variablesFacet = state.Facet.define();
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* @type {Facet<dialect>}
|
|
436
|
+
*/
|
|
437
|
+
const dialectFacet = state.Facet.define();
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* @typedef {object} Variable
|
|
441
|
+
* @property {string} name name or key of the variable
|
|
442
|
+
* @property {string} [info] short information about the variable, e.g. type
|
|
443
|
+
* @property {string} [detail] longer description of the variable content
|
|
444
|
+
* @property {boolean} [isList] whether the variable is a list
|
|
445
|
+
* @property {Array<Variable>} [schema] array of child variables if the variable is a context or list
|
|
446
|
+
* @property {'function'|'variable'} [type] type of the variable
|
|
447
|
+
* @property {Array<{name: string, type: string}>} [params] function parameters
|
|
448
|
+
*/
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* @typedef { {
|
|
452
|
+
* dialect?: import('../language').Dialect,
|
|
453
|
+
* variables?: Variable[],
|
|
454
|
+
* builtins?: Variable[]
|
|
455
|
+
* } } CoreConfig
|
|
456
|
+
*
|
|
457
|
+
* @typedef { import('@codemirror/autocomplete').CompletionSource } CompletionSource
|
|
458
|
+
* @typedef { import('@codemirror/state').Extension } Extension
|
|
459
|
+
*/
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* @param { CoreConfig & { completions?: CompletionSource[] } } config
|
|
463
|
+
*
|
|
464
|
+
* @return { Extension }
|
|
465
|
+
*/
|
|
466
|
+
function configure({
|
|
467
|
+
dialect = 'expression',
|
|
468
|
+
variables = [],
|
|
469
|
+
builtins = [],
|
|
470
|
+
completions: completions$1 = completions({ builtins, variables })
|
|
471
|
+
}) {
|
|
472
|
+
|
|
473
|
+
const context = createContext([ ...variables, ...builtins ]);
|
|
474
|
+
|
|
475
|
+
return [
|
|
476
|
+
dialectFacet.of(dialect),
|
|
477
|
+
builtinsFacet.of(builtins),
|
|
478
|
+
variablesFacet.of(variables),
|
|
479
|
+
language({
|
|
480
|
+
dialect,
|
|
481
|
+
context,
|
|
482
|
+
completions: completions$1
|
|
483
|
+
})
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @param {import('@codemirror/state').EditorState } state
|
|
489
|
+
*
|
|
490
|
+
* @return { CoreConfig }
|
|
491
|
+
*/
|
|
492
|
+
function get(state) {
|
|
493
|
+
|
|
494
|
+
const builtins = state.facet(builtinsFacet)[0];
|
|
495
|
+
const variables = state.facet(variablesFacet)[0];
|
|
496
|
+
const dialect = state.facet(dialectFacet)[0];
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
builtins,
|
|
500
|
+
variables,
|
|
501
|
+
dialect
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
var camundaTags = [
|
|
33
506
|
{
|
|
34
507
|
name: "not(negand)",
|
|
35
508
|
description: "<p>Returns the logical negation of the given value.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">not(negand: boolean): boolean\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">not(true)\n// false\n\nnot(null)\n// null\n</code></pre>\n"
|
|
@@ -108,7 +581,7 @@ var tags = [
|
|
|
108
581
|
},
|
|
109
582
|
{
|
|
110
583
|
name: "date and time(from)",
|
|
111
|
-
description: "<p>Parses the given string into a date and time.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">date and time(from: string): date and time\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">date and time("2018-04-
|
|
584
|
+
description: "<p>Parses the given string into a date and time.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">date and time(from: string): date and time\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">date and time("2018-04-29T09:30:00")\n// date and time("2018-04-29T09:30:00")\n</code></pre>\n"
|
|
112
585
|
},
|
|
113
586
|
{
|
|
114
587
|
name: "date and time(date, time)",
|
|
@@ -491,384 +964,78 @@ var tags = [
|
|
|
491
964
|
description: "<p>Returns the day of the week according to the Gregorian calendar. Note that it always returns the English name of the day.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">day of week(date: date): string\n</code></pre>\n<pre><code class=\"language-feel\">day of week(date: date and time): string\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">day of week(date("2019-09-17"))\n// "Tuesday"\n\nday of week(date and time("2019-09-17T12:00:00"))\n// "Tuesday"\n</code></pre>\n"
|
|
492
965
|
},
|
|
493
966
|
{
|
|
494
|
-
name: "day of year(date)",
|
|
495
|
-
description: "<p>Returns the Gregorian number of the day within the year.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">day of year(date: date): number\n</code></pre>\n<pre><code class=\"language-feel\">day of year(date: date and time): number\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">day of year(date("2019-09-17"))\n// 260\n\nday of year(date and time("2019-09-17T12:00:00"))\n// 260\n</code></pre>\n"
|
|
496
|
-
},
|
|
497
|
-
{
|
|
498
|
-
name: "week of year(date)",
|
|
499
|
-
description: "<p>Returns the Gregorian number of the week within the year, according to ISO 8601.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">week of year(date: date): number\n</code></pre>\n<pre><code class=\"language-feel\">week of year(date: date and time): number\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">week of year(date("2019-09-17"))\n// 38\n\nweek of year(date and time("2019-09-17T12:00:00"))\n// 38\n</code></pre>\n"
|
|
500
|
-
},
|
|
501
|
-
{
|
|
502
|
-
name: "month of year(date)",
|
|
503
|
-
description: "<p>Returns the month of the year according to the Gregorian calendar. Note that it always returns the English name of the month.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">month of year(date: date): string\n</code></pre>\n<pre><code class=\"language-feel\">month of year(date: date and time): string\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">month of year(date("2019-09-17"))\n// "September"\n\nmonth of year(date and time("2019-09-17T12:00:00"))\n// "September"\n</code></pre>\n"
|
|
504
|
-
},
|
|
505
|
-
{
|
|
506
|
-
name: "abs(n)",
|
|
507
|
-
description: "<p>Returns the absolute value of a given duration.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">abs(n: days and time duration): days and time duration\n</code></pre>\n<pre><code class=\"language-feel\">abs(n: years and months duration): years and months duration\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">abs(duration("-PT5H"))\n// "duration("PT5H")"\n\nabs(duration("PT5H"))\n// "duration("PT5H")"\n\nabs(duration("-P2M"))\n// duration("P2M")\n</code></pre>\n"
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
name: "last day of month(date)",
|
|
511
|
-
description: "<p><em>Camunda Extension</em></p>\n<p>Takes the month of the given date or date-time value and returns the last day of this month.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">last day of month(date: date): date\n</code></pre>\n<pre><code class=\"language-feel\">last day of month(date: date and time): date\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">last day of month(date("2022-10-01"))\n// date("2022-10-31"))\n\nlast day of month(date and time("2022-10-16T12:00:00"))\n// date("2022-10-31"))\n</code></pre>\n"
|
|
512
|
-
}
|
|
513
|
-
];
|
|
514
|
-
|
|
515
|
-
const options = tags.map(tag => {
|
|
516
|
-
const match = tag.name.match(/^([\w\s]+)\((.*)\)$/);
|
|
517
|
-
const functionName = match[1];
|
|
518
|
-
const functionArguments = match[2];
|
|
519
|
-
|
|
520
|
-
const placeHolders = functionArguments
|
|
521
|
-
.split(', ')
|
|
522
|
-
.map((arg) => `\${${arg}}`)
|
|
523
|
-
.join(', ');
|
|
524
|
-
|
|
525
|
-
return autocomplete.snippetCompletion(
|
|
526
|
-
`${functionName}(${placeHolders})`,
|
|
527
|
-
{
|
|
528
|
-
label: tag.name,
|
|
529
|
-
type: 'function',
|
|
530
|
-
info: () => {
|
|
531
|
-
const html = minDom.domify(`<div class="description">${tag.description}<div>`);
|
|
532
|
-
return html;
|
|
533
|
-
},
|
|
534
|
-
boost: -1
|
|
535
|
-
}
|
|
536
|
-
);
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
var builtins = context => {
|
|
540
|
-
|
|
541
|
-
let nodeBefore = language$1.syntaxTree(context.state).resolve(context.pos, -1);
|
|
542
|
-
|
|
543
|
-
// For the special case of empty nodes, we need to check the current node
|
|
544
|
-
// as well. The previous node could be part of another token, e.g.
|
|
545
|
-
// when typing functions "abs(".
|
|
546
|
-
let nextNode = nodeBefore.nextSibling;
|
|
547
|
-
const isInEmptyNode =
|
|
548
|
-
isNodeEmpty(nodeBefore) ||
|
|
549
|
-
nextNode && nextNode.from === context.pos && isNodeEmpty(nextNode);
|
|
550
|
-
|
|
551
|
-
if (isInEmptyNode) {
|
|
552
|
-
return context.explicit ? {
|
|
553
|
-
from: context.pos,
|
|
554
|
-
options: options
|
|
555
|
-
} : null;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// Don't auto-complete on path expressions/context keys/...
|
|
559
|
-
if ((nodeBefore.parent && nodeBefore.parent.name !== 'VariableName') || isPathExpression(nodeBefore)) {
|
|
560
|
-
return null;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
return {
|
|
564
|
-
from: nodeBefore.from,
|
|
565
|
-
options: options
|
|
566
|
-
};
|
|
567
|
-
};
|
|
967
|
+
name: "day of year(date)",
|
|
968
|
+
description: "<p>Returns the Gregorian number of the day within the year.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">day of year(date: date): number\n</code></pre>\n<pre><code class=\"language-feel\">day of year(date: date and time): number\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">day of year(date("2019-09-17"))\n// 260\n\nday of year(date and time("2019-09-17T12:00:00"))\n// 260\n</code></pre>\n"
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
name: "week of year(date)",
|
|
972
|
+
description: "<p>Returns the Gregorian number of the week within the year, according to ISO 8601.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">week of year(date: date): number\n</code></pre>\n<pre><code class=\"language-feel\">week of year(date: date and time): number\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">week of year(date("2019-09-17"))\n// 38\n\nweek of year(date and time("2019-09-17T12:00:00"))\n// 38\n</code></pre>\n"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
name: "month of year(date)",
|
|
976
|
+
description: "<p>Returns the month of the year according to the Gregorian calendar. Note that it always returns the English name of the month.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">month of year(date: date): string\n</code></pre>\n<pre><code class=\"language-feel\">month of year(date: date and time): string\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">month of year(date("2019-09-17"))\n// "September"\n\nmonth of year(date and time("2019-09-17T12:00:00"))\n// "September"\n</code></pre>\n"
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
name: "abs(n)",
|
|
980
|
+
description: "<p>Returns the absolute value of a given duration.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">abs(n: days and time duration): days and time duration\n</code></pre>\n<pre><code class=\"language-feel\">abs(n: years and months duration): years and months duration\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">abs(duration("-PT5H"))\n// "duration("PT5H")"\n\nabs(duration("PT5H"))\n// "duration("PT5H")"\n\nabs(duration("-P2M"))\n// duration("P2M")\n</code></pre>\n"
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
name: "last day of month(date)",
|
|
984
|
+
description: "<p><em>Camunda Extension</em></p>\n<p>Takes the month of the given date or date-time value and returns the last day of this month.</p>\n<p><strong>Function signature</strong></p>\n<pre><code class=\"language-feel\">last day of month(date: date): date\n</code></pre>\n<pre><code class=\"language-feel\">last day of month(date: date and time): date\n</code></pre>\n<p><strong>Examples</strong></p>\n<pre><code class=\"language-feel\">last day of month(date("2022-10-01"))\n// date("2022-10-31"))\n\nlast day of month(date and time("2022-10-16T12:00:00"))\n// date("2022-10-31"))\n</code></pre>\n"
|
|
985
|
+
}
|
|
986
|
+
];
|
|
568
987
|
|
|
569
988
|
/**
|
|
570
|
-
* @
|
|
989
|
+
* @param { import('..').Builtin[] } builtins
|
|
990
|
+
*
|
|
991
|
+
* @returns {import('..').Variable[] } variable
|
|
571
992
|
*/
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
var pathExpression = context => {
|
|
575
|
-
const variables = context.state.facet(variablesFacet)[0];
|
|
576
|
-
const nodeBefore = language$1.syntaxTree(context.state).resolve(context.pos, -1);
|
|
577
|
-
|
|
578
|
-
if (!isPathExpression(nodeBefore)) {
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const expression = findPathExpression(nodeBefore);
|
|
583
|
-
|
|
584
|
-
// if the cursor is directly after the `.`, variable starts at the cursor position
|
|
585
|
-
const from = nodeBefore === expression ? context.pos : nodeBefore.from;
|
|
586
|
-
|
|
587
|
-
const path = getPath(expression, context);
|
|
588
|
-
|
|
589
|
-
let options = variables;
|
|
590
|
-
for (var i = 0; i < path.length - 1; i++) {
|
|
591
|
-
var childVar = options.find(val => val.name === path[i].name);
|
|
592
|
-
|
|
593
|
-
if (!childVar) {
|
|
594
|
-
return null;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// only suggest if variable type matches
|
|
598
|
-
if (
|
|
599
|
-
childVar.isList !== 'optional' &&
|
|
600
|
-
!!childVar.isList !== path[i].isList
|
|
601
|
-
) {
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
options = childVar.entries;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
if (!options) return;
|
|
609
|
-
|
|
610
|
-
options = options.map(v => ({
|
|
611
|
-
label: v.name,
|
|
612
|
-
type: 'variable',
|
|
613
|
-
info: v.info,
|
|
614
|
-
detail: v.detail
|
|
615
|
-
}));
|
|
616
|
-
|
|
617
|
-
const result = {
|
|
618
|
-
from: from,
|
|
619
|
-
options: options
|
|
620
|
-
};
|
|
621
|
-
|
|
622
|
-
return result;
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
function findPathExpression(node) {
|
|
627
|
-
while (node) {
|
|
628
|
-
if (node.name === 'PathExpression') {
|
|
629
|
-
return node;
|
|
630
|
-
}
|
|
631
|
-
node = node.parent;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// parses the path expression into a list of variable names with type information
|
|
636
|
-
// e.g. foo[0].bar => [ { name: 'foo', isList: true }, { name: 'bar', isList: false } ]
|
|
637
|
-
function getPath(node, context) {
|
|
638
|
-
let path = [];
|
|
639
|
-
|
|
640
|
-
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
641
|
-
if (child.name === 'PathExpression') {
|
|
642
|
-
path.push(...getPath(child, context));
|
|
643
|
-
} else if (child.name === 'FilterExpression') {
|
|
644
|
-
path.push(...getFilter(child, context));
|
|
645
|
-
}
|
|
646
|
-
else {
|
|
647
|
-
path.push({
|
|
648
|
-
name: getNodeContent(child, context),
|
|
649
|
-
isList: false
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
return path;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
function getFilter(node, context) {
|
|
657
|
-
const list = node.firstChild;
|
|
658
|
-
|
|
659
|
-
if (list.name === 'PathExpression') {
|
|
660
|
-
const path = getPath(list, context);
|
|
661
|
-
const last = path[path.length - 1];
|
|
662
|
-
last.isList = true;
|
|
663
|
-
|
|
664
|
-
return path;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
return [ {
|
|
668
|
-
name: getNodeContent(list, context),
|
|
669
|
-
isList: true
|
|
670
|
-
} ];
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function getNodeContent(node, context) {
|
|
674
|
-
return context.state.sliceDoc(node.from, node.to);
|
|
993
|
+
function parseBuiltins(builtins) {
|
|
994
|
+
return builtins.map(parseBuiltin);
|
|
675
995
|
}
|
|
676
996
|
|
|
677
997
|
/**
|
|
678
|
-
* @
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const variables = context.state.facet(variablesFacet)[0];
|
|
683
|
-
|
|
684
|
-
const options = variables.map(v => createVariableSuggestion(v));
|
|
685
|
-
|
|
686
|
-
// In most cases, use what is typed before the cursor
|
|
687
|
-
let nodeBefore = language$1.syntaxTree(context.state).resolve(context.pos, -1);
|
|
688
|
-
|
|
689
|
-
// For the special case of empty nodes, we need to check the current node
|
|
690
|
-
// as well. The previous node could be part of another token, e.g.
|
|
691
|
-
// when typing functions "abs(".
|
|
692
|
-
let nextNode = nodeBefore.nextSibling;
|
|
693
|
-
const isInEmptyNode =
|
|
694
|
-
isNodeEmpty(nodeBefore) ||
|
|
695
|
-
nextNode && nextNode.from === context.pos && isNodeEmpty(nextNode);
|
|
696
|
-
|
|
697
|
-
if (isInEmptyNode) {
|
|
698
|
-
return context.explicit ? {
|
|
699
|
-
from: context.pos,
|
|
700
|
-
options: options
|
|
701
|
-
} : null;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
const result = {
|
|
705
|
-
from: nodeBefore.from,
|
|
706
|
-
options: options
|
|
707
|
-
};
|
|
708
|
-
|
|
709
|
-
// Only auto-complete variables
|
|
710
|
-
if ((nodeBefore.parent && nodeBefore.parent.name !== 'VariableName') || isPathExpression(nodeBefore)) {
|
|
711
|
-
return null;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
return result;
|
|
715
|
-
};
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* @param {import('..').Variable} variable
|
|
719
|
-
* @returns {import('@codemirror/autocomplete').Completion}
|
|
998
|
+
* @param { import('..').Builtin } builtin
|
|
999
|
+
*
|
|
1000
|
+
* @returns { import('..').Variable } variable
|
|
720
1001
|
*/
|
|
721
|
-
function
|
|
722
|
-
if (variable.type === 'function') {
|
|
723
|
-
return createFunctionVariable(variable);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
return {
|
|
727
|
-
label: variable.name,
|
|
728
|
-
type: 'variable',
|
|
729
|
-
info: variable.info,
|
|
730
|
-
detail: variable.detail
|
|
731
|
-
};
|
|
732
|
-
}
|
|
1002
|
+
function parseBuiltin(builtin) {
|
|
733
1003
|
|
|
734
|
-
/**
|
|
735
|
-
* @param {import('..').Variable} variable
|
|
736
|
-
* @returns {import('@codemirror/autocomplete').Completion}
|
|
737
|
-
*/
|
|
738
|
-
function createFunctionVariable(variable) {
|
|
739
1004
|
const {
|
|
740
1005
|
name,
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
params = []
|
|
744
|
-
} = variable;
|
|
745
|
-
|
|
746
|
-
const paramsWithNames = params.map(({ name, type }, index) => ({
|
|
747
|
-
name: name || `param ${index + 1}`,
|
|
748
|
-
type
|
|
749
|
-
}));
|
|
1006
|
+
description
|
|
1007
|
+
} = builtin;
|
|
750
1008
|
|
|
751
|
-
const
|
|
1009
|
+
const match = name.match(/^([\w\s]+)\((.*)\)$/);
|
|
1010
|
+
const functionName = match[1];
|
|
1011
|
+
const functionArguments = match[2];
|
|
752
1012
|
|
|
753
|
-
const
|
|
754
|
-
type ? `${name}: ${type}` : name
|
|
755
|
-
)).join(', ');
|
|
756
|
-
const label = `${name}(${paramsSignature})`;
|
|
1013
|
+
const params = functionArguments.split(', ').map(name => ({ name }));
|
|
757
1014
|
|
|
758
|
-
return
|
|
759
|
-
|
|
1015
|
+
return {
|
|
1016
|
+
name: functionName,
|
|
760
1017
|
type: 'function',
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
return [
|
|
768
|
-
autocomplete.autocompletion({
|
|
769
|
-
override: [
|
|
770
|
-
variables,
|
|
771
|
-
builtins,
|
|
772
|
-
autocomplete.completeFromList(langFeel.snippets.map(s => ({ ...s, boost: -1 }))),
|
|
773
|
-
pathExpression,
|
|
774
|
-
...langFeel.keywordCompletions
|
|
775
|
-
]
|
|
776
|
-
})
|
|
777
|
-
];
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
function language() {
|
|
781
|
-
return new language$1.LanguageSupport(langFeel.feelLanguage, [ ]);
|
|
1018
|
+
params,
|
|
1019
|
+
info: () => {
|
|
1020
|
+
return minDom.domify(`<div class="description">${description}<div>`);
|
|
1021
|
+
},
|
|
1022
|
+
boost: 0
|
|
1023
|
+
};
|
|
782
1024
|
}
|
|
783
1025
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const baseTheme = view.EditorView.theme({
|
|
787
|
-
'& .cm-content': {
|
|
788
|
-
padding: '0px',
|
|
789
|
-
},
|
|
790
|
-
'& .cm-line': {
|
|
791
|
-
padding: '0px',
|
|
792
|
-
},
|
|
793
|
-
'&.cm-editor.cm-focused': {
|
|
794
|
-
outline: 'none',
|
|
795
|
-
},
|
|
796
|
-
'& .cm-completionInfo': {
|
|
797
|
-
whiteSpace: 'pre-wrap',
|
|
798
|
-
overflow: 'hidden',
|
|
799
|
-
textOverflow: 'ellipsis'
|
|
800
|
-
},
|
|
801
|
-
|
|
802
|
-
// Don't wrap whitespace for custom HTML
|
|
803
|
-
'& .cm-completionInfo > *': {
|
|
804
|
-
whiteSpace: 'normal'
|
|
805
|
-
},
|
|
806
|
-
'& .cm-completionInfo ul': {
|
|
807
|
-
margin: 0,
|
|
808
|
-
paddingLeft: '15px'
|
|
809
|
-
},
|
|
810
|
-
'& .cm-completionInfo pre': {
|
|
811
|
-
marginBottom: 0,
|
|
812
|
-
whiteSpace: 'pre-wrap'
|
|
813
|
-
},
|
|
814
|
-
'& .cm-completionInfo p': {
|
|
815
|
-
marginTop: 0,
|
|
816
|
-
},
|
|
817
|
-
'& .cm-completionInfo p:not(:last-of-type)': {
|
|
818
|
-
marginBottom: 0,
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
const highlightTheme = view.EditorView.baseTheme({
|
|
823
|
-
'& .variableName': {
|
|
824
|
-
color: '#10f'
|
|
825
|
-
},
|
|
826
|
-
'& .number': {
|
|
827
|
-
color: '#164'
|
|
828
|
-
},
|
|
829
|
-
'& .string': {
|
|
830
|
-
color: '#a11'
|
|
831
|
-
},
|
|
832
|
-
'& .bool': {
|
|
833
|
-
color: '#219'
|
|
834
|
-
},
|
|
835
|
-
'& .function': {
|
|
836
|
-
color: '#aa3731',
|
|
837
|
-
fontWeight: 'bold'
|
|
838
|
-
},
|
|
839
|
-
'& .control': {
|
|
840
|
-
color: '#708'
|
|
841
|
-
}
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
const syntaxClasses = language$1.syntaxHighlighting(
|
|
845
|
-
language$1.HighlightStyle.define([
|
|
846
|
-
{ tag: highlight.tags.variableName, class: 'variableName' },
|
|
847
|
-
{ tag: highlight.tags.name, class: 'variableName' },
|
|
848
|
-
{ tag: highlight.tags.number, class: 'number' },
|
|
849
|
-
{ tag: highlight.tags.string, class: 'string' },
|
|
850
|
-
{ tag: highlight.tags.bool, class: 'bool' },
|
|
851
|
-
{ tag: highlight.tags.function(highlight.tags.variableName), class: 'function' },
|
|
852
|
-
{ tag: highlight.tags.function(highlight.tags.special(highlight.tags.variableName)), class: 'function' },
|
|
853
|
-
{ tag: highlight.tags.controlKeyword, class: 'control' },
|
|
854
|
-
{ tag: highlight.tags.operatorKeyword, class: 'control' }
|
|
855
|
-
])
|
|
856
|
-
);
|
|
1026
|
+
const camunda = parseBuiltins(camundaTags);
|
|
857
1027
|
|
|
858
|
-
|
|
1028
|
+
/**
|
|
1029
|
+
* @typedef { import('./core').Variable } Variable
|
|
1030
|
+
*/
|
|
859
1031
|
|
|
860
1032
|
/**
|
|
861
|
-
* @typedef {object}
|
|
862
|
-
* @property {string} name
|
|
863
|
-
* @property {string}
|
|
864
|
-
* @property {string} [detail] longer description of the variable content
|
|
865
|
-
* @property {boolean} [isList] whether the variable is a list
|
|
866
|
-
* @property {Array<Variable>} [schema] array of child variables if the variable is a context or list
|
|
867
|
-
* @property {'function'|'variable'} [type] type of the variable
|
|
868
|
-
* @property {Array<{name: string, type: string}>} [params] function parameters
|
|
1033
|
+
* @typedef {object} Builtin
|
|
1034
|
+
* @property {string} name
|
|
1035
|
+
* @property {string} description
|
|
869
1036
|
*/
|
|
870
1037
|
|
|
871
|
-
const
|
|
1038
|
+
const coreConf = new state.Compartment();
|
|
872
1039
|
const placeholderConf = new state.Compartment();
|
|
873
1040
|
|
|
874
1041
|
|
|
@@ -878,6 +1045,7 @@ const placeholderConf = new state.Compartment();
|
|
|
878
1045
|
* @param {Object} config
|
|
879
1046
|
* @param {DOMNode} config.container
|
|
880
1047
|
* @param {Extension[]} [config.extensions]
|
|
1048
|
+
* @param {Dialect} [config.dialect='expression']
|
|
881
1049
|
* @param {DOMNode|String} [config.tooltipContainer]
|
|
882
1050
|
* @param {Function} [config.onChange]
|
|
883
1051
|
* @param {Function} [config.onKeyDown]
|
|
@@ -885,11 +1053,13 @@ const placeholderConf = new state.Compartment();
|
|
|
885
1053
|
* @param {Boolean} [config.readOnly]
|
|
886
1054
|
* @param {String} [config.value]
|
|
887
1055
|
* @param {Variable[]} [config.variables]
|
|
1056
|
+
* @param {Variable[]} [config.builtins]
|
|
888
1057
|
*
|
|
889
1058
|
* @returns {Object} editor
|
|
890
1059
|
*/
|
|
891
1060
|
function FeelEditor({
|
|
892
1061
|
extensions: editorExtensions = [],
|
|
1062
|
+
dialect = 'expression',
|
|
893
1063
|
container,
|
|
894
1064
|
contentAttributes = {},
|
|
895
1065
|
tooltipContainer,
|
|
@@ -899,6 +1069,7 @@ function FeelEditor({
|
|
|
899
1069
|
placeholder = '',
|
|
900
1070
|
readOnly = false,
|
|
901
1071
|
value = '',
|
|
1072
|
+
builtins = camunda,
|
|
902
1073
|
variables = []
|
|
903
1074
|
}) {
|
|
904
1075
|
|
|
@@ -939,22 +1110,25 @@ function FeelEditor({
|
|
|
939
1110
|
}) : [];
|
|
940
1111
|
|
|
941
1112
|
const extensions = [
|
|
942
|
-
|
|
943
|
-
|
|
1113
|
+
autocomplete.autocompletion(),
|
|
1114
|
+
coreConf.of(configure({
|
|
1115
|
+
dialect,
|
|
1116
|
+
builtins,
|
|
1117
|
+
variables
|
|
1118
|
+
})),
|
|
944
1119
|
language$1.bracketMatching(),
|
|
945
|
-
|
|
1120
|
+
language$1.indentOnInput(),
|
|
946
1121
|
autocomplete.closeBrackets(),
|
|
947
1122
|
view.EditorView.contentAttributes.of(contentAttributes),
|
|
948
|
-
|
|
1123
|
+
changeHandler,
|
|
949
1124
|
keyHandler,
|
|
950
1125
|
view.keymap.of([
|
|
951
1126
|
...commands.defaultKeymap,
|
|
952
1127
|
]),
|
|
953
|
-
language(),
|
|
954
1128
|
linter,
|
|
955
1129
|
lintHandler,
|
|
956
|
-
placeholderConf.of(view.placeholder(placeholder)),
|
|
957
1130
|
tooltipLayout,
|
|
1131
|
+
placeholderConf.of(view.placeholder(placeholder)),
|
|
958
1132
|
theme,
|
|
959
1133
|
...editorExtensions
|
|
960
1134
|
];
|
|
@@ -966,7 +1140,7 @@ function FeelEditor({
|
|
|
966
1140
|
this._cmEditor = new view.EditorView({
|
|
967
1141
|
state: state.EditorState.create({
|
|
968
1142
|
doc: value,
|
|
969
|
-
extensions
|
|
1143
|
+
extensions
|
|
970
1144
|
}),
|
|
971
1145
|
parent: container
|
|
972
1146
|
});
|
|
@@ -1019,19 +1193,31 @@ FeelEditor.prototype.getSelection = function() {
|
|
|
1019
1193
|
|
|
1020
1194
|
/**
|
|
1021
1195
|
* Set variables to be used for autocompletion.
|
|
1196
|
+
*
|
|
1022
1197
|
* @param {Variable[]} variables
|
|
1023
|
-
* @returns {void}
|
|
1024
1198
|
*/
|
|
1025
1199
|
FeelEditor.prototype.setVariables = function(variables) {
|
|
1200
|
+
|
|
1201
|
+
const {
|
|
1202
|
+
dialect,
|
|
1203
|
+
builtins
|
|
1204
|
+
} = get(this._cmEditor.state);
|
|
1205
|
+
|
|
1026
1206
|
this._cmEditor.dispatch({
|
|
1027
|
-
effects:
|
|
1207
|
+
effects: [
|
|
1208
|
+
coreConf.reconfigure(configure({
|
|
1209
|
+
dialect,
|
|
1210
|
+
builtins,
|
|
1211
|
+
variables
|
|
1212
|
+
}))
|
|
1213
|
+
]
|
|
1028
1214
|
});
|
|
1029
1215
|
};
|
|
1030
1216
|
|
|
1031
1217
|
/**
|
|
1032
1218
|
* Update placeholder text.
|
|
1219
|
+
*
|
|
1033
1220
|
* @param {string} placeholder
|
|
1034
|
-
* @returns {void}
|
|
1035
1221
|
*/
|
|
1036
1222
|
FeelEditor.prototype.setPlaceholder = function(placeholder) {
|
|
1037
1223
|
this._cmEditor.dispatch({
|