@neo4j-cypher/react-codemirror 2.0.0-next.40 → 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 +20 -0
- package/dist/src/CypherEditor.d.ts +4 -2
- package/dist/src/CypherEditor.js +2 -2
- package/dist/src/CypherEditor.js.map +1 -1
- package/dist/src/CypherEditor.test.js +19 -11
- package/dist/src/CypherEditor.test.js.map +1 -1
- package/dist/src/e2e_tests/autoCompletion.spec.js +0 -1
- package/dist/src/e2e_tests/autoCompletion.spec.js.map +1 -1
- package/dist/src/e2e_tests/historyNavigation.spec.js.map +1 -1
- package/dist/src/e2e_tests/signatureHelp.spec.js +0 -1
- package/dist/src/e2e_tests/signatureHelp.spec.js.map +1 -1
- package/dist/src/editorActions.js +83 -18
- package/dist/src/editorActions.js.map +1 -1
- package/dist/src/historyNavigation.d.ts +1 -1
- package/dist/src/historyNavigation.js.map +1 -1
- package/dist/src/lang-cypher/autocomplete.d.ts +1 -1
- package/dist/src/lang-cypher/autocomplete.js +1 -1
- package/dist/src/lang-cypher/autocomplete.js.map +1 -1
- package/dist/src/lang-cypher/constants.d.ts +2 -2
- package/dist/src/lang-cypher/constants.js.map +1 -1
- package/dist/src/lang-cypher/createCypherTheme.d.ts +2 -2
- package/dist/src/lang-cypher/createCypherTheme.js +1 -1
- package/dist/src/lang-cypher/createCypherTheme.js.map +1 -1
- package/dist/src/lang-cypher/langCypher.d.ts +2 -1
- package/dist/src/lang-cypher/langCypher.js.map +1 -1
- package/dist/src/lang-cypher/lintWorker.mjs +144 -140
- package/dist/src/lang-cypher/parser-adapter.d.ts +6 -4
- package/dist/src/lang-cypher/parser-adapter.js +4 -1
- package/dist/src/lang-cypher/parser-adapter.js.map +1 -1
- 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 +2 -2
- package/dist/src/lang-cypher/signatureHelp.js +1 -1
- package/dist/src/lang-cypher/signatureHelp.js.map +1 -1
- package/dist/src/lang-cypher/syntaxValidation.d.ts +1 -1
- package/dist/src/lang-cypher/syntaxValidation.js +7 -2
- package/dist/src/lang-cypher/syntaxValidation.js.map +1 -1
- package/dist/src/neo4jSetup.d.ts +1 -1
- package/dist/src/neo4jSetup.js.map +1 -1
- package/dist/src/richClipboardCopier.js +2 -2
- package/dist/src/richClipboardCopier.js.map +1 -1
- package/dist/src/richClipboardCopier.test.js.map +1 -1
- package/dist/src/themes.d.ts +2 -2
- package/dist/src/themes.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -10
- package/src/CypherEditor.test.tsx +27 -19
- package/src/CypherEditor.tsx +7 -16
- package/src/e2e_tests/autoCompletion.spec.tsx +0 -1
- package/src/e2e_tests/historyNavigation.spec.tsx +1 -1
- package/src/e2e_tests/signatureHelp.spec.tsx +1 -2
- package/src/editorActions.ts +103 -22
- package/src/historyNavigation.ts +5 -3
- package/src/lang-cypher/autocomplete.ts +3 -6
- package/src/lang-cypher/constants.ts +3 -2
- package/src/lang-cypher/createCypherTheme.ts +12 -10
- package/src/lang-cypher/langCypher.ts +2 -4
- package/src/lang-cypher/lintWorker.mjs +144 -140
- package/src/lang-cypher/parser-adapter.ts +10 -9
- package/src/lang-cypher/prism-global.ts +7 -0
- package/src/lang-cypher/signatureHelp.ts +7 -7
- package/src/lang-cypher/syntaxValidation.ts +9 -4
- package/src/neo4jSetup.tsx +3 -3
- package/src/richClipboardCopier.test.ts +7 -5
- package/src/richClipboardCopier.ts +2 -2
- package/src/themes.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neo4j-cypher/react-codemirror",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.41",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"codemirror",
|
|
6
6
|
"codemirror 6",
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
"CHANGELOG.md"
|
|
27
27
|
],
|
|
28
28
|
"type": "module",
|
|
29
|
-
"sideEffects":
|
|
29
|
+
"sideEffects": [
|
|
30
|
+
"**/lang-cypher/prism-global.js"
|
|
31
|
+
],
|
|
30
32
|
"main": "./dist/src/index.js",
|
|
31
33
|
"types": "./dist/src/index.d.ts",
|
|
32
34
|
"dependencies": {
|
|
@@ -46,23 +48,23 @@
|
|
|
46
48
|
"style-mod": "^4.1.2",
|
|
47
49
|
"vscode-languageserver-types": "^3.17.3",
|
|
48
50
|
"workerpool": "^9.3.3",
|
|
49
|
-
"@neo4j-cypher/language-support": "2.0.0-next.
|
|
50
|
-
"@neo4j-cypher/lint-worker": "1.10.1-next.
|
|
51
|
+
"@neo4j-cypher/language-support": "2.0.0-next.38",
|
|
52
|
+
"@neo4j-cypher/lint-worker": "1.10.1-next.15"
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
53
55
|
"@neo4j-ndl/base": "^3.2.10",
|
|
54
|
-
"@playwright/experimental-ct-react": "
|
|
55
|
-
"@playwright/test": "
|
|
56
|
+
"@playwright/experimental-ct-react": "1.62.1",
|
|
57
|
+
"@playwright/test": "1.62.1",
|
|
56
58
|
"@types/lodash.debounce": "^4.0.9",
|
|
57
59
|
"@types/react": "^18.0.28",
|
|
58
60
|
"@types/react-dom": "^18.0.11",
|
|
59
|
-
"@vitejs/plugin-react": "^
|
|
61
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
60
62
|
"copyfiles": "^2.4.1",
|
|
61
63
|
"jsdom": "^24.1.1",
|
|
62
|
-
"playwright": "
|
|
64
|
+
"playwright": "1.62.1",
|
|
63
65
|
"react": "^18.2.0",
|
|
64
66
|
"react-dom": "^18.2.0",
|
|
65
|
-
"vite": "^
|
|
67
|
+
"vite": "^8.2.2"
|
|
66
68
|
},
|
|
67
69
|
"peerDependencies": {
|
|
68
70
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
@@ -75,7 +77,7 @@
|
|
|
75
77
|
"dev": "tsc --watch",
|
|
76
78
|
"build": "pnpm copy-lint-worker && tsc --declaration --outDir dist/",
|
|
77
79
|
"copy-lint-worker": "copyfiles -u 4 ../lint-worker/dist/esm/lintWorker.mjs dist/src/lang-cypher && copyfiles -u 4 ../lint-worker/dist/esm/lintWorker.mjs src/lang-cypher",
|
|
78
|
-
"clean": "rm -rf
|
|
80
|
+
"clean": "rm -rf dist tsconfig*.tsbuildinfo",
|
|
79
81
|
"test": "vitest run",
|
|
80
82
|
"test:e2e": "playwright test -c playwright-ct.config.ts",
|
|
81
83
|
"test:e2e-ui": "playwright test -c playwright-ct.config.ts --ui",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
2
|
|
|
3
|
-
import { EditorView } from '@codemirror/view';
|
|
3
|
+
import type { EditorView } from '@codemirror/view';
|
|
4
4
|
import { createRef } from 'react';
|
|
5
5
|
import { createRoot } from 'react-dom/client';
|
|
6
6
|
import { act } from 'react-dom/test-utils';
|
|
@@ -13,22 +13,24 @@ let root: ReturnType<typeof createRoot>;
|
|
|
13
13
|
|
|
14
14
|
const ref = createRef<CypherEditor>();
|
|
15
15
|
let value = '';
|
|
16
|
-
const onChange = vi.fn(
|
|
16
|
+
const onChange = vi.fn<(v: string) => void>((v) => {
|
|
17
17
|
value = v;
|
|
18
18
|
rerender();
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
global.IS_REACT_ACT_ENVIRONMENT = true;
|
|
21
|
+
(global as any).IS_REACT_ACT_ENVIRONMENT = true;
|
|
22
22
|
const DEBOUNCE_TIME_WITH_MARGIN = DEBOUNCE_TIME + 100;
|
|
23
23
|
|
|
24
24
|
/** Avoids crash in test environment */
|
|
25
25
|
function mockEditorView(editorView: EditorView) {
|
|
26
|
-
editorView.coordsAtPos = vi
|
|
27
|
-
left:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
editorView.coordsAtPos = vi
|
|
27
|
+
.fn<() => { left: number; top: number; right: number; bottom: number }>()
|
|
28
|
+
.mockReturnValue({
|
|
29
|
+
left: 0,
|
|
30
|
+
top: 0,
|
|
31
|
+
right: 0,
|
|
32
|
+
bottom: 0,
|
|
33
|
+
});
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
async function debounce() {
|
|
@@ -107,24 +109,28 @@ test('props.value set to undefined preserves editorValue', () => {
|
|
|
107
109
|
expect(getEditorValue()).toBe('initial');
|
|
108
110
|
});
|
|
109
111
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
+
// This test documents unexpected behaviour:
|
|
113
|
+
// value updates (from outside onExecute) are overwritten by pending updates.
|
|
114
|
+
test('new props.value should cancel onChange', async () => {
|
|
112
115
|
// 1. value is updated internally
|
|
113
116
|
ref.current.setValueAndFocus('update');
|
|
114
117
|
|
|
115
118
|
// 2. editor is rerendered with a new value while a value update is still pending
|
|
119
|
+
// the update to 'new external value' is overwritten by the pending update to 'update'
|
|
116
120
|
value = 'new external value';
|
|
117
121
|
rerender();
|
|
118
122
|
|
|
119
123
|
await debounce();
|
|
120
124
|
|
|
121
|
-
//
|
|
122
|
-
expect(
|
|
123
|
-
expect(
|
|
125
|
+
// NOTE: These assertions currently check for the WRONG behavior (they are inverted).
|
|
126
|
+
expect(onChange).toHaveBeenCalled();
|
|
127
|
+
expect(getEditorValue()).not.toBe('new external value');
|
|
128
|
+
expect(value).not.toBe('new external value');
|
|
124
129
|
});
|
|
125
130
|
|
|
126
|
-
//
|
|
127
|
-
|
|
131
|
+
// This test documents unexpected behaviour:
|
|
132
|
+
// value updates (from outside onExecute) are overwritten by pending updates
|
|
133
|
+
test('new props.value set to same value should cancel onChange', async () => {
|
|
128
134
|
// 1. value is set initially
|
|
129
135
|
value = 'same value';
|
|
130
136
|
rerender();
|
|
@@ -133,14 +139,16 @@ test.fails('new props.value set to same value should cancel onChange', async ()
|
|
|
133
139
|
ref.current.setValueAndFocus('update');
|
|
134
140
|
|
|
135
141
|
// 3. editor is rerendered with a new value while a value update is still pending
|
|
142
|
+
// the update to 'same value' is overwritten by the pending update to 'update'
|
|
136
143
|
value = 'same value';
|
|
137
144
|
rerender();
|
|
138
145
|
|
|
139
146
|
await debounce();
|
|
140
147
|
|
|
141
|
-
//
|
|
142
|
-
expect(
|
|
143
|
-
expect(
|
|
148
|
+
// NOTE: These assertions currently check for the WRONG behavior (they are inverted).
|
|
149
|
+
expect(onChange).toHaveBeenCalled();
|
|
150
|
+
expect(getEditorValue()).not.toBe('same value');
|
|
151
|
+
expect(value).not.toBe('same value');
|
|
144
152
|
});
|
|
145
153
|
|
|
146
154
|
test('rerender should not cancel onChange', async () => {
|
package/src/CypherEditor.tsx
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import { insertNewline } from '@codemirror/commands';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Extension,
|
|
7
|
-
} from '@codemirror/state';
|
|
8
|
-
import {
|
|
9
|
-
EditorView,
|
|
10
|
-
KeyBinding,
|
|
11
|
-
keymap,
|
|
12
|
-
lineNumbers,
|
|
13
|
-
placeholder,
|
|
14
|
-
ViewUpdate,
|
|
15
|
-
} from '@codemirror/view';
|
|
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';
|
|
16
6
|
import {
|
|
17
7
|
createInlinePanelController,
|
|
18
8
|
type InlinePanelCallbacks,
|
|
@@ -37,12 +27,13 @@ import {
|
|
|
37
27
|
replaceHistory,
|
|
38
28
|
replMode as historyNavigation,
|
|
39
29
|
} from './historyNavigation';
|
|
40
|
-
import {
|
|
30
|
+
import type { CypherConfig } from './lang-cypher/langCypher';
|
|
31
|
+
import { cypher } from './lang-cypher/langCypher';
|
|
41
32
|
import { cleanupWorkers } from './lang-cypher/syntaxValidation';
|
|
42
33
|
import { basicNeo4jSetup } from './neo4jSetup';
|
|
43
34
|
import { getThemeExtension } from './themes';
|
|
44
35
|
import { richClipboardCopier } from './richClipboardCopier';
|
|
45
|
-
import { LintWorker } from '@neo4j-cypher/lint-worker';
|
|
36
|
+
import type { LintWorker } from '@neo4j-cypher/lint-worker';
|
|
46
37
|
import workerpool from 'workerpool';
|
|
47
38
|
|
|
48
39
|
type DomEventHandlers = Parameters<typeof EditorView.domEventHandlers>[0];
|
|
@@ -284,7 +284,7 @@ test('test onExecute', async ({ page, mount }) => {
|
|
|
284
284
|
const execButton = isMac ? 'Meta+Enter' : 'Control+Enter';
|
|
285
285
|
|
|
286
286
|
const initialValue = 'MATCH (n) RETURN n;';
|
|
287
|
-
const history = [];
|
|
287
|
+
const history: string[] = [];
|
|
288
288
|
|
|
289
289
|
const onExecute = (cmd: string) => {
|
|
290
290
|
history.unshift(cmd);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
1
|
import { testData } from '@neo4j-cypher/language-support';
|
|
3
2
|
import { expect, test } from '@playwright/experimental-ct-react';
|
|
4
|
-
import { Locator } from 'playwright/test';
|
|
3
|
+
import type { Locator } from 'playwright/test';
|
|
5
4
|
import { CypherEditor } from '../CypherEditor';
|
|
6
5
|
|
|
7
6
|
test.use({ viewport: { width: 1000, height: 500 } });
|
package/src/editorActions.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { StateEffect, StateField, type Extension } from '@codemirror/state';
|
|
|
2
2
|
import {
|
|
3
3
|
Decoration,
|
|
4
4
|
EditorView,
|
|
5
|
+
type Rect,
|
|
5
6
|
ViewPlugin,
|
|
6
7
|
type ViewUpdate,
|
|
7
8
|
WidgetType,
|
|
@@ -40,10 +41,23 @@ export type EditorActionsController = {
|
|
|
40
41
|
* `absolute` on the editor element (which does not scroll). It never moves
|
|
41
42
|
* when the document grows, when an inline panel opens above the first line,
|
|
42
43
|
* or when the content scrolls — content simply scrolls behind it.
|
|
44
|
+
*
|
|
45
|
+
* The overlay is measured against the scroller's visible box rather than
|
|
46
|
+
* against the spacer or the content: the spacer moves down the document
|
|
47
|
+
* whenever something renders above the first line (an inline panel, the
|
|
48
|
+
* deleted lines of a diff), and the content reaches past the right edge as
|
|
49
|
+
* soon as it overflows.
|
|
50
|
+
* Vertically the cluster is centred on the first line.
|
|
43
51
|
*/
|
|
44
52
|
|
|
45
53
|
const SPACER_BOTTOM_GAP = 2;
|
|
46
54
|
const SPACER_LEFT_GAP = 4;
|
|
55
|
+
/**
|
|
56
|
+
* Floor for the overlay's inset from the visible right edge, so it stays off
|
|
57
|
+
* the content's last pixel even if a theme zeroes `.cm-line`'s right padding
|
|
58
|
+
* (the base theme's 2px, which is what normally provides the inset).
|
|
59
|
+
*/
|
|
60
|
+
const MIN_OVERLAY_RIGHT_INSET = 2;
|
|
47
61
|
|
|
48
62
|
export function createEditorActionsController(): EditorActionsController {
|
|
49
63
|
const setActiveEffect = StateEffect.define<boolean>();
|
|
@@ -74,6 +88,38 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
74
88
|
return true;
|
|
75
89
|
}
|
|
76
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The spacer floats right, so CodeMirror's default — the widget's own box —
|
|
93
|
+
* puts the caret at the right edge of the cluster whenever the spacer is
|
|
94
|
+
* the only thing it can measure on the first line (an empty document).
|
|
95
|
+
* Report the start of the line's inline content instead: the node after
|
|
96
|
+
* the spacer (text, a placeholder, or CodeMirror's trailing `<br>`).
|
|
97
|
+
*/
|
|
98
|
+
coordsAt(dom: HTMLElement): Rect | null {
|
|
99
|
+
const next = dom.nextSibling;
|
|
100
|
+
if (!next) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
let rects: DOMRectList;
|
|
104
|
+
if (next instanceof Element) {
|
|
105
|
+
rects = next.getClientRects();
|
|
106
|
+
} else {
|
|
107
|
+
const range = document.createRange();
|
|
108
|
+
range.selectNodeContents(next);
|
|
109
|
+
rects = range.getClientRects();
|
|
110
|
+
}
|
|
111
|
+
const rect = rects[0];
|
|
112
|
+
if (!rect) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
left: rect.left,
|
|
117
|
+
right: rect.left,
|
|
118
|
+
top: rect.top,
|
|
119
|
+
bottom: rect.bottom,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
77
123
|
ignoreEvent(): boolean {
|
|
78
124
|
return true;
|
|
79
125
|
}
|
|
@@ -136,13 +182,11 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
136
182
|
`${container.offsetWidth}px`,
|
|
137
183
|
);
|
|
138
184
|
view.dom.style.setProperty(
|
|
139
|
-
'--cm-editor-actions-
|
|
140
|
-
`${container.
|
|
141
|
-
);
|
|
142
|
-
view.dom.style.setProperty(
|
|
143
|
-
'--cm-editor-actions-content-min-height',
|
|
144
|
-
`${container.offsetHeight + SPACER_BOTTOM_GAP}px`,
|
|
185
|
+
'--cm-editor-actions-reserved-width',
|
|
186
|
+
`${container.offsetWidth + SPACER_LEFT_GAP}px`,
|
|
145
187
|
);
|
|
188
|
+
// The spacer's height follows the overlay's position, so `align`
|
|
189
|
+
// owns it.
|
|
146
190
|
this.align(view);
|
|
147
191
|
});
|
|
148
192
|
this.resizeObserver.observe(container);
|
|
@@ -153,28 +197,52 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
153
197
|
}
|
|
154
198
|
|
|
155
199
|
private align(view: EditorView): void {
|
|
156
|
-
view.requestMeasure<{
|
|
200
|
+
view.requestMeasure<{
|
|
201
|
+
top: number;
|
|
202
|
+
right: number;
|
|
203
|
+
spacerHeight: number;
|
|
204
|
+
contentMinHeight: number;
|
|
205
|
+
} | null>({
|
|
157
206
|
read: () => {
|
|
158
207
|
if (!this.dom) {
|
|
159
208
|
return null;
|
|
160
209
|
}
|
|
161
210
|
const editorRect = view.dom.getBoundingClientRect();
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
211
|
+
const scrollerRect = view.scrollDOM.getBoundingClientRect();
|
|
212
|
+
const style = window.getComputedStyle(view.contentDOM);
|
|
213
|
+
const visibleRight = scrollerRect.left + view.scrollDOM.clientWidth;
|
|
214
|
+
// Match the inset `.cm-line`'s right padding gives the spacer, so
|
|
215
|
+
// the overlay lands on the space the spacer reserves and stays off
|
|
216
|
+
// `contentRect.right - 1`, the pixel CodeMirror hit-tests to
|
|
217
|
+
// measure a wrapped line's selection. Cover it and the first
|
|
218
|
+
// line's highlight collapses to zero width — hence the floor.
|
|
219
|
+
const line = view.contentDOM.querySelector('.cm-line');
|
|
220
|
+
const lineRightPadding = line
|
|
221
|
+
? parseFloat(window.getComputedStyle(line).paddingRight) || 0
|
|
222
|
+
: 0;
|
|
223
|
+
|
|
224
|
+
const contentPaddingTop = parseFloat(style.paddingTop) || 0;
|
|
225
|
+
const height = this.dom.offsetHeight;
|
|
226
|
+
// How far the cluster has to rise above the first line's top to
|
|
227
|
+
// sit centred on it, limited to the padding it can rise into.
|
|
228
|
+
const lift = Math.min(
|
|
229
|
+
Math.max((height - view.defaultLineHeight) / 2, 0),
|
|
230
|
+
contentPaddingTop,
|
|
231
|
+
);
|
|
232
|
+
|
|
175
233
|
return {
|
|
176
|
-
top:
|
|
177
|
-
right:
|
|
234
|
+
top: scrollerRect.top - editorRect.top + contentPaddingTop - lift,
|
|
235
|
+
right:
|
|
236
|
+
editorRect.right -
|
|
237
|
+
visibleRight +
|
|
238
|
+
(parseFloat(style.paddingRight) || 0) +
|
|
239
|
+
Math.max(lineRightPadding, MIN_OVERLAY_RIGHT_INSET),
|
|
240
|
+
// The spacer starts at the first line's top, so it only needs to
|
|
241
|
+
// clear the part of the cluster that hangs below it.
|
|
242
|
+
spacerHeight: height - lift,
|
|
243
|
+
// A floor under the content, so an empty document is still tall
|
|
244
|
+
// enough to hold the overlay.
|
|
245
|
+
contentMinHeight: contentPaddingTop - lift + height,
|
|
178
246
|
};
|
|
179
247
|
},
|
|
180
248
|
write: (pos) => {
|
|
@@ -189,6 +257,14 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
189
257
|
'--cm-editor-actions-right',
|
|
190
258
|
`${pos.right}px`,
|
|
191
259
|
);
|
|
260
|
+
view.dom.style.setProperty(
|
|
261
|
+
'--cm-editor-actions-height',
|
|
262
|
+
`${pos.spacerHeight}px`,
|
|
263
|
+
);
|
|
264
|
+
view.dom.style.setProperty(
|
|
265
|
+
'--cm-editor-actions-content-min-height',
|
|
266
|
+
`${pos.contentMinHeight}px`,
|
|
267
|
+
);
|
|
192
268
|
},
|
|
193
269
|
});
|
|
194
270
|
}
|
|
@@ -206,6 +282,7 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
206
282
|
root.style.removeProperty('--cm-editor-actions-width');
|
|
207
283
|
root.style.removeProperty('--cm-editor-actions-height');
|
|
208
284
|
root.style.removeProperty('--cm-editor-actions-content-min-height');
|
|
285
|
+
root.style.removeProperty('--cm-editor-actions-reserved-width');
|
|
209
286
|
root.style.removeProperty('--cm-editor-actions-top');
|
|
210
287
|
root.style.removeProperty('--cm-editor-actions-right');
|
|
211
288
|
}
|
|
@@ -230,6 +307,10 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
230
307
|
WebkitUserSelect: 'none',
|
|
231
308
|
pointerEvents: 'none',
|
|
232
309
|
},
|
|
310
|
+
// The spacer only clears the first line, so a panel above it makes its own room.
|
|
311
|
+
'.cm-inline-panel': {
|
|
312
|
+
paddingRight: 'var(--cm-editor-actions-reserved-width, 0px)',
|
|
313
|
+
},
|
|
233
314
|
'.cm-placeholder': {
|
|
234
315
|
display: 'inline',
|
|
235
316
|
},
|
package/src/historyNavigation.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { Extension
|
|
2
|
-
import {
|
|
1
|
+
import type { Extension } from '@codemirror/state';
|
|
2
|
+
import { StateEffect } from '@codemirror/state';
|
|
3
|
+
import type { EditorView } from '@codemirror/view';
|
|
4
|
+
import { keymap } from '@codemirror/view';
|
|
3
5
|
|
|
4
6
|
import { StateField } from '@codemirror/state';
|
|
5
7
|
|
|
@@ -10,7 +12,7 @@ type HistoryState = {
|
|
|
10
12
|
documentUpdate: string | null;
|
|
11
13
|
};
|
|
12
14
|
const DRAFT_ENTRY_INDEX = -1;
|
|
13
|
-
const historyInitialState = {
|
|
15
|
+
const historyInitialState: HistoryState = {
|
|
14
16
|
history: [],
|
|
15
17
|
index: DRAFT_ENTRY_INDEX,
|
|
16
18
|
documentUpdate: null,
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
CompletionSource,
|
|
4
|
-
snippet,
|
|
5
|
-
} from '@codemirror/autocomplete';
|
|
1
|
+
import type { Completion, CompletionSource } from '@codemirror/autocomplete';
|
|
2
|
+
import { snippet } from '@codemirror/autocomplete';
|
|
6
3
|
import { shouldAutoCompleteYield } from '@neo4j-cypher/language-support';
|
|
7
4
|
import {
|
|
8
5
|
CompletionItemKind,
|
|
9
6
|
CompletionItemTag,
|
|
10
7
|
} from 'vscode-languageserver-types';
|
|
11
|
-
import { CompletionItemIcons } from '../icons';
|
|
8
|
+
import type { CompletionItemIcons } from '../icons';
|
|
12
9
|
import type { CypherConfig } from './langCypher';
|
|
13
10
|
import { getDocString } from './utils';
|
|
14
11
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { languageDataProp } from '@codemirror/language';
|
|
2
2
|
import type { Facet } from '@codemirror/state';
|
|
3
3
|
import { NodeSet, NodeType } from '@lezer/common';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import type { Tag } from '@lezer/highlight';
|
|
5
|
+
import { styleTags, tags } from '@lezer/highlight';
|
|
6
|
+
import type { CypherTokenType } from '@neo4j-cypher/language-support';
|
|
6
7
|
|
|
7
8
|
export const cypherTokenTypeToNode = (facet: Facet<unknown>) => ({
|
|
8
9
|
topNode: NodeType.define({
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
TagStyle,
|
|
5
|
-
} from '@codemirror/language';
|
|
6
|
-
import { Extension } from '@codemirror/state';
|
|
1
|
+
import type { TagStyle } from '@codemirror/language';
|
|
2
|
+
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
3
|
+
import type { Extension } from '@codemirror/state';
|
|
7
4
|
import { EditorView } from '@codemirror/view';
|
|
8
5
|
import { CypherTokenType } from '@neo4j-cypher/language-support';
|
|
9
|
-
import { StyleSpec } from 'style-mod';
|
|
10
|
-
import { HighlightedCypherTokenTypes
|
|
6
|
+
import type { StyleSpec } from 'style-mod';
|
|
7
|
+
import type { HighlightedCypherTokenTypes } from './constants';
|
|
8
|
+
import { tokenTypeToStyleTag } from './constants';
|
|
11
9
|
import {
|
|
12
10
|
byWordSvg,
|
|
13
11
|
caseSensitiveSvg,
|
|
@@ -255,8 +253,12 @@ export const createCypherTheme = ({
|
|
|
255
253
|
|
|
256
254
|
const themeExtension = EditorView.theme(themeOptions, { dark });
|
|
257
255
|
|
|
258
|
-
const styles =
|
|
259
|
-
(
|
|
256
|
+
const styles = (
|
|
257
|
+
Object.entries(highlightStyles) as Array<
|
|
258
|
+
[HighlightedCypherTokenTypes, string]
|
|
259
|
+
>
|
|
260
|
+
).map(
|
|
261
|
+
([token, color]): TagStyle => ({
|
|
260
262
|
tag: tokenTypeToStyleTag[token],
|
|
261
263
|
color,
|
|
262
264
|
class: token === CypherTokenType.consoleCommand ? 'cm-bold' : undefined,
|
|
@@ -4,10 +4,8 @@ import {
|
|
|
4
4
|
Language,
|
|
5
5
|
LanguageSupport,
|
|
6
6
|
} from '@codemirror/language';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
type DbSchema,
|
|
10
|
-
} from '@neo4j-cypher/language-support';
|
|
7
|
+
import type { CypherLanguageService } from '@neo4j-cypher/language-support';
|
|
8
|
+
import { type DbSchema } from '@neo4j-cypher/language-support';
|
|
11
9
|
import { completionStyles, cypherAutocomplete } from './autocomplete';
|
|
12
10
|
import { ParserAdapter } from './parser-adapter';
|
|
13
11
|
import { signatureHelpTooltip } from './signatureHelp';
|