Skip to content

Commit

Permalink
feat(editor): Add sections to autocomplete dropdown (#8720)
Browse files Browse the repository at this point in the history
Co-authored-by: Giulio Andreini <andreini@netseven.it>
  • Loading branch information
2 people authored and despairblue committed Mar 11, 2024
1 parent 2990a49 commit 5226e00
Show file tree
Hide file tree
Showing 35 changed files with 1,306 additions and 466 deletions.
4 changes: 3 additions & 1 deletion cypress/e2e/13-pinning.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,7 @@ function setExpressionOnStringValueInSet(expression: string) {
ndv.getters
.inlineExpressionEditorInput()
.clear()
.type(expression, { parseSpecialCharSequences: false });
.type(expression, { parseSpecialCharSequences: false })
// hide autocomplete
.type('{esc}');
}
4 changes: 3 additions & 1 deletion packages/design-system/src/css/_tokens.dark.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
--color-sticky-background-7: var(--prim-gray-740);
--color-sticky-border-7: var(--prim-gray-670);

// Expressions
// Expressions and autocomplete
--color-valid-resolvable-foreground: var(--prim-color-alt-a-tint-300);
--color-valid-resolvable-background: var(--prim-color-alt-a-alpha-025);
--color-invalid-resolvable-foreground: var(--prim-color-alt-c-tint-250);
Expand All @@ -78,6 +78,8 @@
--color-pending-resolvable-background: var(--prim-gray-70-alpha-01);
--color-expression-editor-background: var(--prim-gray-800);
--color-expression-syntax-example: var(--prim-gray-670);
--color-autocomplete-item-selected: var(--prim-color-secondary-tint-200);
--color-autocomplete-section-header-border: var(--prim-gray-540);

// Code
--color-code-tags-string: var(--prim-color-alt-f-tint-150);
Expand Down
6 changes: 3 additions & 3 deletions packages/design-system/src/css/_tokens.scss
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
--color-sticky-background-7: var(--prim-gray-10);
--color-sticky-border-7: var(--prim-gray-120);

// Expressions
// Expressions and autocomplete
--color-valid-resolvable-foreground: var(--prim-color-alt-a);
--color-valid-resolvable-background: var(--prim-color-alt-a-tint-500);
--color-invalid-resolvable-foreground: var(--prim-color-alt-c);
Expand All @@ -111,6 +111,8 @@
--color-pending-resolvable-background: var(--prim-gray-40);
--color-expression-editor-background: var(--prim-gray-0);
--color-expression-syntax-example: var(--prim-gray-40);
--color-autocomplete-item-selected: var(--color-secondary);
--color-autocomplete-section-header-border: var(--color-foreground-light);

// Code
--color-code-tags-string: var(--prim-color-alt-f);
Expand Down Expand Up @@ -138,8 +140,6 @@
--color-code-gutterBackground: var(--prim-gray-0);
--color-code-gutterForeground: var(--prim-gray-320);
--color-code-tags-comment: var(--prim-gray-420);
--color-autocomplete-selected-background: var(--prim-color-alt-e);
--color-autocomplete-selected-font: var(--prim-gray-0);

// Variables
--color-variables-usage-font: var(--color-success);
Expand Down
4 changes: 4 additions & 0 deletions packages/editor-ui/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ declare global {
};
// eslint-disable-next-line @typescript-eslint/naming-convention
Cypress: unknown;

Sentry?: {
captureException: (error: Error, metadata?: unknown) => void;
};
}
}

Expand Down
61 changes: 10 additions & 51 deletions packages/editor-ui/src/components/CodeNodeEditor/baseExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,67 +6,26 @@ import {
highlightSpecialChars,
keymap,
lineNumbers,
type KeyBinding,
} from '@codemirror/view';
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
import { acceptCompletion, selectedCompletion } from '@codemirror/autocomplete';
import {
history,
indentLess,
indentMore,
insertNewlineAndIndent,
toggleComment,
redo,
deleteCharBackward,
undo,
} from '@codemirror/commands';
import { history, toggleComment, deleteCharBackward } from '@codemirror/commands';
import { lintGutter } from '@codemirror/lint';
import { type Extension, Prec } from '@codemirror/state';

import { codeInputHandler } from '@/plugins/codemirror/inputHandlers/code.inputHandler';
import {
autocompleteKeyMap,
enterKeyMap,
historyKeyMap,
tabKeyMap,
} from '@/plugins/codemirror/keymap';

