Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
dist
coverage
coverage
*.tsbuildinfo
143 changes: 143 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
"main": "dist/index.js",
"module": "dist/index.es.js",
"scripts": {
"all": "run-s lint test build",
"all": "run-s lint check-types test build",
"test": "karma start",
"build": "rollup -c --bundleConfigAsCjs",
"build:watch": "npm run build -- -w",
"lint": "eslint .",
"check-types": "run-s check-types:*",
"check-types:src": "tsc",
"check-types:test": "tsc -p test",
"start": "cross-env SINGLE_START=true npm run dev",
"start:camunda": "cross-env SINGLE_START=camunda npm run dev",
"dev": "npm test -- --auto-watch --no-single-run",
Expand Down Expand Up @@ -46,6 +49,7 @@
"license": "MIT",
"dependencies": {
"@bpmn-io/feel-lint": "^2.1.0",
"@bpmn-io/lang-feel": "^2.4.0",
"@camunda/feel-builtins": "^0.2.0",
"@codemirror/autocomplete": "^6.16.2",
"@codemirror/commands": "^6.8.0",
Expand All @@ -54,7 +58,6 @@
"@codemirror/state": "^6.5.1",
"@codemirror/view": "^6.36.2",
"@lezer/highlight": "^1.2.1",
"@bpmn-io/lang-feel": "^2.4.0",
"min-dom": "^4.2.1"
},
"devDependencies": {
Expand All @@ -63,6 +66,10 @@
"@rollup/plugin-json": "^6.1.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/user-event": "^14.6.1",
"@types/chai": "^5.2.3",
"@types/mocha": "^10.0.10",
"@types/sinon": "^17.0.4",
"@types/sinon-chai": "^4.0.0",
"babel-loader": "^9.2.1",
"babel-plugin-istanbul": "^7.0.0",
"chai": "^4.5.0",
Expand All @@ -85,6 +92,7 @@
"rollup": "^4.31.0",
"sinon": "^17.0.1",
"sinon-chai": "^3.7.0",
"typescript": "^5.9.3",
"webpack": "^5.97.1"
}
}
3 changes: 2 additions & 1 deletion src/autocompletion/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { snippets, keywordCompletions } from '@bpmn-io/lang-feel';
import { completeFromList } from '@codemirror/autocomplete';

import { pathExpressionCompletion } from './pathExpression';
import { variableCompletion } from './variable';
Expand All @@ -21,7 +22,7 @@ export function completions({ variables = [], builtins = [] }) {
return [
pathExpressionCompletion({ variables }),
variableCompletion({ variables, builtins }),
snippets,
completeFromList(snippets),
...keywordCompletions
];
}
23 changes: 12 additions & 11 deletions src/autocompletion/pathExpression.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function pathExpressionCompletion({ variables }) {
const nodeBefore = syntaxTree(context.state).resolve(context.pos, -1);

if (!isPathExpression(nodeBefore)) {
return;
return null;
}

const expression = findPathExpression(nodeBefore);
Expand All @@ -41,26 +41,27 @@ export function pathExpressionCompletion({ variables }) {
// only suggest if variable type matches
if (
childVar.isList !== 'optional' &&
!!childVar.isList !== path[i].isList
!!childVar.isList !== path[i].isList
) {
return;
return null;
}

options = childVar.entries;
}

if (!options) return;
if (!options) return null;

options = options.map(v => ({
label: v.name,
type: 'variable',
info: v.info,
detail: v.detail
}));
const completionOptions = options.map(option => (
{
label: option.name,
type: 'variable',
info: option.info,
detail: option.detail
}));

const result = {
from: from,
options: options
options: completionOptions
};

return result;
Expand Down
4 changes: 2 additions & 2 deletions src/autocompletion/variable.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ function getVariableSuggestions(variables, builtins) {

/**
* @param {import('..').Variable} variable
* @param {number} boost

* @param {number} [boost]
*
* @returns {import('@codemirror/autocomplete').Completion}
*/
function createVariableSuggestion(variable, boost) {
Expand Down
8 changes: 4 additions & 4 deletions src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import {
/**
* @typedef {object} Variable
* @property {string} name name or key of the variable
* @property {string} [info] short information about the variable, e.g. type
* @property {string | (() => HTMLElement)} [info] short information about the variable, e.g. type
* @property {string} [detail] longer description of the variable content
* @property {boolean} [isList] whether the variable is a list
* @property {Array<Variable>} [schema] array of child variables if the variable is a context or list
* @property {boolean|'optional'} [isList] whether the variable is a list
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea of what it means that isList is "optional"?

Copy link
Member Author

@Buckwich Buckwich Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not quite. the tests are using both boolean values and 'optional'. I didn't want to adjust the implementation (for now) and just fix the docs to reflect the implementation (unless its obviously broken).

optional is checked here: https://github.com/bpmn-io/feel-editor/blob/39_check-types/src/autocompletion/pathExpression.js#L43

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the answer is in the variable resolver. I will look for an answer in our codebase.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it's coming from variable resolver, and is supposed to handle cases where a variable is created once as a list, and once as a non-list: https://github.com/bpmn-io/variable-resolver/blob/e69f839538a8368024dfcbfcef7e56e64b53713a/lib/base/VariableResolver.js#L346
So essentially variable: T[] | U

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meaning: The variable can be a list, but does not have to.

* @property {Array<Variable>} [entries] array of child variables if the variable is a context or list
* @property {'function'|'variable'} [type] type of the variable
* @property {Array<{name: string, type: string}>} [params] function parameters
* @property {Array<{name: string, type?: string}>} [params] function parameters
*/

/**
Expand Down
10 changes: 10 additions & 0 deletions src/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Global type definitions for FEEL Editor

import { Extension as CodeMirrorExtension } from "@codemirror/state";

declare global {
type DOMNode = HTMLElement;
type Extension = CodeMirrorExtension;
}

export {};
Loading