diff --git a/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue b/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue index e1708d9851..a543a0375c 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue @@ -16,7 +16,7 @@ @keydown.enter.prevent="selectText" />
- Hint: Use $ to reference columns, e.g: $column_name$. For more, please check out + Hint: Use {} to reference columns, e.g: {column_name}. For more, please check out Formulas.
@@ -97,6 +97,7 @@ import debounce from 'debounce' import jsep from 'jsep' import { UITypes } from 'nocodb-sdk' import formulaList, { validations } from '../../../../../helpers/formulaList' +import curly from '../../../../../helpers/formulaCurlyHook' import { getWordUntilCaret, insertAtCursor } from '@/helpers' import NcAutocompleteTree from '@/helpers/NcAutocompleteTree' @@ -161,6 +162,7 @@ export default { }, created() { this.formula = { value: this.value || '' } + jsep.plugins.register(curly) }, methods: { async save() { @@ -219,6 +221,7 @@ export default { } }, validateAgainstMeta(pt, arr = []) { + console.log('pt.type= ' + pt.type) if (pt.type === 'CallExpression') { if (!this.availableFunctions.includes(pt.callee.name)) { arr.push(`'${pt.callee.name}' function is not available`) @@ -235,11 +238,7 @@ export default { } pt.arguments.map(arg => this.validateAgainstMeta(arg, arr)) } else if (pt.type === 'Identifier') { - // $column_name$ -> column_name - const sanitizedPtName = pt.name.substring(1, pt.name.length - 1) - if (this.meta.columns.filter(c => !this.column || this.column.id !== c.id).find(c => c.title === pt.name)) { - arr.push(`Column '${pt.name}' needs to be wrapped by $. Try $${pt.name}$ instead`) - } else if (this.meta.columns.filter(c => !this.column || this.column.id !== c.id).every(c => c.title !== sanitizedPtName)) { + if (this.meta.columns.filter(c => !this.column || this.column.id !== c.id).every(c => c.title !== pt.name)) { arr.push(`Column '${pt.name}' is not available`) } } else if (pt.type === 'BinaryExpression') { @@ -257,7 +256,7 @@ export default { if (it.type === 'function') { this.$set(this.formula, 'value', insertAtCursor(this.$refs.input.$el.querySelector('input'), text, len, 1)) } else if (it.type === 'column') { - this.$set(this.formula, 'value', insertAtCursor(this.$refs.input.$el.querySelector('input'), '$' + text + '$', len)) + this.$set(this.formula, 'value', insertAtCursor(this.$refs.input.$el.querySelector('input'), '{' + text + '}', len)) } else { this.$set(this.formula, 'value', insertAtCursor(this.$refs.input.$el.querySelector('input'), text, len)) } diff --git a/packages/nc-gui/helpers/formulaCurlyHook.js b/packages/nc-gui/helpers/formulaCurlyHook.js new file mode 100644 index 0000000000..3661b088f6 --- /dev/null +++ b/packages/nc-gui/helpers/formulaCurlyHook.js @@ -0,0 +1,24 @@ +const OCURLY_CODE = 123; // { +const CCURLY_CODE = 125; // } + +export default { + name: 'curly', + init(jsep) { + jsep.hooks.add('gobble-token', function gobbleCurlyLiteral(env) { + const { context } = env + if (!jsep.isIdentifierStart(context.code) && context.code === OCURLY_CODE) { + context.index += 1 + let nodes = context.gobbleExpressions(CCURLY_CODE) + if (context.code === CCURLY_CODE) { + context.index += 1 + if (nodes.length > 0) { + env.node = nodes[0] + } + return env.node + } else { + context.throwError('Unclosed }') + } + } + }); + } +} \ No newline at end of file diff --git a/packages/nc-gui/package-lock.json b/packages/nc-gui/package-lock.json index 31f19b9f86..66b60bcf64 100644 --- a/packages/nc-gui/package-lock.json +++ b/packages/nc-gui/package-lock.json @@ -22,7 +22,7 @@ "fix-path": "^3.0.0", "httpsnippet": "^2.0.0", "inflection": "^1.12.0", - "jsep": "^0.4.0", + "jsep": "^1.3.6", "material-design-icons-iconfont": "^5.0.1", "monaco-editor": "^0.19.3", "monaco-themes": "^0.2.5", @@ -76,7 +76,7 @@ "license": "MIT", "dependencies": { "axios": "^0.21.1", - "jsep": "^0.4.0" + "jsep": "^1.3.6" }, "devDependencies": { "@ava/typescript": "^1.1.1", @@ -9952,9 +9952,9 @@ } }, "node_modules/jsep": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.4.0.tgz", - "integrity": "sha512-UDkrzhJK8hmgXeGK8WIiecc/cuW4Vnx5nnrRma7yaxK0WXlvZ4VerGrcxPzifd/CA6QdcI1hpXqr22tHKXpcQA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz", + "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==", "engines": { "node": ">= 10.16.0" } @@ -24714,9 +24714,9 @@ } }, "jsep": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.4.0.tgz", - "integrity": "sha512-UDkrzhJK8hmgXeGK8WIiecc/cuW4Vnx5nnrRma7yaxK0WXlvZ4VerGrcxPzifd/CA6QdcI1hpXqr22tHKXpcQA==" + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz", + "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==" }, "jsesc": { "version": "2.5.2", @@ -25389,7 +25389,7 @@ "eslint-plugin-import": "^2.22.0", "eslint-plugin-prettier": "^4.0.0", "gh-pages": "^3.1.0", - "jsep": "^0.4.0", + "jsep": "^1.3.6", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", "open-cli": "^6.0.1", diff --git a/packages/nc-gui/package.json b/packages/nc-gui/package.json index 9305f5b655..057b4ded37 100644 --- a/packages/nc-gui/package.json +++ b/packages/nc-gui/package.json @@ -25,7 +25,7 @@ "fix-path": "^3.0.0", "httpsnippet": "^2.0.0", "inflection": "^1.12.0", - "jsep": "^0.4.0", + "jsep": "^1.3.6", "material-design-icons-iconfont": "^5.0.1", "monaco-editor": "^0.19.3", "monaco-themes": "^0.2.5", diff --git a/packages/nocodb-sdk/package-lock.json b/packages/nocodb-sdk/package-lock.json index 2d1bb01701..1b51d927c4 100644 --- a/packages/nocodb-sdk/package-lock.json +++ b/packages/nocodb-sdk/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "axios": "^0.21.1", - "jsep": "^0.4.0" + "jsep": "^1.3.6" }, "devDependencies": { "@ava/typescript": "^1.1.1", @@ -6764,9 +6764,9 @@ } }, "node_modules/jsep": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.4.0.tgz", - "integrity": "sha512-UDkrzhJK8hmgXeGK8WIiecc/cuW4Vnx5nnrRma7yaxK0WXlvZ4VerGrcxPzifd/CA6QdcI1hpXqr22tHKXpcQA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz", + "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==", "engines": { "node": ">= 10.16.0" } @@ -15535,9 +15535,9 @@ } }, "jsep": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.4.0.tgz", - "integrity": "sha512-UDkrzhJK8hmgXeGK8WIiecc/cuW4Vnx5nnrRma7yaxK0WXlvZ4VerGrcxPzifd/CA6QdcI1hpXqr22tHKXpcQA==" + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz", + "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==" }, "jsesc": { "version": "2.5.2", diff --git a/packages/nocodb-sdk/package.json b/packages/nocodb-sdk/package.json index 96a2dbeace..985cfa4649 100644 --- a/packages/nocodb-sdk/package.json +++ b/packages/nocodb-sdk/package.json @@ -44,7 +44,7 @@ }, "dependencies": { "axios": "^0.21.1", - "jsep": "^0.4.0" + "jsep": "^1.3.6" }, "devDependencies": { "@ava/typescript": "^1.1.1", diff --git a/packages/nocodb-sdk/src/lib/formulaHelpers.ts b/packages/nocodb-sdk/src/lib/formulaHelpers.ts index a1f97b20a3..1472f4fa40 100644 --- a/packages/nocodb-sdk/src/lib/formulaHelpers.ts +++ b/packages/nocodb-sdk/src/lib/formulaHelpers.ts @@ -1,6 +1,7 @@ import jsep from 'jsep'; import { ColumnType } from './Api'; + export function substituteColumnIdWithAliasInFormula( formula, columns: ColumnType[], @@ -23,15 +24,39 @@ export function substituteColumnIdWithAliasInFormula( c.title === colNameOrId ); pt.name = column?.title || ptRaw?.name || pt?.name; - if (pt.name[0] != '$' && pt.name[pt.name.length - 1] != '$') { - pt.name = '$' + pt.name + '$'; - } } else if (pt.type === 'BinaryExpression') { substituteId(pt.left, ptRaw?.left); substituteId(pt.right, ptRaw?.right); } }; + // register curly hook + jsep.plugins.register({ + name: 'curly', + init(jsep) { + jsep.hooks.add('gobble-token', function gobbleCurlyLiteral(env) { + const OCURLY_CODE = 123; // { + const CCURLY_CODE = 125; // } + const { context } = env; + if ( + !jsep.isIdentifierStart(context.code) && + context.code === OCURLY_CODE + ) { + context.index += 1; + const nodes = context.gobbleExpressions(CCURLY_CODE); + if (context.code === CCURLY_CODE) { + context.index += 1; + if (nodes.length > 0) { + env.node = nodes[0]; + } + return env.node; + } else { + context.throwError('Unclosed }'); + } + } + }); + }, + } as jsep.IPlugin); const parsedFormula = jsep(formula); const parsedRawFormula = rawFormula && jsep(rawFormula); substituteId(parsedFormula, parsedRawFormula); @@ -65,7 +90,7 @@ export function jsepTreeToFormula(node) { } if (node.type === 'Identifier') { - return node.name; + return '{' + node.name + '}'; } if (node.type === 'Literal') { diff --git a/packages/nocodb/package-lock.json b/packages/nocodb/package-lock.json index 9b7f90dce5..4425d71f66 100644 --- a/packages/nocodb/package-lock.json +++ b/packages/nocodb/package-lock.json @@ -52,7 +52,7 @@ "ioredis-mock": "^7.1.0", "is-docker": "^2.2.1", "js-beautify": "^1.11.0", - "jsep": "^0.4.0", + "jsep": "^1.3.6", "json2csv": "^5.0.6", "jsonfile": "^6.1.0", "jsonwebtoken": "^8.5.1", @@ -160,7 +160,7 @@ "license": "MIT", "dependencies": { "axios": "^0.21.1", - "jsep": "^0.4.0" + "jsep": "^1.3.6" }, "devDependencies": { "@ava/typescript": "^1.1.1", @@ -13834,9 +13834,9 @@ "optional": true }, "node_modules/jsep": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.4.0.tgz", - "integrity": "sha512-UDkrzhJK8hmgXeGK8WIiecc/cuW4Vnx5nnrRma7yaxK0WXlvZ4VerGrcxPzifd/CA6QdcI1hpXqr22tHKXpcQA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz", + "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==", "engines": { "node": ">= 10.16.0" } @@ -35846,9 +35846,9 @@ "optional": true }, "jsep": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/jsep/-/jsep-0.4.0.tgz", - "integrity": "sha512-UDkrzhJK8hmgXeGK8WIiecc/cuW4Vnx5nnrRma7yaxK0WXlvZ4VerGrcxPzifd/CA6QdcI1hpXqr22tHKXpcQA==" + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz", + "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==" }, "jsesc": { "version": "2.5.2", @@ -37849,7 +37849,7 @@ "eslint-plugin-import": "^2.22.0", "eslint-plugin-prettier": "^4.0.0", "gh-pages": "^3.1.0", - "jsep": "^0.4.0", + "jsep": "^1.3.6", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", "open-cli": "^6.0.1", diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json index cc2cae2723..67778b1c7d 100644 --- a/packages/nocodb/package.json +++ b/packages/nocodb/package.json @@ -134,7 +134,7 @@ "ioredis-mock": "^7.1.0", "is-docker": "^2.2.1", "js-beautify": "^1.11.0", - "jsep": "^0.4.0", + "jsep": "^1.3.6", "json2csv": "^5.0.6", "jsonfile": "^6.1.0", "jsonwebtoken": "^8.5.1", diff --git a/packages/nocodb/src/lib/noco/meta/helpers/formulaHelpers.ts b/packages/nocodb/src/lib/noco/meta/helpers/formulaHelpers.ts index f49927f09c..b87441f351 100644 --- a/packages/nocodb/src/lib/noco/meta/helpers/formulaHelpers.ts +++ b/packages/nocodb/src/lib/noco/meta/helpers/formulaHelpers.ts @@ -14,53 +14,79 @@ export async function substituteColumnAliasWithIdInFormula( } else if (pt.type === 'Literal') { return; } else if (pt.type === 'Identifier') { - const sanitizedPtName = pt.name.substring(1, pt.name.length - 1); - const colNameOrId = sanitizedPtName; + const colNameOrId = pt.name; const column = columns.find( c => c.id === colNameOrId || c.column_name === colNameOrId || c.title === colNameOrId ); - pt.name = column.id; + pt.name = '{' + column.id + '}'; } else if (pt.type === 'BinaryExpression') { await substituteId(pt.left); await substituteId(pt.right); } }; - + // register curly hook + jsep.plugins.register({ + name: 'curly', + init(jsep) { + jsep.hooks.add('gobble-token', function gobbleCurlyLiteral(env) { + const OCURLY_CODE = 123; // { + const CCURLY_CODE = 125; // } + const { context } = env; + if ( + !jsep.isIdentifierStart(context.code) && + context.code === OCURLY_CODE + ) { + context.index += 1; + const nodes = context.gobbleExpressions(CCURLY_CODE); + if (context.code === CCURLY_CODE) { + context.index += 1; + if (nodes.length > 0) { + env.node = nodes[0]; + } + return env.node; + } else { + context.throwError('Unclosed }'); + } + } + }); + } + } as jsep.IPlugin); const parsedFormula = jsep(formula); await substituteId(parsedFormula); return jsepTreeToFormula(parsedFormula); } -export function substituteColumnIdWithAliasInFormula( - formula, - columns: Column[] -) { - const substituteId = (pt: any) => { - if (pt.type === 'CallExpression') { - for (const arg of pt.arguments || []) { - substituteId(arg); - } - } else if (pt.type === 'Literal') { - return; - } else if (pt.type === 'Identifier') { - const colNameOrId = pt.name; - const column = columns.find( - c => - c.id === colNameOrId || - c.column_name === colNameOrId || - c.title === colNameOrId - ); - pt.name = column.id; - } else if (pt.type === 'BinaryExpression') { - substituteId(pt.left); - substituteId(pt.right); - } - }; - - const parsedFormula = jsep(formula); - substituteId(parsedFormula); - return jsepTreeToFormula(parsedFormula); -} +// not in use +// export function substituteColumnIdWithAliasInFormula( +// formula, +// columns: Column[] +// ) { +// const substituteId = (pt: any) => { +// if (pt.type === 'CallExpression') { +// for (const arg of pt.arguments || []) { +// substituteId(arg); +// } +// } else if (pt.type === 'Literal') { +// return; +// } else if (pt.type === 'Identifier') { +// const colNameOrId = pt.name; +// const column = columns.find( +// c => +// c.id === colNameOrId || +// c.column_name === colNameOrId || +// c.title === colNameOrId +// ); +// pt.name = column.id; +// } else if (pt.type === 'BinaryExpression') { +// substituteId(pt.left); +// substituteId(pt.right); +// } +// }; +// +// const parsedFormula = jsep(formula); +// substituteId(parsedFormula); +// return jsepTreeToFormula(parsedFormula); +// }