export const readOnlyEditorExtensions: readonly Extension[] = [
lineNumbers(),
EditorView.lineWrapping,
highlightSpecialChars(),
];

export const tabKeyMap: KeyBinding[] = [
{
any(editor, event) {
if (event.key === 'Tab' || (event.key === 'Escape' && selectedCompletion(editor.state))) {
event.stopPropagation();
}

return false;
},
},
{
key: 'Tab',
run: (editor) => {
if (selectedCompletion(editor.state)) {
return acceptCompletion(editor);
}

return indentMore(editor);
},
},
{ key: 'Shift-Tab', run: indentLess },
];

export const enterKeyMap: KeyBinding[] = [
{
key: 'Enter',
run: (editor) => {
if (selectedCompletion(editor.state)) {
return acceptCompletion(editor);
}

return insertNewlineAndIndent(editor);
},
},
];

export const writableEditorExtensions: readonly Extension[] = [
history(),
lintGutter(),
Expand All @@ -79,11 +38,11 @@ export const writableEditorExtensions: readonly Extension[] = [
highlightActiveLineGutter(),
Prec.highest(
keymap.of([
...tabKeyMap,
...tabKeyMap(),
...enterKeyMap,
...autocompleteKeyMap,
...historyKeyMap,
{ key: 'Mod-/', run: toggleComment },
{ key: 'Mod-z', run: undo },
{ key: 'Mod-Shift-z', run: redo },
{ key: 'Backspace', run: deleteCharBackward, shift: deleteCharBackward },
]),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const completerExtension = defineComponent({
}

return autocompletion({
icons: false,
compareCompletions: (a: Completion, b: Completion) => {
if (/\.json$|id$|id['"]\]$/.test(a.label)) return 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@
import { defineComponent } from 'vue';
import { EditorView, keymap } from '@codemirror/view';
import { EditorState, Prec } from '@codemirror/state';
import { history, redo, undo } from '@codemirror/commands';
import { history } from '@codemirror/commands';
import { expressionManager } from '@/mixins/expressionManager';
import { completionManager } from '@/mixins/completionManager';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
import { n8nLang } from '@/plugins/codemirror/n8nLang';
import { n8nAutocompletion, n8nLang } from '@/plugins/codemirror/n8nLang';
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
import { inputTheme } from './theme';
import { forceParse } from '@/utils/forceParse';
import { acceptCompletion, autocompletion } from '@codemirror/autocomplete';
import { completionStatus } from '@codemirror/autocomplete';
import type { IVariableItemSelected } from '@/Interface';
import {
autocompleteKeyMap,
enterKeyMap,
historyKeyMap,
tabKeyMap,
} from '@/plugins/codemirror/keymap';
export default defineComponent({
name: 'ExpressionEditorModalInput',
Expand All @@ -44,25 +50,26 @@ export default defineComponent({
mounted() {
const extensions = [
inputTheme(),
autocompletion(),
Prec.highest(
keymap.of([
{ key: 'Tab', run: acceptCompletion },
...tabKeyMap(),
...historyKeyMap,
...enterKeyMap,
...autocompleteKeyMap,
{
any: (_: EditorView, event: KeyboardEvent) => {
if (event.key === 'Escape') {
any: (view, event) => {
if (event.key === 'Escape' && completionStatus(view.state) === null) {
event.stopPropagation();
this.$emit('close');
}
return false;
},
},
{ key: 'Mod-z', run: undo },
{ key: 'Mod-Shift-z', run: redo },
]),
),
n8nLang(),
n8nAutocompletion(),
history(),
expressionInputHandler(),
EditorView.lineWrapping,
Expand All @@ -71,7 +78,11 @@ export default defineComponent({
EditorView.contentAttributes.of({ 'data-gramm': 'false' }), // disable grammarly
EditorView.domEventHandlers({ scroll: forceParse }),
EditorView.updateListener.of((viewUpdate) => {
if (!this.editor || !viewUpdate.docChanged) return;
if (!this.editor) return;
this.completionStatus = completionStatus(viewUpdate.view.state);
if (!viewUpdate.docChanged) return;
this.editorState = this.editor.state;
Expand Down
27 changes: 16 additions & 11 deletions packages/editor-ui/src/components/HtmlEditor/HtmlEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
</template>

<script lang="ts">
import { autocompletion } from '@codemirror/autocomplete';
import { history, redo, undo } from '@codemirror/commands';
import { history } from '@codemirror/commands';
import {
LanguageSupport,
bracketMatching,
Expand Down Expand Up @@ -39,11 +38,18 @@ import { expressionManager } from '@/mixins/expressionManager';
import { n8nCompletionSources } from '@/plugins/codemirror/completions/addCompletions';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
import type { Range, Section } from './types';
import { nonTakenRanges } from './utils';
import { isEqual } from 'lodash-es';
import {
autocompleteKeyMap,
enterKeyMap,
historyKeyMap,
tabKeyMap,
} from '@/plugins/codemirror/keymap';
import { n8nAutocompletion } from '@/plugins/codemirror/n8nLang';
import { completionStatus } from '@codemirror/autocomplete';
export default defineComponent({
name: 'HtmlEditor',
Expand Down Expand Up @@ -105,17 +111,12 @@ export default defineComponent({
return [
bracketMatching(),
autocompletion(),
n8nAutocompletion(),
this.disableExpressionCompletions ? html() : htmlWithCompletions(),
autoCloseTags,
expressionInputHandler(),
Prec.highest(
keymap.of([
...tabKeyMap,
...enterKeyMap,
{ key: 'Mod-z', run: undo },
{ key: 'Mod-Shift-z', run: redo },
]),
keymap.of([...tabKeyMap(), ...enterKeyMap, ...historyKeyMap, ...autocompleteKeyMap]),
),
indentOnInput(),
codeNodeEditorTheme({
Expand All @@ -135,7 +136,11 @@ export default defineComponent({
EditorView.editable.of(!this.isReadOnly),
EditorState.readOnly.of(this.isReadOnly),
EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
if (!this.editor || !viewUpdate.docChanged) return;
if (!this.editor) return;
this.completionStatus = completionStatus(viewUpdate.view.state);
if (!viewUpdate.docChanged) return;
// Force segments value update by keeping track of editor state
this.editorState = this.editor.state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
</template>

<script lang="ts">
import { acceptCompletion, autocompletion, completionStatus } from '@codemirror/autocomplete';
import { history, redo, undo } from '@codemirror/commands';
import { history } from '@codemirror/commands';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import { EditorView, keymap } from '@codemirror/view';
import type { PropType } from 'vue';
Expand All @@ -13,11 +12,18 @@ import { defineComponent } from 'vue';
import { completionManager } from '@/mixins/completionManager';
import { expressionManager } from '@/mixins/expressionManager';
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
import { n8nLang } from '@/plugins/codemirror/n8nLang';
import { n8nAutocompletion, n8nLang } from '@/plugins/codemirror/n8nLang';
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
import { isEqual } from 'lodash-es';
import type { IDataObject } from 'n8n-workflow';
import { inputTheme } from './theme';
import {
autocompleteKeyMap,
enterKeyMap,
historyKeyMap,
tabKeyMap,
} from '@/plugins/codemirror/keymap';
import { completionStatus } from '@codemirror/autocomplete';
const editableConf = new Compartment();
Expand Down Expand Up @@ -81,25 +87,12 @@ export default defineComponent({
},
mounted() {
const extensions = [
n8nLang(),
inputTheme({ rows: this.rows }),
Prec.highest(
keymap.of([
{ key: 'Tab', run: acceptCompletion },
{
any(view: EditorView, event: KeyboardEvent) {
if (event.key === 'Escape' && completionStatus(view.state) !== null) {
event.stopPropagation();
}
return false;
},
},
{ key: 'Mod-z', run: undo },
{ key: 'Mod-Shift-z', run: redo },
]),
keymap.of([...tabKeyMap(true), ...enterKeyMap, ...autocompleteKeyMap, ...historyKeyMap]),
),
autocompletion(),
n8nLang(),
n8nAutocompletion(),
inputTheme({ rows: this.rows }),
history(),
expressionInputHandler(),
EditorView.lineWrapping,
Expand All @@ -111,7 +104,11 @@ export default defineComponent({
},
}),
EditorView.updateListener.of((viewUpdate) => {
if (!this.editor || !viewUpdate.docChanged) return;
if (!this.editor) return;
this.completionStatus = completionStatus(viewUpdate.view.state);
if (!viewUpdate.docChanged) return;
// Force segments value update by keeping track of editor state
this.editorState = this.editor.state;
Expand Down
Loading

0 comments on commit 5226e00

Please sign in to comment.