@neo4j-cypher/react-codemirror 2.0.0-next.4 → 2.0.0-next.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +370 -0
- package/README.md +3 -2
- package/dist/src/CypherEditor.d.ts +245 -0
- package/dist/src/CypherEditor.js +513 -0
- package/dist/src/CypherEditor.js.map +1 -0
- package/dist/src/CypherEditor.test.js +162 -0
- package/dist/src/CypherEditor.test.js.map +1 -0
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/constants.js +2 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/diffView.d.ts +13 -0
- package/dist/src/diffView.js +11 -0
- package/dist/src/diffView.js.map +1 -0
- package/dist/src/e2e_tests/autoCompletion.spec.js +334 -0
- package/dist/src/e2e_tests/autoCompletion.spec.js.map +1 -0
- package/dist/src/e2e_tests/configuration.spec.js +83 -0
- package/dist/src/e2e_tests/configuration.spec.js.map +1 -0
- package/dist/src/e2e_tests/debounce.spec.js +66 -0
- package/dist/src/e2e_tests/debounce.spec.js.map +1 -0
- package/dist/{types/e2e_tests/e2e-utils.d.ts → src/e2e_tests/e2eUtils.d.ts} +2 -0
- package/dist/src/e2e_tests/e2eUtils.js +79 -0
- package/dist/src/e2e_tests/e2eUtils.js.map +1 -0
- package/dist/src/e2e_tests/extraKeybindings.spec.js +43 -0
- package/dist/src/e2e_tests/extraKeybindings.spec.js.map +1 -0
- package/dist/src/e2e_tests/historyNavigation.spec.js +227 -0
- package/dist/src/e2e_tests/historyNavigation.spec.js.map +1 -0
- package/dist/src/e2e_tests/performanceTest.spec.d.ts +6 -0
- package/dist/src/e2e_tests/performanceTest.spec.js +97 -0
- package/dist/src/e2e_tests/performanceTest.spec.js.map +1 -0
- package/dist/src/e2e_tests/sanityChecks.spec.js +64 -0
- package/dist/src/e2e_tests/sanityChecks.spec.js.map +1 -0
- package/dist/src/e2e_tests/signatureHelp.spec.js +226 -0
- package/dist/src/e2e_tests/signatureHelp.spec.js.map +1 -0
- package/dist/src/e2e_tests/snippets.spec.js +14 -0
- package/dist/src/e2e_tests/snippets.spec.js.map +1 -0
- package/dist/src/e2e_tests/syntaxHighlighting.spec.d.ts +1 -0
- package/dist/src/e2e_tests/syntaxHighlighting.spec.js +90 -0
- package/dist/src/e2e_tests/syntaxHighlighting.spec.js.map +1 -0
- package/dist/src/e2e_tests/syntaxValidation.spec.d.ts +1 -0
- package/dist/src/e2e_tests/syntaxValidation.spec.js +129 -0
- package/dist/src/e2e_tests/syntaxValidation.spec.js.map +1 -0
- package/dist/src/editorActions.d.ts +17 -0
- package/dist/src/editorActions.js +259 -0
- package/dist/src/editorActions.js.map +1 -0
- package/dist/{types/history-navigation.d.ts → src/historyNavigation.d.ts} +1 -1
- package/dist/src/historyNavigation.js +163 -0
- package/dist/src/historyNavigation.js.map +1 -0
- package/dist/src/hostCallbacks.d.ts +9 -0
- package/dist/src/hostCallbacks.js +2 -0
- package/dist/src/hostCallbacks.js.map +1 -0
- package/dist/{types → src}/icons.d.ts +1 -1
- package/dist/src/icons.js +62 -0
- package/dist/src/icons.js.map +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/inlinePanel.d.ts +27 -0
- package/dist/src/inlinePanel.js +86 -0
- package/dist/src/inlinePanel.js.map +1 -0
- package/dist/src/lang-cypher/autocomplete.d.ts +6 -0
- package/dist/src/lang-cypher/autocomplete.js +116 -0
- package/dist/src/lang-cypher/autocomplete.js.map +1 -0
- package/dist/{types → src}/lang-cypher/constants.d.ts +15 -2
- package/dist/src/lang-cypher/constants.js +74 -0
- package/dist/src/lang-cypher/constants.js.map +1 -0
- package/dist/src/lang-cypher/contants.test.d.ts +1 -0
- package/dist/src/lang-cypher/contants.test.js +103 -0
- package/dist/src/lang-cypher/contants.test.js.map +1 -0
- package/dist/{types/lang-cypher/create-cypher-theme.d.ts → src/lang-cypher/createCypherTheme.d.ts} +14 -3
- package/dist/src/lang-cypher/createCypherTheme.js +202 -0
- package/dist/src/lang-cypher/createCypherTheme.js.map +1 -0
- package/dist/src/lang-cypher/langCypher.d.ts +15 -0
- package/dist/src/lang-cypher/langCypher.js +23 -0
- package/dist/src/lang-cypher/langCypher.js.map +1 -0
- package/dist/src/lang-cypher/lintWorker.mjs +2039 -0
- package/dist/src/lang-cypher/parser-adapter.d.ts +21 -0
- package/dist/src/lang-cypher/parser-adapter.js +115 -0
- package/dist/src/lang-cypher/parser-adapter.js.map +1 -0
- package/dist/src/lang-cypher/prism-global.d.ts +1 -0
- package/dist/src/lang-cypher/prism-global.js +7 -0
- package/dist/src/lang-cypher/prism-global.js.map +1 -0
- package/dist/src/lang-cypher/signatureHelp.d.ts +4 -0
- package/dist/src/lang-cypher/signatureHelp.js +108 -0
- package/dist/src/lang-cypher/signatureHelp.js.map +1 -0
- package/dist/src/lang-cypher/syntaxValidation.d.ts +4 -0
- package/dist/src/lang-cypher/syntaxValidation.js +65 -0
- package/dist/src/lang-cypher/syntaxValidation.js.map +1 -0
- package/dist/src/lang-cypher/themeIcons.js +22 -0
- package/dist/src/lang-cypher/themeIcons.js.map +1 -0
- package/dist/src/lang-cypher/utils.d.ts +2 -0
- package/dist/src/lang-cypher/utils.js +10 -0
- package/dist/src/lang-cypher/utils.js.map +1 -0
- package/dist/src/ndlTokensCopy.d.ts +570 -0
- package/dist/src/ndlTokensCopy.js +571 -0
- package/dist/src/ndlTokensCopy.js.map +1 -0
- package/dist/src/ndlTokensCopy.test.d.ts +1 -0
- package/dist/src/ndlTokensCopy.test.js +12 -0
- package/dist/src/ndlTokensCopy.test.js.map +1 -0
- package/dist/src/neo4jSetup.d.ts +6 -0
- package/dist/src/neo4jSetup.js +120 -0
- package/dist/src/neo4jSetup.js.map +1 -0
- package/dist/src/richClipboardCopier.d.ts +4 -0
- package/dist/src/richClipboardCopier.js +78 -0
- package/dist/src/richClipboardCopier.js.map +1 -0
- package/dist/src/richClipboardCopier.test.d.ts +1 -0
- package/dist/src/richClipboardCopier.test.js +53 -0
- package/dist/src/richClipboardCopier.test.js.map +1 -0
- package/dist/src/themes.d.ts +5 -0
- package/dist/src/themes.js +107 -0
- package/dist/src/themes.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +64 -62
- package/src/CypherEditor.test.tsx +211 -0
- package/src/CypherEditor.tsx +586 -59
- package/src/constants.ts +1 -0
- package/src/diffView.ts +24 -0
- package/src/e2e_tests/autoCompletion.spec.tsx +576 -0
- package/src/e2e_tests/configuration.spec.tsx +111 -0
- package/src/e2e_tests/debounce.spec.tsx +102 -0
- package/src/e2e_tests/{e2e-utils.ts → e2eUtils.ts} +41 -3
- package/src/e2e_tests/{extra-keybindings.spec.tsx → extraKeybindings.spec.tsx} +1 -3
- package/src/e2e_tests/{history-navigation.spec.tsx → historyNavigation.spec.tsx} +138 -19
- package/src/e2e_tests/performanceTest.spec.tsx +163 -0
- package/src/e2e_tests/{sanity-checks.spec.tsx → sanityChecks.spec.tsx} +28 -20
- package/src/e2e_tests/signatureHelp.spec.tsx +442 -0
- package/src/e2e_tests/snippets.spec.tsx +19 -0
- package/src/e2e_tests/{syntax-highlighting.spec.tsx → syntaxHighlighting.spec.tsx} +26 -24
- package/src/e2e_tests/syntaxValidation.spec.tsx +252 -0
- package/src/editorActions.ts +334 -0
- package/src/{history-navigation.ts → historyNavigation.ts} +6 -4
- package/src/hostCallbacks.ts +9 -0
- package/src/icons.ts +3 -0
- package/src/index.ts +4 -2
- package/src/inlinePanel.ts +140 -0
- package/src/lang-cypher/autocomplete.ts +96 -19
- package/src/lang-cypher/constants.ts +35 -2
- package/src/lang-cypher/contants.test.ts +7 -3
- package/src/lang-cypher/{create-cypher-theme.ts → createCypherTheme.ts} +97 -33
- package/src/lang-cypher/langCypher.ts +44 -0
- package/src/lang-cypher/lintWorker.mjs +2039 -0
- package/src/lang-cypher/parser-adapter.ts +146 -0
- package/src/lang-cypher/prism-global.ts +7 -0
- package/src/lang-cypher/signatureHelp.ts +154 -0
- package/src/lang-cypher/syntaxValidation.ts +81 -0
- package/src/lang-cypher/utils.ts +9 -0
- package/src/{ndl-tokens-copy.test.ts → ndlTokensCopy.test.ts} +2 -1
- package/src/ndlTokensCopy.ts +570 -0
- package/src/{neo4j-setup.tsx → neo4jSetup.tsx} +79 -18
- package/src/richClipboardCopier.test.ts +67 -0
- package/src/richClipboardCopier.ts +99 -0
- package/src/themes.ts +63 -71
- package/src/viteEnv.d.ts +1 -0
- package/dist/cjs/index.cjs +0 -1441
- package/dist/cjs/index.cjs.map +0 -7
- package/dist/esm/index.mjs +0 -1464
- package/dist/esm/index.mjs.map +0 -7
- package/dist/types/CypherEditor.d.ts +0 -113
- package/dist/types/e2e_tests/mock-data.d.ts +0 -3779
- package/dist/types/index.d.ts +0 -4
- package/dist/types/lang-cypher/ParserAdapter.d.ts +0 -14
- package/dist/types/lang-cypher/autocomplete.d.ts +0 -3
- package/dist/types/lang-cypher/lang-cypher.d.ts +0 -7
- package/dist/types/lang-cypher/syntax-validation.d.ts +0 -3
- package/dist/types/ndl-tokens-copy.d.ts +0 -379
- package/dist/types/neo4j-setup.d.ts +0 -2
- package/dist/types/themes.d.ts +0 -11
- package/dist/types/tsconfig.tsbuildinfo +0 -1
- package/src/e2e_tests/auto-completion.spec.tsx +0 -232
- package/src/e2e_tests/mock-data.ts +0 -4310
- package/src/e2e_tests/performance-test.spec.tsx +0 -71
- package/src/e2e_tests/syntax-validation.spec.tsx +0 -156
- package/src/lang-cypher/ParserAdapter.ts +0 -92
- package/src/lang-cypher/lang-cypher.ts +0 -32
- package/src/lang-cypher/syntax-validation.ts +0 -24
- package/src/ndl-tokens-copy.ts +0 -379
- /package/dist/{types/e2e_tests/auto-completion.spec.d.ts → src/CypherEditor.test.d.ts} +0 -0
- /package/dist/{types/e2e_tests/extra-keybindings.spec.d.ts → src/e2e_tests/autoCompletion.spec.d.ts} +0 -0
- /package/dist/{types/e2e_tests/history-navigation.spec.d.ts → src/e2e_tests/configuration.spec.d.ts} +0 -0
- /package/dist/{types/e2e_tests/performance-test.spec.d.ts → src/e2e_tests/debounce.spec.d.ts} +0 -0
- /package/dist/{types/e2e_tests/sanity-checks.spec.d.ts → src/e2e_tests/extraKeybindings.spec.d.ts} +0 -0
- /package/dist/{types/e2e_tests/syntax-highlighting.spec.d.ts → src/e2e_tests/historyNavigation.spec.d.ts} +0 -0
- /package/dist/{types/e2e_tests/syntax-validation.spec.d.ts → src/e2e_tests/sanityChecks.spec.d.ts} +0 -0
- /package/dist/{types/lang-cypher/contants.test.d.ts → src/e2e_tests/signatureHelp.spec.d.ts} +0 -0
- /package/dist/{types/ndl-tokens-copy.test.d.ts → src/e2e_tests/snippets.spec.d.ts} +0 -0
- /package/dist/{types/lang-cypher/theme-icons.d.ts → src/lang-cypher/themeIcons.d.ts} +0 -0
- /package/src/lang-cypher/{theme-icons.ts → themeIcons.ts} +0 -0
package/src/CypherEditor.tsx
CHANGED
|
@@ -1,25 +1,54 @@
|
|
|
1
|
+
import { insertNewline } from '@codemirror/commands';
|
|
2
|
+
import type { Extension } from '@codemirror/state';
|
|
3
|
+
import { Annotation, Compartment, EditorState } from '@codemirror/state';
|
|
4
|
+
import type { KeyBinding, ViewUpdate } from '@codemirror/view';
|
|
5
|
+
import { EditorView, keymap, lineNumbers, placeholder } from '@codemirror/view';
|
|
1
6
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '
|
|
7
|
+
createInlinePanelController,
|
|
8
|
+
type InlinePanelCallbacks,
|
|
9
|
+
type InlinePanelController,
|
|
10
|
+
} from './inlinePanel';
|
|
11
|
+
import { createDiffExtension, type DiffProps } from './diffView';
|
|
7
12
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
createEditorActionsController,
|
|
14
|
+
type EditorActionsCallbacks,
|
|
15
|
+
type EditorActionsController,
|
|
16
|
+
} from './editorActions';
|
|
17
|
+
import {
|
|
18
|
+
formatQuery,
|
|
19
|
+
CypherLanguageService,
|
|
20
|
+
type DbSchema,
|
|
21
|
+
} from '@neo4j-cypher/language-support';
|
|
22
|
+
import debounce from 'lodash.debounce';
|
|
23
|
+
import { Component, createRef, type ReactNode } from 'react';
|
|
24
|
+
import { createPortal } from 'react-dom';
|
|
25
|
+
import { DEBOUNCE_TIME } from './constants';
|
|
16
26
|
import {
|
|
17
27
|
replaceHistory,
|
|
18
28
|
replMode as historyNavigation,
|
|
19
|
-
} from './
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
29
|
+
} from './historyNavigation';
|
|
30
|
+
import type { CypherConfig } from './lang-cypher/langCypher';
|
|
31
|
+
import { cypher } from './lang-cypher/langCypher';
|
|
32
|
+
import { cleanupWorkers } from './lang-cypher/syntaxValidation';
|
|
33
|
+
import { basicNeo4jSetup } from './neo4jSetup';
|
|
22
34
|
import { getThemeExtension } from './themes';
|
|
35
|
+
import { richClipboardCopier } from './richClipboardCopier';
|
|
36
|
+
import type { LintWorker } from '@neo4j-cypher/lint-worker';
|
|
37
|
+
import workerpool from 'workerpool';
|
|
38
|
+
|
|
39
|
+
type DomEventHandlers = Parameters<typeof EditorView.domEventHandlers>[0];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Normalize CRLF line endings to LF, which is what CodeMirror expects.
|
|
43
|
+
* CodeMirror collapses `\r\n` into a single line break, so a raw `value`
|
|
44
|
+
* string with CRLF endings is longer than the resulting document. Normalizing
|
|
45
|
+
* up front keeps the document length in sync with the value length used for
|
|
46
|
+
* cursor placement, and avoids "Selection points outside of document" errors.
|
|
47
|
+
* https://codemirror.net/docs/ref/#state.EditorState^lineSeparator
|
|
48
|
+
*/
|
|
49
|
+
function normalizeLineEndings(value: string): string {
|
|
50
|
+
return value.replace(/\r\n/g, '\n');
|
|
51
|
+
}
|
|
23
52
|
|
|
24
53
|
export interface CypherEditorProps {
|
|
25
54
|
/**
|
|
@@ -40,7 +69,14 @@ export interface CypherEditorProps {
|
|
|
40
69
|
*/
|
|
41
70
|
onExecute?: (cmd: string) => void;
|
|
42
71
|
/**
|
|
43
|
-
*
|
|
72
|
+
* If true, pressing enter will add a new line to the editor and cmd/ctrl + enter will execute the query.
|
|
73
|
+
* Otherwise pressing enter on a single line will execute the query.
|
|
74
|
+
*
|
|
75
|
+
* @default false
|
|
76
|
+
*/
|
|
77
|
+
newLineOnEnter?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* The editor history navigable via up/down arrow keys. Order newest to oldest.
|
|
44
80
|
* Add to this list with the `onExecute` callback for REPL style history.
|
|
45
81
|
*/
|
|
46
82
|
history?: string[];
|
|
@@ -57,6 +93,10 @@ export interface CypherEditorProps {
|
|
|
57
93
|
* @default false
|
|
58
94
|
*/
|
|
59
95
|
autofocus?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Where to place the cursor in the query. Cannot be enabled at the same time than autofocus
|
|
98
|
+
*/
|
|
99
|
+
offset?: number;
|
|
60
100
|
/**
|
|
61
101
|
* Whether the editor should wrap lines.
|
|
62
102
|
*
|
|
@@ -69,6 +109,20 @@ export interface CypherEditorProps {
|
|
|
69
109
|
* @default true
|
|
70
110
|
*/
|
|
71
111
|
lint?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Whether the signature help tooltip should be shown below the text.
|
|
114
|
+
* If false, it will be shown above.
|
|
115
|
+
*
|
|
116
|
+
* @default true
|
|
117
|
+
*/
|
|
118
|
+
showSignatureTooltipBelow?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Internal feature flags for the editor. Don't use in production
|
|
121
|
+
*
|
|
122
|
+
*/
|
|
123
|
+
featureFlags?: {
|
|
124
|
+
consoleCommands?: boolean;
|
|
125
|
+
};
|
|
72
126
|
/**
|
|
73
127
|
* The schema to use for autocompletion and linting.
|
|
74
128
|
*
|
|
@@ -96,32 +150,253 @@ export interface CypherEditorProps {
|
|
|
96
150
|
* @param {ViewUpdate} viewUpdate - the view update from codemirror
|
|
97
151
|
*/
|
|
98
152
|
onChange?(value: string, viewUpdate: ViewUpdate): void;
|
|
99
|
-
}
|
|
100
153
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Map of event handlers to add to the editor.
|
|
156
|
+
*
|
|
157
|
+
* Note that the props are compared by reference, meaning object defined inline
|
|
158
|
+
* will cause the editor to re-render (much like the style prop does in this example:
|
|
159
|
+
* <div style={{}} />
|
|
160
|
+
*
|
|
161
|
+
* Memoize the object if you want/need to avoid this.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // listen to blur events
|
|
165
|
+
* <CypherEditor domEventHandlers={{blur: () => console.log("blur event fired")}} />
|
|
166
|
+
*/
|
|
167
|
+
domEventHandlers?: DomEventHandlers;
|
|
168
|
+
/**
|
|
169
|
+
* Placeholder text to display when the editor is empty.
|
|
170
|
+
*/
|
|
171
|
+
placeholder?: string;
|
|
172
|
+
/**
|
|
173
|
+
* Whether the editor should show line numbers.
|
|
174
|
+
*
|
|
175
|
+
* @default true
|
|
176
|
+
*/
|
|
177
|
+
lineNumbers?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Whether the editor is read-only.
|
|
180
|
+
*
|
|
181
|
+
* @default false
|
|
182
|
+
*/
|
|
183
|
+
readonly?: boolean;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* String value to assign to the aria-label attribute of the editor.
|
|
187
|
+
*/
|
|
188
|
+
ariaLabel?: string;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Whether keybindings for inserting indents with the Tab key should be disabled.
|
|
192
|
+
*
|
|
193
|
+
* true will not create keybindings for inserting indents.
|
|
194
|
+
* false will create keybindings for inserting indents.
|
|
195
|
+
*
|
|
196
|
+
* @default false
|
|
197
|
+
*/
|
|
198
|
+
moveFocusOnTab?: boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Render a panel as a block widget inside the editor.
|
|
201
|
+
* The widget DOM is only rebuilt when `pos` or `placement` change
|
|
202
|
+
*/
|
|
203
|
+
inlinePanel?: InlinePanelProps | null;
|
|
204
|
+
/**
|
|
205
|
+
* Render a unified diff of the current document against `diff.original`.
|
|
206
|
+
* Deleted lines are shown as uneditable widgets.
|
|
207
|
+
*/
|
|
208
|
+
diff?: DiffProps | null;
|
|
209
|
+
/**
|
|
210
|
+
* React content rendered as a cluster of action buttons (e.g. a context menu
|
|
211
|
+
* + Run) pinned to the top-right corner of the editor. Omit (or `null`) to
|
|
212
|
+
* hide it.
|
|
213
|
+
*/
|
|
214
|
+
editorActions?: ReactNode;
|
|
215
|
+
}
|
|
113
216
|
|
|
114
|
-
|
|
115
|
-
|
|
217
|
+
export type InlinePanelProps = {
|
|
218
|
+
/**
|
|
219
|
+
* Position the panel anchors to.
|
|
220
|
+
*/
|
|
221
|
+
pos: number;
|
|
222
|
+
/**
|
|
223
|
+
* Whether to render above or below the line
|
|
224
|
+
*
|
|
225
|
+
* @default 'above'
|
|
226
|
+
*/
|
|
227
|
+
placement?: 'above' | 'below';
|
|
228
|
+
} & InlinePanelCallbacks;
|
|
229
|
+
|
|
230
|
+
const format = (view: EditorView): void => {
|
|
231
|
+
try {
|
|
232
|
+
const doc = view.state.doc.toString();
|
|
233
|
+
const { formattedQuery, newCursorPos } = formatQuery(doc, {
|
|
234
|
+
cursorPosition: view.state.selection.main.anchor,
|
|
235
|
+
});
|
|
236
|
+
view.dispatch({
|
|
237
|
+
changes: {
|
|
238
|
+
from: 0,
|
|
239
|
+
to: doc.length,
|
|
240
|
+
insert: formattedQuery,
|
|
241
|
+
},
|
|
242
|
+
selection: { anchor: newCursorPos },
|
|
243
|
+
});
|
|
244
|
+
} catch {
|
|
245
|
+
// Formatting failed, likely because of a syntax error
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const executeKeybinding = (
|
|
250
|
+
onExecute?: (cmd: string) => void,
|
|
251
|
+
newLineOnEnter?: boolean,
|
|
252
|
+
flush?: () => void,
|
|
253
|
+
) => {
|
|
254
|
+
const keybindings: Record<string, KeyBinding> = {
|
|
255
|
+
'Shift-Enter': {
|
|
256
|
+
key: 'Shift-Enter',
|
|
257
|
+
run: insertNewline,
|
|
258
|
+
},
|
|
259
|
+
'Ctrl-Enter': {
|
|
260
|
+
key: 'Ctrl-Enter',
|
|
261
|
+
run: () => true,
|
|
262
|
+
},
|
|
263
|
+
Enter: {
|
|
264
|
+
key: 'Enter',
|
|
265
|
+
run: insertNewline,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
if (onExecute) {
|
|
269
|
+
keybindings['Ctrl-Enter'] = {
|
|
270
|
+
key: 'Ctrl-Enter',
|
|
271
|
+
mac: 'Mod-Enter',
|
|
272
|
+
preventDefault: true,
|
|
273
|
+
run: (view: EditorView) => {
|
|
274
|
+
const doc = view.state.doc.toString();
|
|
275
|
+
if (doc.trim() !== '') {
|
|
276
|
+
flush?.();
|
|
277
|
+
onExecute(doc);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return true;
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
if (!newLineOnEnter) {
|
|
285
|
+
keybindings['Enter'] = {
|
|
286
|
+
key: 'Enter',
|
|
287
|
+
preventDefault: true,
|
|
288
|
+
run: (view: EditorView) => {
|
|
289
|
+
const doc = view.state.doc.toString();
|
|
290
|
+
if (doc.includes('\n')) {
|
|
291
|
+
// Returning false means the event will mark the event
|
|
292
|
+
// as not handled and the default behavior will be executed
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (doc.trim() !== '') {
|
|
297
|
+
flush?.();
|
|
298
|
+
onExecute(doc);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return true;
|
|
116
302
|
},
|
|
117
|
-
|
|
118
|
-
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return Object.values(keybindings);
|
|
308
|
+
};
|
|
119
309
|
|
|
120
310
|
const themeCompartment = new Compartment();
|
|
121
311
|
const keyBindingCompartment = new Compartment();
|
|
312
|
+
const lineNumbersCompartment = new Compartment();
|
|
313
|
+
const readOnlyCompartment = new Compartment();
|
|
314
|
+
const placeholderCompartment = new Compartment();
|
|
315
|
+
const domEventHandlerCompartment = new Compartment();
|
|
316
|
+
const diffCompartment = new Compartment();
|
|
317
|
+
|
|
318
|
+
const formatLineNumber =
|
|
319
|
+
(prompt?: string) => (a: number, state: EditorState) => {
|
|
320
|
+
if (state.doc.lines === 1 && prompt !== undefined) {
|
|
321
|
+
return prompt;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return a.toString();
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
type CypherEditorState = {
|
|
328
|
+
editorActionsContainer: HTMLElement | null;
|
|
329
|
+
};
|
|
122
330
|
|
|
123
331
|
const ExternalEdit = Annotation.define<boolean>();
|
|
124
|
-
|
|
332
|
+
const WorkerURL = new URL('./lang-cypher/lintWorker.mjs', import.meta.url)
|
|
333
|
+
.pathname;
|
|
334
|
+
|
|
335
|
+
class CodemirrorSymbolFetcher {
|
|
336
|
+
constructor(languageService: CypherLanguageService) {
|
|
337
|
+
this.languageService = languageService;
|
|
338
|
+
}
|
|
339
|
+
private languageService: CypherLanguageService;
|
|
340
|
+
private processing = false;
|
|
341
|
+
private nextJob:
|
|
342
|
+
| {
|
|
343
|
+
query: string;
|
|
344
|
+
schema: DbSchema;
|
|
345
|
+
}
|
|
346
|
+
| undefined;
|
|
347
|
+
private symbolTablePool = workerpool.pool(WorkerURL, {
|
|
348
|
+
minWorkers: 1,
|
|
349
|
+
workerOpts: { type: 'module' },
|
|
350
|
+
workerTerminateTimeout: 2000,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
public queueSymbolJob(query: string, schema: DbSchema) {
|
|
354
|
+
this.nextJob = { query, schema };
|
|
355
|
+
if (!this.processing) {
|
|
356
|
+
void this.processJobQueue();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
public terminate() {
|
|
361
|
+
this.nextJob = undefined;
|
|
362
|
+
void this.symbolTablePool.terminate();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private async processJobQueue() {
|
|
366
|
+
this.processing = true;
|
|
367
|
+
while (this.nextJob) {
|
|
368
|
+
try {
|
|
369
|
+
const query = this.nextJob.query;
|
|
370
|
+
const dbSchema = this.nextJob.schema;
|
|
371
|
+
this.nextJob = undefined;
|
|
372
|
+
const proxyWorker =
|
|
373
|
+
(await this.symbolTablePool.proxy()) as unknown as LintWorker;
|
|
374
|
+
|
|
375
|
+
const result = await proxyWorker.lintCypherQuery(query, dbSchema);
|
|
376
|
+
|
|
377
|
+
if (result.symbolTables) {
|
|
378
|
+
this.languageService.setSymbolsInfo({
|
|
379
|
+
query,
|
|
380
|
+
symbolTables: result.symbolTables,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
} catch (err) {
|
|
384
|
+
//eslint-disable-next-line
|
|
385
|
+
console.log('Symbol table calculation failed ' + String(err));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
this.processing = false;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export class CypherEditor extends Component<
|
|
393
|
+
CypherEditorProps,
|
|
394
|
+
CypherEditorState
|
|
395
|
+
> {
|
|
396
|
+
/**
|
|
397
|
+
* The symbol fetcher object used to fetch the current symbol table on document changes
|
|
398
|
+
*/
|
|
399
|
+
symbolFetcher: CodemirrorSymbolFetcher;
|
|
125
400
|
/**
|
|
126
401
|
* The codemirror editor container.
|
|
127
402
|
*/
|
|
@@ -135,6 +410,25 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
135
410
|
*/
|
|
136
411
|
editorView: React.MutableRefObject<EditorView> = createRef();
|
|
137
412
|
private schemaRef: React.MutableRefObject<CypherConfig> = createRef();
|
|
413
|
+
private inlinePanelController: InlinePanelController | null = null;
|
|
414
|
+
private editorActionsController: EditorActionsController | null = null;
|
|
415
|
+
|
|
416
|
+
state: CypherEditorState = {
|
|
417
|
+
editorActionsContainer: null,
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
private editorActionsCallbacks: EditorActionsCallbacks = {
|
|
421
|
+
onMount: (container) =>
|
|
422
|
+
this.setState({ editorActionsContainer: container }),
|
|
423
|
+
onUnmount: () => this.setState({ editorActionsContainer: null }),
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Format Cypher query
|
|
428
|
+
*/
|
|
429
|
+
format() {
|
|
430
|
+
format(this.editorView.current);
|
|
431
|
+
}
|
|
138
432
|
|
|
139
433
|
/**
|
|
140
434
|
* Focus the editor
|
|
@@ -148,8 +442,17 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
148
442
|
* For example, to move the cursor to the end of the editor, use `value.length`
|
|
149
443
|
*/
|
|
150
444
|
updateCursorPosition(position: number) {
|
|
151
|
-
this.editorView.current
|
|
152
|
-
|
|
445
|
+
const view = this.editorView.current;
|
|
446
|
+
if (!view) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Clamp to the doc length; `position` may come from a raw value longer than
|
|
451
|
+
// the doc (e.g. CRLF endings), which would throw "Selection points outside
|
|
452
|
+
// of document".
|
|
453
|
+
const clamped = Math.max(0, Math.min(position, view.state.doc.length));
|
|
454
|
+
view.dispatch({
|
|
455
|
+
selection: { anchor: clamped, head: clamped },
|
|
153
456
|
});
|
|
154
457
|
}
|
|
155
458
|
|
|
@@ -158,13 +461,17 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
158
461
|
*/
|
|
159
462
|
setValueAndFocus(value = '') {
|
|
160
463
|
const currentCmValue = this.editorView.current.state?.doc.toString() ?? '';
|
|
464
|
+
const normalizedValue = normalizeLineEndings(value);
|
|
161
465
|
this.editorView.current.dispatch({
|
|
162
466
|
changes: {
|
|
163
467
|
from: 0,
|
|
164
468
|
to: currentCmValue.length,
|
|
165
|
-
insert:
|
|
469
|
+
insert: normalizedValue,
|
|
470
|
+
},
|
|
471
|
+
selection: {
|
|
472
|
+
anchor: normalizedValue.length,
|
|
473
|
+
head: normalizedValue.length,
|
|
166
474
|
},
|
|
167
|
-
selection: { anchor: value.length, head: value.length },
|
|
168
475
|
});
|
|
169
476
|
this.editorView.current?.focus();
|
|
170
477
|
}
|
|
@@ -174,11 +481,24 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
174
481
|
schema: {},
|
|
175
482
|
overrideThemeBackgroundColor: false,
|
|
176
483
|
lineWrap: false,
|
|
484
|
+
showSignatureTooltipBelow: true,
|
|
177
485
|
extraKeybindings: [],
|
|
178
486
|
history: [],
|
|
179
487
|
theme: 'light',
|
|
488
|
+
lineNumbers: true,
|
|
489
|
+
newLineOnEnter: false,
|
|
490
|
+
moveFocusOnTab: false,
|
|
180
491
|
};
|
|
181
492
|
|
|
493
|
+
private debouncedOnChange = this.props.onChange
|
|
494
|
+
? debounce(
|
|
495
|
+
((value, viewUpdate) => {
|
|
496
|
+
this.props.onChange(value, viewUpdate);
|
|
497
|
+
}) satisfies CypherEditorProps['onChange'],
|
|
498
|
+
DEBOUNCE_TIME,
|
|
499
|
+
)
|
|
500
|
+
: undefined;
|
|
501
|
+
|
|
182
502
|
componentDidMount(): void {
|
|
183
503
|
const {
|
|
184
504
|
theme,
|
|
@@ -187,20 +507,50 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
187
507
|
overrideThemeBackgroundColor,
|
|
188
508
|
schema,
|
|
189
509
|
lint,
|
|
190
|
-
|
|
510
|
+
showSignatureTooltipBelow,
|
|
511
|
+
featureFlags,
|
|
191
512
|
onExecute,
|
|
513
|
+
newLineOnEnter,
|
|
192
514
|
} = this.props;
|
|
193
515
|
|
|
194
|
-
this.schemaRef.current = {
|
|
516
|
+
this.schemaRef.current = {
|
|
517
|
+
languageService: new CypherLanguageService(),
|
|
518
|
+
schema,
|
|
519
|
+
lint,
|
|
520
|
+
showSignatureTooltipBelow,
|
|
521
|
+
featureFlags: {
|
|
522
|
+
consoleCommands: true,
|
|
523
|
+
...featureFlags,
|
|
524
|
+
},
|
|
525
|
+
useLightVersion: false,
|
|
526
|
+
setUseLightVersion: (newVal) => {
|
|
527
|
+
if (this.schemaRef.current !== undefined) {
|
|
528
|
+
this.schemaRef.current.useLightVersion = newVal;
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
this.symbolFetcher = new CodemirrorSymbolFetcher(
|
|
534
|
+
this.schemaRef.current.languageService,
|
|
535
|
+
);
|
|
195
536
|
|
|
196
537
|
const themeExtension = getThemeExtension(
|
|
197
538
|
theme,
|
|
198
539
|
overrideThemeBackgroundColor,
|
|
199
540
|
);
|
|
200
541
|
|
|
201
|
-
|
|
542
|
+
this.inlinePanelController = createInlinePanelController();
|
|
543
|
+
this.editorActionsController = createEditorActionsController();
|
|
544
|
+
|
|
545
|
+
const changeListener = this.debouncedOnChange
|
|
202
546
|
? [
|
|
203
547
|
EditorView.updateListener.of((upt: ViewUpdate) => {
|
|
548
|
+
if (upt.docChanged) {
|
|
549
|
+
this.symbolFetcher.queueSymbolJob(
|
|
550
|
+
upt.state.doc.toString(),
|
|
551
|
+
this.schemaRef.current.schema,
|
|
552
|
+
);
|
|
553
|
+
}
|
|
204
554
|
const wasUserEdit = !upt.transactions.some((tr) =>
|
|
205
555
|
tr.annotation(ExternalEdit),
|
|
206
556
|
);
|
|
@@ -208,7 +558,7 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
208
558
|
if (upt.docChanged && wasUserEdit) {
|
|
209
559
|
const doc = upt.state.doc;
|
|
210
560
|
const value = doc.toString();
|
|
211
|
-
|
|
561
|
+
this.debouncedOnChange(value, upt);
|
|
212
562
|
}
|
|
213
563
|
}),
|
|
214
564
|
]
|
|
@@ -217,26 +567,53 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
217
567
|
this.editorState.current = EditorState.create({
|
|
218
568
|
extensions: [
|
|
219
569
|
keyBindingCompartment.of(
|
|
220
|
-
keymap.of([
|
|
570
|
+
keymap.of([
|
|
571
|
+
...executeKeybinding(onExecute, newLineOnEnter, () =>
|
|
572
|
+
this.debouncedOnChange?.flush(),
|
|
573
|
+
),
|
|
574
|
+
...extraKeybindings,
|
|
575
|
+
]),
|
|
221
576
|
),
|
|
577
|
+
richClipboardCopier,
|
|
222
578
|
historyNavigation(this.props),
|
|
223
|
-
basicNeo4jSetup(),
|
|
579
|
+
basicNeo4jSetup(this.props),
|
|
224
580
|
themeCompartment.of(themeExtension),
|
|
225
581
|
changeListener,
|
|
226
582
|
cypher(this.schemaRef.current),
|
|
227
583
|
lineWrap ? EditorView.lineWrapping : [],
|
|
228
584
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
585
|
+
lineNumbersCompartment.of(
|
|
586
|
+
this.props.lineNumbers
|
|
587
|
+
? lineNumbers({ formatNumber: formatLineNumber(this.props.prompt) })
|
|
588
|
+
: [],
|
|
589
|
+
),
|
|
590
|
+
readOnlyCompartment.of(EditorState.readOnly.of(this.props.readonly)),
|
|
591
|
+
placeholderCompartment.of(
|
|
592
|
+
this.props.placeholder ? placeholder(this.props.placeholder) : [],
|
|
593
|
+
),
|
|
594
|
+
domEventHandlerCompartment.of(
|
|
595
|
+
this.props.domEventHandlers
|
|
596
|
+
? EditorView.domEventHandlers(this.props.domEventHandlers)
|
|
597
|
+
: [],
|
|
598
|
+
),
|
|
599
|
+
this.props.ariaLabel
|
|
600
|
+
? EditorView.contentAttributes.of({
|
|
601
|
+
'aria-label': this.props.ariaLabel,
|
|
602
|
+
})
|
|
603
|
+
: [],
|
|
604
|
+
!this.props.moveFocusOnTab
|
|
605
|
+
? EditorView.contentAttributes.of({
|
|
606
|
+
'aria-description':
|
|
607
|
+
'Press Escape to leave the editor and continue tabbing through the page',
|
|
608
|
+
})
|
|
609
|
+
: [],
|
|
610
|
+
this.inlinePanelController.extension,
|
|
611
|
+
diffCompartment.of(
|
|
612
|
+
this.props.diff ? createDiffExtension(this.props.diff) : [],
|
|
613
|
+
),
|
|
614
|
+
this.editorActionsController.extension,
|
|
238
615
|
],
|
|
239
|
-
doc: this.props.value,
|
|
616
|
+
doc: normalizeLineEndings(this.props.value ?? ''),
|
|
240
617
|
});
|
|
241
618
|
|
|
242
619
|
this.editorView.current = new EditorView({
|
|
@@ -249,7 +626,72 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
249
626
|
if (this.props.value) {
|
|
250
627
|
this.updateCursorPosition(this.props.value.length);
|
|
251
628
|
}
|
|
629
|
+
} else if (this.props.offset) {
|
|
630
|
+
this.updateCursorPosition(this.props.offset);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (this.props.inlinePanel) {
|
|
634
|
+
this.openInlinePanel(this.props.inlinePanel);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (this.props.editorActions != null) {
|
|
638
|
+
this.setEditorActions(true);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
private setEditorActions(active: boolean): void {
|
|
643
|
+
const view = this.editorView.current;
|
|
644
|
+
const controller = this.editorActionsController;
|
|
645
|
+
if (!view || !controller) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
view.dispatch({
|
|
650
|
+
effects: controller.set(active ? this.editorActionsCallbacks : null),
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
private openInlinePanel(
|
|
655
|
+
props: NonNullable<CypherEditorProps['inlinePanel']>,
|
|
656
|
+
): void {
|
|
657
|
+
const view = this.editorView.current;
|
|
658
|
+
const controller = this.inlinePanelController;
|
|
659
|
+
if (!view || !controller) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const pos = Math.max(0, Math.min(props.pos, view.state.doc.length));
|
|
664
|
+
const line = view.state.doc.lineAt(pos);
|
|
665
|
+
controller.updateCallbacks({
|
|
666
|
+
onMount: props.onMount,
|
|
667
|
+
onUnmount: props.onUnmount,
|
|
668
|
+
});
|
|
669
|
+
view.dispatch({
|
|
670
|
+
effects: controller.show({
|
|
671
|
+
pos: props.placement === 'below' ? line.to : line.from,
|
|
672
|
+
placement: props.placement,
|
|
673
|
+
onMount: props.onMount,
|
|
674
|
+
onUnmount: props.onUnmount,
|
|
675
|
+
}),
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
private updateInlinePanel(
|
|
680
|
+
props: NonNullable<CypherEditorProps['inlinePanel']>,
|
|
681
|
+
): void {
|
|
682
|
+
this.inlinePanelController?.updateCallbacks({
|
|
683
|
+
onMount: props.onMount,
|
|
684
|
+
onUnmount: props.onUnmount,
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
private closeInlinePanel(): void {
|
|
689
|
+
const view = this.editorView.current;
|
|
690
|
+
const controller = this.inlinePanelController;
|
|
691
|
+
if (!view || !controller) {
|
|
692
|
+
return;
|
|
252
693
|
}
|
|
694
|
+
view.dispatch({ effects: controller.hide() });
|
|
253
695
|
}
|
|
254
696
|
|
|
255
697
|
componentDidUpdate(prevProps: CypherEditorProps): void {
|
|
@@ -260,12 +702,16 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
260
702
|
// Handle externally set value
|
|
261
703
|
const currentCmValue = this.editorView.current.state?.doc.toString() ?? '';
|
|
262
704
|
|
|
263
|
-
if (
|
|
705
|
+
if (
|
|
706
|
+
this.props.value !== undefined && // If the component becomes uncontolled, we just leave the value as is
|
|
707
|
+
this.props.value !== prevProps.value && // The value prop has changed, we need to update the editor
|
|
708
|
+
this.props.value !== currentCmValue // No need to dispatch an update if the value is the same
|
|
709
|
+
) {
|
|
264
710
|
this.editorView.current.dispatch({
|
|
265
711
|
changes: {
|
|
266
712
|
from: 0,
|
|
267
713
|
to: currentCmValue.length,
|
|
268
|
-
insert: this.props.value ?? '',
|
|
714
|
+
insert: normalizeLineEndings(this.props.value ?? ''),
|
|
269
715
|
},
|
|
270
716
|
annotations: [ExternalEdit.of(true)],
|
|
271
717
|
});
|
|
@@ -288,6 +734,35 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
288
734
|
});
|
|
289
735
|
}
|
|
290
736
|
|
|
737
|
+
if (
|
|
738
|
+
prevProps.lineNumbers !== this.props.lineNumbers ||
|
|
739
|
+
prevProps.prompt !== this.props.prompt
|
|
740
|
+
) {
|
|
741
|
+
this.editorView.current.dispatch({
|
|
742
|
+
effects: lineNumbersCompartment.reconfigure(
|
|
743
|
+
this.props.lineNumbers
|
|
744
|
+
? lineNumbers({ formatNumber: formatLineNumber(this.props.prompt) })
|
|
745
|
+
: [],
|
|
746
|
+
),
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (prevProps.readonly !== this.props.readonly) {
|
|
751
|
+
this.editorView.current.dispatch({
|
|
752
|
+
effects: readOnlyCompartment.reconfigure(
|
|
753
|
+
EditorState.readOnly.of(this.props.readonly),
|
|
754
|
+
),
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (prevProps.placeholder !== this.props.placeholder) {
|
|
759
|
+
this.editorView.current.dispatch({
|
|
760
|
+
effects: placeholderCompartment.reconfigure(
|
|
761
|
+
this.props.placeholder ? placeholder(this.props.placeholder) : [],
|
|
762
|
+
),
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
|
|
291
766
|
if (
|
|
292
767
|
prevProps.extraKeybindings !== this.props.extraKeybindings ||
|
|
293
768
|
prevProps.onExecute !== this.props.onExecute
|
|
@@ -295,13 +770,57 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
295
770
|
this.editorView.current.dispatch({
|
|
296
771
|
effects: keyBindingCompartment.reconfigure(
|
|
297
772
|
keymap.of([
|
|
298
|
-
...executeKeybinding(
|
|
773
|
+
...executeKeybinding(
|
|
774
|
+
this.props.onExecute,
|
|
775
|
+
this.props.newLineOnEnter,
|
|
776
|
+
() => this.debouncedOnChange?.flush(),
|
|
777
|
+
),
|
|
299
778
|
...this.props.extraKeybindings,
|
|
300
779
|
]),
|
|
301
780
|
),
|
|
302
781
|
});
|
|
303
782
|
}
|
|
304
783
|
|
|
784
|
+
const prevPanel = prevProps.inlinePanel;
|
|
785
|
+
const nextPanel = this.props.inlinePanel;
|
|
786
|
+
if (prevPanel !== nextPanel) {
|
|
787
|
+
if (!nextPanel) {
|
|
788
|
+
this.closeInlinePanel();
|
|
789
|
+
} else if (
|
|
790
|
+
!prevPanel ||
|
|
791
|
+
prevPanel.pos !== nextPanel.pos ||
|
|
792
|
+
prevPanel.placement !== nextPanel.placement
|
|
793
|
+
) {
|
|
794
|
+
this.openInlinePanel(nextPanel);
|
|
795
|
+
} else {
|
|
796
|
+
this.updateInlinePanel(nextPanel);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (prevProps.diff?.original !== this.props.diff?.original) {
|
|
801
|
+
this.editorView.current.dispatch({
|
|
802
|
+
effects: diffCompartment.reconfigure(
|
|
803
|
+
this.props.diff ? createDiffExtension(this.props.diff) : [],
|
|
804
|
+
),
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const hadActions = prevProps.editorActions != null;
|
|
809
|
+
const hasActions = this.props.editorActions != null;
|
|
810
|
+
if (hadActions !== hasActions) {
|
|
811
|
+
this.setEditorActions(hasActions);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (prevProps.domEventHandlers !== this.props.domEventHandlers) {
|
|
815
|
+
this.editorView.current.dispatch({
|
|
816
|
+
effects: domEventHandlerCompartment.reconfigure(
|
|
817
|
+
this.props.domEventHandlers
|
|
818
|
+
? EditorView.domEventHandlers(this.props.domEventHandlers)
|
|
819
|
+
: [],
|
|
820
|
+
),
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
|
|
305
824
|
// This component rerenders on every keystroke and comparing the
|
|
306
825
|
// full lists of editor strings on every render could be expensive.
|
|
307
826
|
const didChangeHistoryEstimate =
|
|
@@ -321,10 +840,13 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
321
840
|
*/
|
|
322
841
|
this.schemaRef.current.schema = this.props.schema;
|
|
323
842
|
this.schemaRef.current.lint = this.props.lint;
|
|
843
|
+
this.schemaRef.current.featureFlags = this.props.featureFlags;
|
|
324
844
|
}
|
|
325
845
|
|
|
326
846
|
componentWillUnmount(): void {
|
|
327
847
|
this.editorView.current?.destroy();
|
|
848
|
+
this.symbolFetcher?.terminate();
|
|
849
|
+
cleanupWorkers();
|
|
328
850
|
}
|
|
329
851
|
|
|
330
852
|
render(): React.ReactNode {
|
|
@@ -333,11 +855,16 @@ export class CypherEditor extends Component<CypherEditorProps> {
|
|
|
333
855
|
const themeClass =
|
|
334
856
|
typeof theme === 'string' ? `cm-theme-${theme}` : 'cm-theme';
|
|
335
857
|
|
|
858
|
+
const { editorActionsContainer } = this.state;
|
|
859
|
+
|
|
336
860
|
return (
|
|
337
861
|
<div
|
|
338
862
|
ref={this.editorContainer}
|
|
339
863
|
className={`${themeClass}${className ? ` ${className}` : ''}`}
|
|
340
|
-
|
|
864
|
+
>
|
|
865
|
+
{editorActionsContainer &&
|
|
866
|
+
createPortal(this.props.editorActions, editorActionsContainer)}
|
|
867
|
+
</div>
|
|
341
868
|
);
|
|
342
869
|
}
|
|
343
870
|
}
|