@bpmn-io/feel-editor 1.4.0 → 1.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/README.md +10 -1
- package/dist/index.es.js +555 -354
- package/dist/index.js +567 -366
- 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] = new Function();
|
|
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)",
|
|
@@ -496,379 +969,75 @@ var tags = [
|
|
|
496
969
|
},
|
|
497
970
|
{
|
|
498
971
|
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
|
-
};
|
|
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();
|
|
1039
|
+
const placeholderConf = new state.Compartment();
|
|
1040
|
+
|
|
872
1041
|
|
|
873
1042
|
/**
|
|
874
1043
|
* Creates a FEEL editor in the supplied container
|
|
@@ -876,6 +1045,7 @@ const autocompletionConf = new state.Compartment();
|
|
|
876
1045
|
* @param {Object} config
|
|
877
1046
|
* @param {DOMNode} config.container
|
|
878
1047
|
* @param {Extension[]} [config.extensions]
|
|
1048
|
+
* @param {Dialect} [config.dialect='expression']
|
|
879
1049
|
* @param {DOMNode|String} [config.tooltipContainer]
|
|
880
1050
|
* @param {Function} [config.onChange]
|
|
881
1051
|
* @param {Function} [config.onKeyDown]
|
|
@@ -883,19 +1053,23 @@ const autocompletionConf = new state.Compartment();
|
|
|
883
1053
|
* @param {Boolean} [config.readOnly]
|
|
884
1054
|
* @param {String} [config.value]
|
|
885
1055
|
* @param {Variable[]} [config.variables]
|
|
1056
|
+
* @param {Variable[]} [config.builtins]
|
|
886
1057
|
*
|
|
887
1058
|
* @returns {Object} editor
|
|
888
1059
|
*/
|
|
889
1060
|
function FeelEditor({
|
|
890
1061
|
extensions: editorExtensions = [],
|
|
1062
|
+
dialect = 'expression',
|
|
891
1063
|
container,
|
|
892
1064
|
contentAttributes = {},
|
|
893
1065
|
tooltipContainer,
|
|
894
1066
|
onChange = () => {},
|
|
895
1067
|
onKeyDown = () => {},
|
|
896
1068
|
onLint = () => {},
|
|
1069
|
+
placeholder = '',
|
|
897
1070
|
readOnly = false,
|
|
898
1071
|
value = '',
|
|
1072
|
+
builtins = camunda,
|
|
899
1073
|
variables = []
|
|
900
1074
|
}) {
|
|
901
1075
|
|
|
@@ -936,21 +1110,25 @@ function FeelEditor({
|
|
|
936
1110
|
}) : [];
|
|
937
1111
|
|
|
938
1112
|
const extensions = [
|
|
939
|
-
|
|
940
|
-
|
|
1113
|
+
autocomplete.autocompletion(),
|
|
1114
|
+
coreConf.of(configure({
|
|
1115
|
+
dialect,
|
|
1116
|
+
builtins,
|
|
1117
|
+
variables
|
|
1118
|
+
})),
|
|
941
1119
|
language$1.bracketMatching(),
|
|
942
|
-
|
|
1120
|
+
language$1.indentOnInput(),
|
|
943
1121
|
autocomplete.closeBrackets(),
|
|
944
1122
|
view.EditorView.contentAttributes.of(contentAttributes),
|
|
945
|
-
|
|
1123
|
+
changeHandler,
|
|
946
1124
|
keyHandler,
|
|
947
1125
|
view.keymap.of([
|
|
948
1126
|
...commands.defaultKeymap,
|
|
949
1127
|
]),
|
|
950
|
-
language(),
|
|
951
1128
|
linter,
|
|
952
1129
|
lintHandler,
|
|
953
1130
|
tooltipLayout,
|
|
1131
|
+
placeholderConf.of(view.placeholder(placeholder)),
|
|
954
1132
|
theme,
|
|
955
1133
|
...editorExtensions
|
|
956
1134
|
];
|
|
@@ -962,7 +1140,7 @@ function FeelEditor({
|
|
|
962
1140
|
this._cmEditor = new view.EditorView({
|
|
963
1141
|
state: state.EditorState.create({
|
|
964
1142
|
doc: value,
|
|
965
|
-
extensions
|
|
1143
|
+
extensions
|
|
966
1144
|
}),
|
|
967
1145
|
parent: container
|
|
968
1146
|
});
|
|
@@ -1015,12 +1193,35 @@ FeelEditor.prototype.getSelection = function() {
|
|
|
1015
1193
|
|
|
1016
1194
|
/**
|
|
1017
1195
|
* Set variables to be used for autocompletion.
|
|
1196
|
+
*
|
|
1018
1197
|
* @param {Variable[]} variables
|
|
1019
|
-
* @returns {void}
|
|
1020
1198
|
*/
|
|
1021
1199
|
FeelEditor.prototype.setVariables = function(variables) {
|
|
1200
|
+
|
|
1201
|
+
const {
|
|
1202
|
+
dialect,
|
|
1203
|
+
builtins
|
|
1204
|
+
} = get(this._cmEditor.state);
|
|
1205
|
+
|
|
1206
|
+
this._cmEditor.dispatch({
|
|
1207
|
+
effects: [
|
|
1208
|
+
coreConf.reconfigure(configure({
|
|
1209
|
+
dialect,
|
|
1210
|
+
builtins,
|
|
1211
|
+
variables
|
|
1212
|
+
}))
|
|
1213
|
+
]
|
|
1214
|
+
});
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* Update placeholder text.
|
|
1219
|
+
*
|
|
1220
|
+
* @param {string} placeholder
|
|
1221
|
+
*/
|
|
1222
|
+
FeelEditor.prototype.setPlaceholder = function(placeholder) {
|
|
1022
1223
|
this._cmEditor.dispatch({
|
|
1023
|
-
effects:
|
|
1224
|
+
effects: placeholderConf.reconfigure(view.placeholder(placeholder))
|
|
1024
1225
|
});
|
|
1025
1226
|
};
|
|
1026
1227
|
|