feat(editor): Introduce proxy completions to expressions (#5075)

*  Introduce proxy completions to expressions

* 🧪 Add tests

*  Replace snippet with alphabetic char completions

*  Tighten `DateTime` check

* 🧹 Clean up `n8nLang`

* 🔥 Remove duplicate

* 👕 Remove non-null assertion

*  Confirm that `overlay` is needed

* 🔥 Remove comment

* 🔥 Remove more unneeded code

* 🔥 Remove unneded Pinia setup

*  Simplify syntax
This commit is contained in:
Iván Ovejero
2023-01-06 10:07:36 +01:00
committed by GitHub
parent 77031a2950
commit f4140d011f
30 changed files with 1391 additions and 520 deletions

View File

@@ -8,7 +8,7 @@ import {
lineNumbers,
} from '@codemirror/view';
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
import { acceptCompletion, closeBrackets } from '@codemirror/autocomplete';
import { acceptCompletion } from '@codemirror/autocomplete';
import {
history,
indentWithTab,
@@ -16,11 +16,8 @@ import {
toggleComment,
} from '@codemirror/commands';
import { lintGutter } from '@codemirror/lint';
import type { Extension } from '@codemirror/state';
import { customInputHandler } from './inputHandler';
const [_, bracketState] = closeBrackets() as readonly Extension[];
import { codeInputHandler } from '@/plugins/codemirror/inputHandlers/code.inputHandler';
export const baseExtensions = [
lineNumbers(),
@@ -29,7 +26,7 @@ export const baseExtensions = [
history(),
foldGutter(),
lintGutter(),
[customInputHandler, bracketState],
codeInputHandler(),
dropCursor(),
indentOnInput(),
bracketMatching(),

View File

@@ -54,7 +54,7 @@ export const completerExtension = mixins(
// luxon
this.todayCompletions,
this.nowCompletions,
this.dateTimeCompltions,
this.dateTimeCompletions,
// item index
this.inputCompletions,
@@ -174,7 +174,7 @@ export const completerExtension = mixins(
if (value === '$now') return this.nowCompletions(context, variable);
if (value === '$today') return this.todayCompletions(context, variable);
if (value === 'DateTime') return this.dateTimeCompltions(context, variable);
if (value === 'DateTime') return this.dateTimeCompletions(context, variable);
// item index

View File

@@ -1,11 +1,12 @@
import Vue from 'vue';
import { isAllowedInDotNotation, escape, toVariableOption } from '../utils';
import { escape, toVariableOption } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { IDataObject, IPinData, IRunData } from 'n8n-workflow';
import type { CodeNodeEditorMixin } from '../types';
import { mapStores } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows';
import { useNDVStore } from '@/stores/ndv';
import { isAllowedInDotNotation } from '@/plugins/codemirror/completions/utils';
export const jsonFieldCompletions = (Vue as CodeNodeEditorMixin).extend({
computed: {

View File

@@ -76,7 +76,7 @@ export const luxonCompletions = (Vue as CodeNodeEditorMixin).extend({
/**
* Complete `DateTime` with luxon `DateTime` static methods.
*/
dateTimeCompltions(context: CompletionContext, matcher = 'DateTime'): CompletionResult | null {
dateTimeCompletions(context: CompletionContext, matcher = 'DateTime'): CompletionResult | null {
const pattern = new RegExp(`${escape(matcher)}\..*`);
const preCursor = context.matchBefore(pattern);

View File

@@ -1,42 +0,0 @@
import { completionStatus, insertBracket } from '@codemirror/autocomplete';
import { codePointAt, codePointSize } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
/**
* Customized input handler to prevent token autoclosing in certain cases.
*
* Based on: https://github.com/codemirror/closebrackets/blob/0a56edfaf2c6d97bc5e88f272de0985b4f41e37a/src/closebrackets.ts#L79
*/
export const customInputHandler = EditorView.inputHandler.of((view, from, to, insert) => {
if (view.composing || view.state.readOnly) return false;
// customization: do not autoclose tokens during autocompletion
if (completionStatus(view.state) !== null) return false;
const selection = view.state.selection.main;
// customization: do not autoclose square brackets prior to `.json`
if (
insert === '[' &&
view.state.doc.toString().slice(selection.from - '.json'.length, selection.to) === '.json'
) {
return false;
}
if (
insert.length > 2 ||
(insert.length === 2 && codePointSize(codePointAt(insert, 0)) === 1) ||
from !== selection.from ||
to !== selection.to
) {
return false;
}
const transaction = insertBracket(view.state, insert);
if (!transaction) return false;
view.dispatch(transaction);
return true;
});

View File

@@ -29,12 +29,6 @@ export function walk<T extends RangeNode>(
return found as T[];
}
export const isAllowedInDotNotation = (str: string) => {
const DOT_NOTATION_BANNED_CHARS = /^(\d)|[\\ `!@#$%^&*()_+\-=[\]{};':"\\|,.<>?~]/g;
return !DOT_NOTATION_BANNED_CHARS.test(str);
};
export const escape = (str: string) =>
str
.replace('$', '\\$')