Comparing version 1.1.0 to 2.0.0
@@ -1,14 +0,3 @@ | ||
import { LRLanguage, LanguageSupport } from '@codemirror/language'; | ||
import { Completion } from '@codemirror/autocomplete'; | ||
declare const feelLanguage: LRLanguage; | ||
declare const unaryTestsLanguage: LRLanguage; | ||
declare const expressionLanguage: LRLanguage; | ||
declare function feel(config?: { | ||
dialect?: 'expression' | 'unaryTests'; | ||
context?: Record<string, any>; | ||
}): LanguageSupport; | ||
declare const snippets: readonly Completion[]; | ||
export { expressionLanguage, feel, feelLanguage, snippets, unaryTestsLanguage }; | ||
export { feel, feelLanguage, expressionLanguage, unaryTestsLanguage } from './feel'; | ||
export { snippets } from './snippets'; | ||
export * from './completion'; |
import { parser, trackVariables } from 'lezer-feel'; | ||
import { LRLanguage, indentNodeProp, delimitedIndent, continuedIndent, foldNodeProp, foldInside, LanguageSupport } from '@codemirror/language'; | ||
import { snippetCompletion, ifNotIn, completeFromList } from '@codemirror/autocomplete'; | ||
import { syntaxTree, LRLanguage, indentNodeProp, delimitedIndent, continuedIndent, foldNodeProp, foldInside, LanguageSupport } from '@codemirror/language'; | ||
import { snippetCompletion as snippetCompletion$1, completeFromList, ifNotIn } from '@codemirror/autocomplete'; | ||
// / A collection of FEEL-related | ||
// / [snippets](#autocomplete.snippet). | ||
const snippets = [ | ||
/*@__PURE__*/snippetCompletion('function(${params}) ${body}', { | ||
label: 'function', | ||
detail: 'definition', | ||
type: 'keyword' | ||
}), | ||
/*@__PURE__*/snippetCompletion('for ${var} in ${collection} return ${value}', { | ||
label: 'for', | ||
detail: 'expression', | ||
type: 'keyword' | ||
}), | ||
/*@__PURE__*/snippetCompletion('every ${var} in ${collection} satisfies ${condition}', { | ||
label: 'every', | ||
detail: 'quantified expression', | ||
type: 'keyword' | ||
}), | ||
/*@__PURE__*/snippetCompletion('some ${var} in ${collection} satisfies ${condition}', { | ||
label: 'some', | ||
detail: 'quantified expression', | ||
type: 'keyword' | ||
}), | ||
/*@__PURE__*/snippetCompletion('if ${condition} then ${value} else ${other value}', { | ||
label: 'if', | ||
detail: 'block', | ||
type: 'keyword' | ||
}) | ||
]; | ||
/** | ||
* A collection of FEEL-related [snippets](#autocomplete.snippet). | ||
*/ | ||
const snippets = [snippetCompletion$1('function(${params}) ${body}', { | ||
label: 'function', | ||
detail: 'definition', | ||
type: 'keyword' | ||
}), snippetCompletion$1('for ${var} in ${collection} return ${value}', { | ||
label: 'for', | ||
detail: 'expression', | ||
type: 'keyword' | ||
}), snippetCompletion$1('every ${var} in ${collection} satisfies ${condition}', { | ||
label: 'every', | ||
detail: 'quantified expression', | ||
type: 'keyword' | ||
}), snippetCompletion$1('some ${var} in ${collection} satisfies ${condition}', { | ||
label: 'some', | ||
detail: 'quantified expression', | ||
type: 'keyword' | ||
}), snippetCompletion$1('if ${condition} then ${value} else ${other value}', { | ||
label: 'if', | ||
detail: 'block', | ||
type: 'keyword' | ||
}), snippetCompletion$1('{ ${key}: ${value} }', { | ||
label: 'context', | ||
detail: 'block', | ||
type: 'keyword' | ||
})]; | ||
// / A language provider based on the [Lezer FEEL | ||
// / parser](https://github.com/nikku/lezer-feel), extended with | ||
// / highlighting and indentation information. | ||
const feelLanguage = /*@__PURE__*/LRLanguage.define({ | ||
parser: /*@__PURE__*/parser.configure({ | ||
props: [ | ||
/*@__PURE__*/indentNodeProp.add({ | ||
'Context': /*@__PURE__*/delimitedIndent({ | ||
closing: '}' | ||
}), | ||
'List FilterExpression': /*@__PURE__*/delimitedIndent({ | ||
closing: ']' | ||
}), | ||
'ParenthesizedExpression FunctionInvocation': /*@__PURE__*/continuedIndent({ | ||
except: /^\s*\)/ | ||
}), | ||
'ForExpression QuantifiedExpression IfExpression': /*@__PURE__*/continuedIndent({ | ||
except: /^\s*(then|else|return|satisfies)\b/ | ||
}), | ||
'FunctionDefinition': /*@__PURE__*/continuedIndent({ | ||
except: /^\s*(\(|\))/ | ||
}) | ||
}), | ||
/*@__PURE__*/foldNodeProp.add({ | ||
Context: foldInside, | ||
List: foldInside, | ||
ParenthesizedExpression: foldInside, | ||
FunctionDefinition(node) { | ||
const last = node.getChild(')'); | ||
if (!last) | ||
return null; | ||
return { | ||
from: last.to, | ||
to: node.to | ||
}; | ||
} | ||
}) | ||
] | ||
}), | ||
languageData: { | ||
indentOnInput: /^\s*(\)|\}|\]|then|else|return|satisfies)$/, | ||
commentTokens: { | ||
line: '//', | ||
block: { | ||
open: '/*', | ||
close: '*/' | ||
} | ||
function _extends() { | ||
_extends = Object.assign ? Object.assign.bind() : function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
} | ||
function contextualKeyword(options) { | ||
const { | ||
context: nodes, | ||
after, | ||
before, | ||
keyword | ||
} = options; | ||
return ifInside({ | ||
nodes, | ||
before, | ||
after, | ||
keyword | ||
}, completeFromList([{ | ||
label: keyword, | ||
type: 'keyword', | ||
boost: 10 | ||
}])); | ||
} | ||
const keywordCompletions = [contextualKeyword({ | ||
context: 'InExpression', | ||
keyword: 'in' | ||
}), contextualKeyword({ | ||
context: 'IfExpression', | ||
keyword: 'then', | ||
after: 'if', | ||
before: 'else' | ||
}), contextualKeyword({ | ||
context: 'IfExpression', | ||
keyword: 'else', | ||
after: 'then' | ||
}), contextualKeyword({ | ||
context: 'QuantifiedExpression', | ||
keyword: 'satisfies' | ||
}), contextualKeyword({ | ||
context: 'ForExpression', | ||
after: 'InExpressions', | ||
keyword: 'return' | ||
})]; | ||
const dontComplete = ['StringLiteral', 'Identifier', 'LineComment', 'BlockComment']; | ||
function snippetCompletion(snippets) { | ||
return ifNotIn(dontComplete, completeFromList(snippets.map(s => _extends({}, s, { | ||
type: 'text' | ||
})))); | ||
} | ||
function matchLeft(node, position, nodes) { | ||
return matchChildren(node, position, nodes, -1); | ||
} | ||
function matchRight(node, position, nodes) { | ||
return matchChildren(node, position, nodes, 1); | ||
} | ||
function matchChildren(node, position, nodes, direction) { | ||
let child = node[direction > 0 ? 'childAfter' : 'childBefore'](position); | ||
while (child) { | ||
if (nodes.includes(child.name)) { | ||
return child; | ||
} | ||
if (child.type.isError && child.firstChild) { | ||
if (nodes.includes(child.firstChild.name)) { | ||
return child.firstChild; | ||
} | ||
} | ||
child = child[direction > 0 ? 'nextSibling' : 'prevSibling']; | ||
} | ||
return null; | ||
} | ||
function matchUp(node, nodeNames) { | ||
if (!Array.isArray(nodeNames)) { | ||
nodeNames = [nodeNames]; | ||
} | ||
for (; node; node = node.parent) { | ||
if (nodeNames.includes(node.name)) { | ||
return node; | ||
} | ||
if (node.type.isTop) { | ||
break; | ||
} | ||
} | ||
return null; | ||
} | ||
function ifInside(options, source) { | ||
const { | ||
nodes, | ||
before, | ||
after, | ||
keyword | ||
} = options; | ||
return context => { | ||
const { | ||
state, | ||
pos | ||
} = context; | ||
const node = matchUp(syntaxTree(state).resolveInner(pos, -1), nodes); | ||
if (!node) { | ||
return null; | ||
} | ||
if (matchLeft(node, pos, [keyword, before])) { | ||
return null; | ||
} | ||
if (matchRight(node, pos, [keyword, after])) { | ||
return null; | ||
} | ||
if (after && !matchLeft(node, pos, [after])) { | ||
return null; | ||
} | ||
return source(context); | ||
}; | ||
} | ||
/** | ||
* A FEEL language provider based on the | ||
* [Lezer FEEL parser](https://github.com/nikku/lezer-feel), | ||
* extended with highlighting and indentation information. | ||
*/ | ||
const feelLanguage = LRLanguage.define({ | ||
parser: parser.configure({ | ||
props: [indentNodeProp.add({ | ||
'Context': delimitedIndent({ | ||
closing: '}' | ||
}), | ||
'List FilterExpression': delimitedIndent({ | ||
closing: ']' | ||
}), | ||
'ParenthesizedExpression FunctionInvocation': continuedIndent({ | ||
except: /^\s*\)/ | ||
}), | ||
'ForExpression QuantifiedExpression IfExpression': continuedIndent({ | ||
except: /^\s*(then|else|return|satisfies)\b/ | ||
}), | ||
'FunctionDefinition': continuedIndent({ | ||
except: /^\s*(\(|\))/ | ||
}) | ||
}), foldNodeProp.add({ | ||
Context: foldInside, | ||
List: foldInside, | ||
ParenthesizedExpression: foldInside, | ||
FunctionDefinition(node) { | ||
const last = node.getChild(')'); | ||
if (!last) return null; | ||
return { | ||
from: last.to, | ||
to: node.to | ||
}; | ||
} | ||
})] | ||
}), | ||
languageData: { | ||
indentOnInput: /^\s*(\)|\}|\]|then|else|return|satisfies)$/, | ||
commentTokens: { | ||
line: '//', | ||
block: { | ||
open: '/*', | ||
close: '*/' | ||
} | ||
} | ||
} | ||
}); | ||
// / A language provider for TypeScript. | ||
const unaryTestsLanguage = /*@__PURE__*/feelLanguage.configure({ top: 'UnaryTests' }); | ||
// / Language provider for JSX. | ||
const expressionLanguage = /*@__PURE__*/feelLanguage.configure({ top: 'Expression' }); | ||
const keywords = /*@__PURE__*/'return satisfies then in'.split(' ').map(kw => ({ label: kw, type: 'keyword' })); | ||
const dontComplete = [ | ||
'StringLiteral', 'Name', | ||
'LineComment', 'BlockComment' | ||
]; | ||
// / FEEL support. Includes [snippet](#lang-feel.snippets) | ||
// / completion. | ||
/** | ||
* A language provider for FEEL Unary Tests | ||
*/ | ||
const unaryTestsLanguage = feelLanguage.configure({ | ||
top: 'UnaryTests' | ||
}); | ||
/** | ||
* Language provider for FEEL Expression | ||
*/ | ||
const expressionLanguage = feelLanguage.configure({ | ||
top: 'Expression' | ||
}); | ||
/** | ||
* Feel language support for CodeMirror. | ||
* | ||
* Includes [snippet](#lang-feel.snippets) | ||
*/ | ||
function feel(config = {}) { | ||
const lang = config.dialect === 'unaryTests' ? unaryTestsLanguage : expressionLanguage; | ||
const contextualLang = lang.configure({ | ||
contextTracker: trackVariables(config.context) | ||
}); | ||
return new LanguageSupport(contextualLang, [ | ||
feelLanguage.data.of({ | ||
autocomplete: ifNotIn(dontComplete, completeFromList(snippets.concat(keywords))) | ||
}) | ||
]); | ||
const lang = config.dialect === 'unaryTests' ? unaryTestsLanguage : expressionLanguage; | ||
const contextualLang = lang.configure({ | ||
contextTracker: trackVariables(config.context) | ||
}); | ||
const completions = config.completions || [snippetCompletion(snippets), keywordCompletions].flat(); | ||
return new LanguageSupport(contextualLang, [...completions.map(autocomplete => feelLanguage.data.of({ | ||
autocomplete | ||
}))]); | ||
} | ||
export { expressionLanguage, feel, feelLanguage, snippets, unaryTestsLanguage }; | ||
export { contextualKeyword, dontComplete, expressionLanguage, feel, feelLanguage, ifInside, keywordCompletions, matchChildren, matchLeft, matchRight, snippetCompletion, snippets, unaryTestsLanguage }; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "lang-feel", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "FEEL language support for the CodeMirror code editor", | ||
"scripts": { | ||
"all": "run-s lint build test", | ||
"test": "cm-runtests", | ||
"test": "karma start karma.conf.cjs", | ||
"dev": "run-p 'build -- watch' 'test -- --no-single-run --auto-watch'", | ||
"lint": "eslint . --ext ts", | ||
"build": "cm-buildhelper src/index.ts", | ||
"prepare": "npm run build", | ||
"dev": "chokidar 'src/*.ts' 'test/*.ts' --initial -c 'npm run build && npm test'" | ||
"build": "microbundle src/index.ts -f modern,cjs --target node", | ||
"prepare": "npm run build" | ||
}, | ||
@@ -36,16 +36,29 @@ "keywords": [ | ||
"@codemirror/view": "^6.21.0", | ||
"@lezer/common": "^1.1.0", | ||
"@lezer/common": "^1.1.2", | ||
"lezer-feel": "^1.2.0" | ||
}, | ||
"devDependencies": { | ||
"@codemirror/buildhelper": "^1.0.0", | ||
"@lezer/lr": "^1.3.12", | ||
"@typescript-eslint/eslint-plugin": "^6.7.3", | ||
"@typescript-eslint/parser": "^6.7.3", | ||
"chokidar": "^3.5.3", | ||
"chokidar-cli": "^3.0.0", | ||
"eslint": "^8.50.0", | ||
"@types/karma-chai": "^0.1.6", | ||
"@types/karma-mocha": "^1.3.4", | ||
"@types/mocha": "^10.0.6", | ||
"@typescript-eslint/eslint-plugin": "^6.14.0", | ||
"@typescript-eslint/parser": "^6.14.0", | ||
"chai": "^4.3.10", | ||
"codemirror": "^6.0.1", | ||
"eslint": "^8.55.0", | ||
"eslint-plugin-bpmn-io": "^1.0.0", | ||
"karma": "^6.4.2", | ||
"karma-chai": "^0.1.0", | ||
"karma-chrome-launcher": "^3.2.0", | ||
"karma-debug-launcher": "^0.0.5", | ||
"karma-mocha": "^2.0.1", | ||
"karma-webpack": "^5.0.0", | ||
"microbundle": "^0.15.1", | ||
"mocha": "^10.2.0", | ||
"npm-run-all": "^4.1.5", | ||
"typescript": "^5.2.2" | ||
"puppeteer": "^21.6.0", | ||
"ts-loader": "^9.5.1", | ||
"typescript": "^5.3.3", | ||
"webpack": "^5.89.0" | ||
}, | ||
@@ -52,0 +65,0 @@ "repository": { |
@@ -11,2 +11,7 @@ # lang-feel | ||
* [lezer-feel](https://github.com/nikku/lezer-feel) - FEEL grammar | ||
* [feelin](https://github.com/nikku/feelin) - FEEL parser + interpreter | ||
* [feelin](https://github.com/nikku/feelin) - FEEL parser + interpreter | ||
## License | ||
MIT |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
47537
11
511
16
23
1
+ Added@codemirror/state@6.5.1(transitive)
- Removed@codemirror/state@6.5.2(transitive)
Updated@lezer/common@^1.1.2