diff --git a/packages/frontend/editor-ui/src/plugins/codemirror/typescript/client/completions.ts b/packages/frontend/editor-ui/src/plugins/codemirror/typescript/client/completions.ts index 6a6cac82824..9774ae7abff 100644 --- a/packages/frontend/editor-ui/src/plugins/codemirror/typescript/client/completions.ts +++ b/packages/frontend/editor-ui/src/plugins/codemirror/typescript/client/completions.ts @@ -1,5 +1,6 @@ import { escapeMappingString } from '@/utils/mappingUtils'; import { + type CompletionContext, insertCompletionText, pickedCompletion, type Completion, @@ -16,16 +17,21 @@ import { blockCommentSnippet, snippets } from './snippets'; const START_CHARACTERS = ['"', "'", '(', '.', '@']; const START_CHARACTERS_REGEX = /[\.\(\'\"\@]/; -export const typescriptCompletionSource: CompletionSource = async (context) => { - const { worker } = context.state.facet(typescriptWorkerFacet); - +export const matchText = (context: CompletionContext) => { let word = context.matchBefore(START_CHARACTERS_REGEX); - if (!word?.text) { - word = context.matchBefore(/[\"\'].*/); - } if (!word?.text) { word = context.matchBefore(/[\$\w]+/); } + if (!word?.text) { + word = context.matchBefore(/[\"\'].*/); + } + return word; +}; + +export const typescriptCompletionSource: CompletionSource = async (context) => { + const { worker } = context.state.facet(typescriptWorkerFacet); + + const word = matchText(context); const blockComment = context.matchBefore(/\/\*?\*?/); if (blockComment) { @@ -46,7 +52,7 @@ export const typescriptCompletionSource: CompletionSource = async (context) => { if (isGlobal) { options = options .flatMap((opt) => { - if (opt.label === '$') { + if (opt.label === '$()') { return [ opt, ...autocompletableNodeNames().map((name) => ({ diff --git a/packages/frontend/editor-ui/src/plugins/codemirror/typescript/client/tests/completions.test.ts b/packages/frontend/editor-ui/src/plugins/codemirror/typescript/client/tests/completions.test.ts new file mode 100644 index 00000000000..353e731d427 --- /dev/null +++ b/packages/frontend/editor-ui/src/plugins/codemirror/typescript/client/tests/completions.test.ts @@ -0,0 +1,35 @@ +import { matchText } from '../completions'; +import { CompletionContext } from '@codemirror/autocomplete'; +import { EditorState } from '@codemirror/state'; +import { n8nLang } from '@/plugins/codemirror/n8nLang'; + +describe('matchText', () => { + afterEach(() => { + vi.resetAllMocks(); + }); + + it.each([ + { node: '$(|)', expected: '(' }, + { node: '$("|', expected: '"' }, + { node: "$('|", expected: "'" }, + { node: '$(""|', expected: '"' }, + { node: "$('|", expected: "'" }, + { node: '$("|")', expected: '"' }, + { node: "$('|')", expected: "'" }, + { node: '$("|)', expected: '"' }, + { node: '$("No|")', expected: 'No' }, + { node: "$('No|')", expected: 'No' }, + { node: '$("N|")', expected: 'N' }, + { node: "$('N|')", expected: 'N' }, + ])('should match on previous node: $node', ({ node, expected }) => { + const cursorPosition = node.indexOf('|'); + const state = EditorState.create({ + doc: node.replace('|', ''), + selection: { anchor: cursorPosition }, + extensions: [n8nLang()], + }); + const context = new CompletionContext(state, cursorPosition, false); + + expect(matchText(context)?.text).toEqual(expected); + }); +});