From 52ae02abaa92e5bbfda58843c8eccc845506fa4b Mon Sep 17 00:00:00 2001 From: Elias Meire Date: Wed, 8 Jan 2025 11:28:56 +0100 Subject: [PATCH] feat(editor): New Code editor based on the TypeScript language service (#12285) --- cypress/e2e/6-code-node.cy.ts | 4 +- .../design-system/src/css/_primitives.scss | 1 + .../design-system/src/css/_tokens.dark.scss | 35 +- packages/design-system/src/css/_tokens.scss | 35 +- packages/editor-ui/package.json | 5 + .../CodeNodeEditor/CodeNodeEditor.vue | 295 +++--------- .../CodeNodeEditor/baseExtensions.ts | 49 -- .../components/CodeNodeEditor/constants.ts | 2 +- .../src/components/CodeNodeEditor/linter.ts | 65 +-- .../src/components/CodeNodeEditor/theme.ts | 271 ++++++----- .../src/components/ExpressionEditModal.vue | 1 - .../ExpressionEditorModalInput.vue | 29 +- .../src/components/HtmlEditor/HtmlEditor.vue | 18 +- .../ExpressionOutput.vue | 2 +- .../InlineExpressionEditorInput.vue | 11 +- .../src/components/JsEditor/JsEditor.vue | 23 +- .../src/components/JsonEditor/JsonEditor.vue | 15 +- .../src/components/ParameterInput.vue | 60 ++- .../src/components/SqlEditor/SqlEditor.vue | 33 +- .../src/composables/useCodeEditor.ts | 448 ++++++++++++++++++ .../composables/useExpressionEditor.test.ts | 43 +- .../src/composables/useExpressionEditor.ts | 5 +- .../src/composables/usePushConnection.ts | 2 +- .../src/event-bus/code-node-editor.ts | 2 +- .../plugins/codemirror/completions/utils.ts | 20 +- .../src/plugins/codemirror/format.ts | 47 ++ .../src/plugins/codemirror/keymap.ts | 313 ++++++++++-- .../src/plugins/codemirror/multiCursor.ts | 52 ++ .../typescript/client/completions.ts | 94 ++++ .../codemirror/typescript/client/facet.ts | 12 + .../typescript/client/hoverTooltip.ts | 54 +++ .../codemirror/typescript/client/linter.ts | 11 + .../codemirror/typescript/client/snippets.ts | 60 +++ .../typescript/client/useTypescript.ts | 131 +++++ .../plugins/codemirror/typescript/types.ts | 49 ++ .../codemirror/typescript/worker/cache.ts | 130 +++++ .../typescript/worker/completions.ts | 60 +++ .../codemirror/typescript/worker/constants.ts | 26 + .../typescript/worker/dynamicTypes.test.ts | 66 +++ .../typescript/worker/dynamicTypes.ts | 91 ++++ .../codemirror/typescript/worker/env.ts | 60 +++ .../typescript/worker/hoverTooltip.ts | 24 + .../codemirror/typescript/worker/linter.ts | 111 +++++ .../typescript/worker/npmTypesLoader.ts | 60 +++ .../worker/type-declarations/globals.d.ts | 16 + .../n8n-once-for-all-items.d.ts | 15 + .../n8n-once-for-each-item.d.ts | 16 + .../worker/type-declarations/n8n.d.ts | 103 ++++ .../typescript/worker/typescript.worker.ts | 224 +++++++++ .../typescript/worker/typescriptAst.ts | 38 ++ .../codemirror/typescript/worker/utils.ts | 64 +++ packages/editor-ui/src/shims-modules.d.ts | 5 + packages/editor-ui/src/shims.d.ts | 2 + .../src/styles/plugins/_codemirror.scss | 56 ++- packages/editor-ui/src/utils/forceParse.ts | 2 +- packages/editor-ui/tsconfig.json | 3 +- packages/editor-ui/vite.config.mts | 3 + pnpm-lock.yaml | 147 ++++-- 58 files changed, 2861 insertions(+), 758 deletions(-) delete mode 100644 packages/editor-ui/src/components/CodeNodeEditor/baseExtensions.ts create mode 100644 packages/editor-ui/src/composables/useCodeEditor.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/format.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/multiCursor.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/client/completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/client/facet.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/client/hoverTooltip.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/client/linter.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/client/snippets.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/client/useTypescript.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/types.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/cache.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/completions.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/constants.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/dynamicTypes.test.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/dynamicTypes.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/env.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/hoverTooltip.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/linter.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/npmTypesLoader.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/type-declarations/globals.d.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/type-declarations/n8n-once-for-all-items.d.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/type-declarations/n8n-once-for-each-item.d.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/type-declarations/n8n.d.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/typescript.worker.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/typescriptAst.ts create mode 100644 packages/editor-ui/src/plugins/codemirror/typescript/worker/utils.ts diff --git a/cypress/e2e/6-code-node.cy.ts b/cypress/e2e/6-code-node.cy.ts index 5bc7d05ee2..674d91af18 100644 --- a/cypress/e2e/6-code-node.cy.ts +++ b/cypress/e2e/6-code-node.cy.ts @@ -57,7 +57,7 @@ for (const item of $input.all()) { return `); - getParameter().get('.cm-lint-marker-error').should('have.length', 6); + getParameter().get('.cm-lintRange-error').should('have.length', 6); getParameter().contains('itemMatching').realHover(); cy.get('.cm-tooltip-lint').should( 'have.text', @@ -81,7 +81,7 @@ $input.item() return [] `); - getParameter().get('.cm-lint-marker-error').should('have.length', 5); + getParameter().get('.cm-lintRange-error').should('have.length', 5); getParameter().contains('all').realHover(); cy.get('.cm-tooltip-lint').should( 'have.text', diff --git a/packages/design-system/src/css/_primitives.scss b/packages/design-system/src/css/_primitives.scss index 71c6b4c10f..73d47a7fd4 100644 --- a/packages/design-system/src/css/_primitives.scss +++ b/packages/design-system/src/css/_primitives.scss @@ -16,6 +16,7 @@ --prim-gray-490: hsl(var(--prim-gray-h), 3%, 51%); --prim-gray-420: hsl(var(--prim-gray-h), 4%, 58%); --prim-gray-320: hsl(var(--prim-gray-h), 10%, 68%); + --prim-gray-320-alpha-010: hsla(var(--prim-gray-h), 10%, 68%, 0.1); --prim-gray-200: hsl(var(--prim-gray-h), 18%, 80%); --prim-gray-120: hsl(var(--prim-gray-h), 25%, 88%); --prim-gray-70: hsl(var(--prim-gray-h), 32%, 93%); diff --git a/packages/design-system/src/css/_tokens.dark.scss b/packages/design-system/src/css/_tokens.dark.scss index 4ea6566677..00eeafb981 100644 --- a/packages/design-system/src/css/_tokens.dark.scss +++ b/packages/design-system/src/css/_tokens.dark.scss @@ -139,12 +139,20 @@ --color-infobox-examples-border-color: var(--prim-gray-670); // Code - --color-code-tags-string: var(--prim-color-alt-f-tint-150); - --color-code-tags-primitive: var(--prim-color-alt-b-shade-100); - --color-code-tags-keyword: var(--prim-color-alt-g-tint-150); - --color-code-tags-operator: var(--prim-color-alt-h); - --color-code-tags-variable: var(--prim-color-primary-tint-100); - --color-code-tags-definition: var(--prim-color-alt-e); + --color-code-tags-string: #9ecbff; + --color-code-tags-regex: #9ecbff; + --color-code-tags-primitive: #79b8ff; + --color-code-tags-keyword: #f97583; + --color-code-tags-variable: #79b8ff; + --color-code-tags-parameter: #e1e4e8; + --color-code-tags-function: #b392f0; + --color-code-tags-constant: #79b8ff; + --color-code-tags-property: #79b8ff; + --color-code-tags-type: #b392f0; + --color-code-tags-class: #b392f0; + --color-code-tags-heading: #79b8ff; + --color-code-tags-invalid: #f97583; + --color-code-tags-comment: #6a737d; --color-json-default: var(--prim-color-secondary-tint-200); --color-json-null: var(--color-danger); --color-json-boolean: var(--prim-color-alt-a); @@ -155,15 +163,18 @@ --color-json-brackets-hover: var(--prim-color-alt-e); --color-json-line: var(--prim-gray-200); --color-json-highlight: var(--color-background-base); - --color-code-background: var(--prim-gray-800); + --color-code-background: var(--prim-gray-820); --color-code-background-readonly: var(--prim-gray-740); - --color-code-lineHighlight: var(--prim-gray-740); + --color-code-lineHighlight: var(--prim-gray-320-alpha-010); --color-code-foreground: var(--prim-gray-70); --color-code-caret: var(--prim-gray-10); - --color-code-selection: var(--prim-color-alt-e-alpha-04); - --color-code-gutterBackground: var(--prim-gray-670); - --color-code-gutterForeground: var(--prim-gray-320); - --color-code-tags-comment: var(--prim-gray-200); + --color-code-selection: #3392ff44; + --color-code-selection-highlight: #17e5e633; + --color-code-gutter-background: var(--prim-gray-820); + --color-code-gutter-foreground: var(--prim-gray-320); + --color-code-gutter-foreground-active: var(--prim-gray-10); + --color-code-indentation-marker: var(--prim-gray-740); + --color-code-indentation-marker-active: var(--prim-gray-670); --color-line-break: var(--prim-gray-420); --color-code-line-break: var(--prim-color-secondary-tint-100); diff --git a/packages/design-system/src/css/_tokens.scss b/packages/design-system/src/css/_tokens.scss index e4faf088cf..3b6b7451be 100644 --- a/packages/design-system/src/css/_tokens.scss +++ b/packages/design-system/src/css/_tokens.scss @@ -183,12 +183,20 @@ --color-infobox-examples-border-color: var(--color-foreground-light); // Code - --color-code-tags-string: var(--prim-color-alt-f); - --color-code-tags-primitive: var(--prim-color-alt-b-shade-100); - --color-code-tags-keyword: var(--prim-color-alt-g); - --color-code-tags-operator: var(--prim-color-alt-h); - --color-code-tags-variable: var(--prim-color-alt-c-shade-100); - --color-code-tags-definition: var(--prim-color-alt-e-shade-150); + --color-code-tags-string: #032f62; + --color-code-tags-regex: #032f62; + --color-code-tags-primitive: #005cc5; + --color-code-tags-keyword: #d73a49; + --color-code-tags-variable: #005cc5; + --color-code-tags-parameter: #24292e; + --color-code-tags-function: #6f42c1; + --color-code-tags-constant: #005cc5; + --color-code-tags-property: #005cc5; + --color-code-tags-type: #005cc5; + --color-code-tags-class: #6f42c1; + --color-code-tags-heading: #005cc5; + --color-code-tags-invalid: #cb2431; + --color-code-tags-comment: #6a737d; --color-json-default: var(--prim-color-secondary-shade-100); --color-json-null: var(--prim-color-alt-c); --color-json-boolean: var(--prim-color-alt-a); @@ -201,13 +209,16 @@ --color-json-highlight: var(--prim-gray-70); --color-code-background: var(--prim-gray-0); --color-code-background-readonly: var(--prim-gray-40); - --color-code-lineHighlight: var(--prim-gray-40); + --color-code-lineHighlight: var(--prim-gray-320-alpha-010); --color-code-foreground: var(--prim-gray-670); - --color-code-caret: var(--prim-gray-420); - --color-code-selection: var(--prim-gray-120); - --color-code-gutterBackground: var(--prim-gray-0); - --color-code-gutterForeground: var(--prim-gray-320); - --color-code-tags-comment: var(--prim-gray-420); + --color-code-caret: var(--prim-gray-820); + --color-code-selection: #0366d625; + --color-code-selection-highlight: #34d05840; + --color-code-gutter-background: var(--prim-gray-0); + --color-code-gutter-foreground: var(--prim-gray-320); + --color-code-gutter-foreground-active: var(--prim-gray-670); + --color-code-indentation-marker: var(--prim-gray-70); + --color-code-indentation-marker-active: var(--prim-gray-200); --color-line-break: var(--prim-gray-320); --color-code-line-break: var(--prim-color-secondary-tint-200); diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 3293abfe3b..e7756983a7 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -25,6 +25,7 @@ "@codemirror/lang-python": "^6.1.6", "@codemirror/language": "^6.10.1", "@codemirror/lint": "^6.8.0", + "@codemirror/search": "^6.5.6", "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.26.3", "@fontsource/open-sans": "^4.5.0", @@ -39,6 +40,8 @@ "@n8n/codemirror-lang": "workspace:*", "@n8n/codemirror-lang-sql": "^1.0.2", "@n8n/permissions": "workspace:*", + "@replit/codemirror-indentation-markers": "^6.5.3", + "@typescript/vfs": "^1.6.0", "@sentry/vue": "catalog:frontend", "@vue-flow/background": "^1.3.2", "@vue-flow/controls": "^1.1.2", @@ -52,6 +55,7 @@ "change-case": "^5.4.4", "chart.js": "^4.4.0", "codemirror-lang-html-n8n": "^1.0.0", + "comlink": "^4.4.1", "dateformat": "^3.0.3", "email-providers": "^2.0.1", "esprima-next": "5.8.4", @@ -70,6 +74,7 @@ "qrcode.vue": "^3.3.4", "stream-browserify": "^3.0.0", "timeago.js": "^4.0.2", + "typescript": "^5.5.2", "uuid": "catalog:", "v3-infinite-loading": "^1.2.2", "vue": "catalog:frontend", diff --git a/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue b/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue index 3f74bfe7a0..c17ff804c0 100644 --- a/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue +++ b/packages/editor-ui/src/components/CodeNodeEditor/CodeNodeEditor.vue @@ -1,32 +1,24 @@