@neo4j-cypher/react-codemirror 2.0.0-next.32 → 2.0.0-next.33
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 +10 -0
- package/dist/src/CypherEditor.js +1 -1
- package/dist/src/CypherEditor.js.map +1 -1
- package/dist/src/CypherEditor.test.js.map +1 -1
- package/dist/src/e2e_tests/autoCompletion.spec.js +6 -3
- package/dist/src/e2e_tests/autoCompletion.spec.js.map +1 -1
- package/dist/src/e2e_tests/debounce.spec.js +1 -1
- package/dist/src/e2e_tests/debounce.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/e2e_tests/snippets.spec.js.map +1 -1
- package/dist/src/e2e_tests/syntaxValidation.spec.js +10 -4
- package/dist/src/e2e_tests/syntaxValidation.spec.js.map +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/createCypherTheme.js.map +1 -1
- package/dist/src/lang-cypher/lintWorker.mjs +138 -139
- package/dist/src/lang-cypher/parser-adapter.js.map +1 -1
- package/dist/src/lang-cypher/utils.js +1 -1
- package/dist/src/lang-cypher/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -24
- package/src/CypherEditor.test.tsx +18 -19
- package/src/CypherEditor.tsx +1 -1
- package/src/e2e_tests/autoCompletion.spec.tsx +13 -7
- package/src/e2e_tests/debounce.spec.tsx +32 -36
- package/src/e2e_tests/signatureHelp.spec.tsx +0 -1
- package/src/e2e_tests/snippets.spec.tsx +0 -1
- package/src/e2e_tests/syntaxValidation.spec.tsx +21 -13
- package/src/lang-cypher/autocomplete.ts +1 -1
- package/src/lang-cypher/createCypherTheme.ts +7 -21
- package/src/lang-cypher/lintWorker.mjs +138 -139
- package/src/lang-cypher/parser-adapter.ts +4 -1
- package/src/lang-cypher/utils.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neo4j-cypher/react-codemirror",
|
|
3
|
+
"version": "2.0.0-next.33",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"codemirror",
|
|
6
|
+
"codemirror 6",
|
|
7
|
+
"cypher",
|
|
8
|
+
"editor",
|
|
9
|
+
"neo4j",
|
|
10
|
+
"react"
|
|
11
|
+
],
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/neo4j/cypher-language-support/issues"
|
|
14
|
+
},
|
|
3
15
|
"license": "Apache-2.0",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git://github.com/neo4j/cypher-language-support.git"
|
|
19
|
+
},
|
|
4
20
|
"files": [
|
|
5
21
|
"dist",
|
|
6
22
|
"src",
|
|
@@ -9,30 +25,10 @@
|
|
|
9
25
|
"LICENSE.md",
|
|
10
26
|
"CHANGELOG.md"
|
|
11
27
|
],
|
|
12
|
-
"keywords": [
|
|
13
|
-
"neo4j",
|
|
14
|
-
"cypher",
|
|
15
|
-
"react",
|
|
16
|
-
"editor",
|
|
17
|
-
"codemirror",
|
|
18
|
-
"codemirror 6"
|
|
19
|
-
],
|
|
20
|
-
"version": "2.0.0-next.32",
|
|
21
|
-
"main": "./dist/src/index.js",
|
|
22
|
-
"types": "./dist/src/index.d.ts",
|
|
23
28
|
"type": "module",
|
|
24
29
|
"sideEffects": false,
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"url": "git://github.com/neo4j/cypher-language-support.git"
|
|
28
|
-
},
|
|
29
|
-
"bugs": {
|
|
30
|
-
"url": "https://github.com/neo4j/cypher-language-support/issues"
|
|
31
|
-
},
|
|
32
|
-
"engineStrict": true,
|
|
33
|
-
"engines": {
|
|
34
|
-
"node": ">=24.11.1"
|
|
35
|
-
},
|
|
30
|
+
"main": "./dist/src/index.js",
|
|
31
|
+
"types": "./dist/src/index.d.ts",
|
|
36
32
|
"dependencies": {
|
|
37
33
|
"@codemirror/autocomplete": "^6.18.6",
|
|
38
34
|
"@codemirror/commands": "^6.8.1",
|
|
@@ -51,8 +47,8 @@
|
|
|
51
47
|
"style-mod": "^4.1.2",
|
|
52
48
|
"vscode-languageserver-types": "^3.17.3",
|
|
53
49
|
"workerpool": "^9.3.3",
|
|
54
|
-
"@neo4j-cypher/language-support": "2.0.0-next.
|
|
55
|
-
"@neo4j-cypher/lint-worker": "1.10.1-next.
|
|
50
|
+
"@neo4j-cypher/language-support": "2.0.0-next.30",
|
|
51
|
+
"@neo4j-cypher/lint-worker": "1.10.1-next.7"
|
|
56
52
|
},
|
|
57
53
|
"devDependencies": {
|
|
58
54
|
"@neo4j-ndl/base": "^3.2.10",
|
|
@@ -73,6 +69,10 @@
|
|
|
73
69
|
"peerDependencies": {
|
|
74
70
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
75
71
|
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=24.11.1"
|
|
74
|
+
},
|
|
75
|
+
"engineStrict": true,
|
|
76
76
|
"scripts": {
|
|
77
77
|
"dev": "tsc --watch",
|
|
78
78
|
"build": "pnpm copy-lint-worker && tsc --declaration --outDir dist/",
|
|
@@ -124,27 +124,24 @@ test.fails('new props.value should cancel onChange', async () => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
// value updates from outside onExecute are overwritten by pending updates
|
|
127
|
-
test.fails(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
value = 'same value';
|
|
132
|
-
rerender();
|
|
127
|
+
test.fails('new props.value set to same value should cancel onChange', async () => {
|
|
128
|
+
// 1. value is set initially
|
|
129
|
+
value = 'same value';
|
|
130
|
+
rerender();
|
|
133
131
|
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
// 2. value is updated internally
|
|
133
|
+
ref.current.setValueAndFocus('update');
|
|
136
134
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
// 3. editor is rerendered with a new value while a value update is still pending
|
|
136
|
+
value = 'same value';
|
|
137
|
+
rerender();
|
|
140
138
|
|
|
141
|
-
|
|
139
|
+
await debounce();
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
);
|
|
141
|
+
// expect(onChange).not.toHaveBeenCalled();
|
|
142
|
+
expect(getEditorValue()).toBe('same value');
|
|
143
|
+
expect(value).toBe('same value');
|
|
144
|
+
});
|
|
148
145
|
|
|
149
146
|
test('rerender should not cancel onChange', async () => {
|
|
150
147
|
// 1. value is updated internally
|
|
@@ -200,5 +197,7 @@ test('rerender with prior external update should not cancel onChange', async ()
|
|
|
200
197
|
});
|
|
201
198
|
|
|
202
199
|
test('setValueAndFocus should handle CRLF newline characters', () => {
|
|
203
|
-
expect(() =>
|
|
204
|
-
|
|
200
|
+
expect(() =>
|
|
201
|
+
ref.current.setValueAndFocus('new value\r\nnew line'),
|
|
202
|
+
).not.toThrow();
|
|
203
|
+
});
|
package/src/CypherEditor.tsx
CHANGED
|
@@ -33,7 +33,10 @@ RETURN n;`}
|
|
|
33
33
|
await textField.press('Control+ ');
|
|
34
34
|
|
|
35
35
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible();
|
|
36
|
-
await page
|
|
36
|
+
await page
|
|
37
|
+
.locator('.cm-tooltip-autocomplete')
|
|
38
|
+
.getByText('WHERE', { exact: true })
|
|
39
|
+
.click();
|
|
37
40
|
|
|
38
41
|
await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible();
|
|
39
42
|
|
|
@@ -544,13 +547,16 @@ test('completions depend on the Cypher version', async ({ page, mount }) => {
|
|
|
544
547
|
).toBeVisible();
|
|
545
548
|
});
|
|
546
549
|
|
|
547
|
-
test('does not complete properties for non node / relationship variables', async ({
|
|
550
|
+
test('does not complete properties for non node / relationship variables', async ({
|
|
551
|
+
page,
|
|
552
|
+
mount,
|
|
553
|
+
}) => {
|
|
548
554
|
await mount(
|
|
549
555
|
<CypherEditor
|
|
550
|
-
|
|
551
|
-
propertyKeys: [
|
|
556
|
+
schema={{
|
|
557
|
+
propertyKeys: ['nodeProperty'],
|
|
552
558
|
}}
|
|
553
|
-
|
|
559
|
+
/>,
|
|
554
560
|
);
|
|
555
561
|
|
|
556
562
|
const textField = page.getByRole('textbox');
|
|
@@ -562,10 +568,10 @@ test('does not complete properties for non node / relationship variables', async
|
|
|
562
568
|
|
|
563
569
|
await textField.fill('WITH 1 AS x RETURN x.');
|
|
564
570
|
// This could be flaky if the semantic analysis takes too long
|
|
565
|
-
await page.waitForTimeout(500)
|
|
571
|
+
await page.waitForTimeout(500);
|
|
566
572
|
await textField.press('Escape');
|
|
567
573
|
await textField.press('Control+ ');
|
|
568
574
|
await expect(
|
|
569
|
-
page.locator('.cm-tooltip-autocomplete').getByText('nodeProperty')
|
|
575
|
+
page.locator('.cm-tooltip-autocomplete').getByText('nodeProperty'),
|
|
570
576
|
).not.toBeVisible();
|
|
571
577
|
});
|
|
@@ -28,47 +28,43 @@ test.fail(
|
|
|
28
28
|
);
|
|
29
29
|
|
|
30
30
|
// TODO Fix this test
|
|
31
|
-
test.fixme(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const onExecute = () => {
|
|
38
|
-
value = '';
|
|
39
|
-
void component.update(
|
|
40
|
-
<CypherEditor
|
|
41
|
-
value={value}
|
|
42
|
-
onChange={onChange}
|
|
43
|
-
onExecute={onExecute}
|
|
44
|
-
/>,
|
|
45
|
-
);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const onChange = (val: string) => {
|
|
49
|
-
value = val;
|
|
50
|
-
void component.update(
|
|
51
|
-
<CypherEditor value={val} onChange={onChange} onExecute={onExecute} />,
|
|
52
|
-
);
|
|
53
|
-
};
|
|
31
|
+
test.fixme('onExecute updates should override debounce updates', async ({
|
|
32
|
+
mount,
|
|
33
|
+
page,
|
|
34
|
+
}) => {
|
|
35
|
+
const editorPage = new CypherEditorPage(page);
|
|
36
|
+
let value = '';
|
|
54
37
|
|
|
55
|
-
|
|
38
|
+
const onExecute = () => {
|
|
39
|
+
value = '';
|
|
40
|
+
void component.update(
|
|
56
41
|
<CypherEditor value={value} onChange={onChange} onExecute={onExecute} />,
|
|
57
42
|
);
|
|
43
|
+
};
|
|
58
44
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
const onChange = (val: string) => {
|
|
46
|
+
value = val;
|
|
47
|
+
void component.update(
|
|
48
|
+
<CypherEditor value={val} onChange={onChange} onExecute={onExecute} />,
|
|
49
|
+
);
|
|
50
|
+
};
|
|
63
51
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
52
|
+
const component = await mount(
|
|
53
|
+
<CypherEditor value={value} onChange={onChange} onExecute={onExecute} />,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
await editorPage.getEditor().pressSequentially('RETURN 1');
|
|
57
|
+
await editorPage.getEditor().press('Enter');
|
|
58
|
+
await page.waitForTimeout(DEBOUNCE_TIME_WITH_MARGIN);
|
|
59
|
+
await expect(component).not.toContainText('RETURN 1');
|
|
60
|
+
|
|
61
|
+
await editorPage.getEditor().pressSequentially('RETURN 1');
|
|
62
|
+
await editorPage.getEditor().pressSequentially('');
|
|
63
|
+
await editorPage.getEditor().pressSequentially('RETURN 1');
|
|
64
|
+
await editorPage.getEditor().press('Enter');
|
|
65
|
+
await page.waitForTimeout(DEBOUNCE_TIME_WITH_MARGIN);
|
|
66
|
+
await expect(component).not.toContainText('RETURN 1');
|
|
67
|
+
});
|
|
72
68
|
|
|
73
69
|
test('onExecute should fire after debounced updates', async ({
|
|
74
70
|
mount,
|
|
@@ -182,9 +182,9 @@ test('Validation errors are correctly overlapped', async ({ page, mount }) => {
|
|
|
182
182
|
);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
-
test('Syntax highlighting works as expected with multiple separate linting messages', async (
|
|
185
|
+
test('Syntax highlighting works as expected with multiple separate linting messages', async ({
|
|
186
186
|
page,
|
|
187
|
-
mount
|
|
187
|
+
mount,
|
|
188
188
|
}) => {
|
|
189
189
|
const editorPage = new CypherEditorPage(page);
|
|
190
190
|
const query = `MATCH (n)--(m) CALL (n) {RETURN id(n) AS b} RETURN apoc.create.uuid(), a`;
|
|
@@ -192,12 +192,20 @@ test('Syntax highlighting works as expected with multiple separate linting messa
|
|
|
192
192
|
await mount(<CypherEditor value={query} schema={testData.mockSchema} />);
|
|
193
193
|
await expect(
|
|
194
194
|
editorPage.page.locator('.cm-deprecated-element').last(),
|
|
195
|
-
).toBeVisible({
|
|
195
|
+
).toBeVisible({
|
|
196
|
+
timeout: 10000,
|
|
197
|
+
});
|
|
196
198
|
await editorPage.checkWarningMessage('id', 'Function id is deprecated.');
|
|
197
|
-
await editorPage.checkWarningMessage(
|
|
198
|
-
|
|
199
|
+
await editorPage.checkWarningMessage(
|
|
200
|
+
'id',
|
|
201
|
+
`The query used a deprecated function. ('id' has been replaced by 'elementId or consider using an application-generated id')`,
|
|
202
|
+
);
|
|
203
|
+
await editorPage.checkWarningMessage(
|
|
204
|
+
'apoc.create.uuid',
|
|
205
|
+
'Function apoc.create.uuid is deprecated. Alternative: Neo4j randomUUID() function',
|
|
206
|
+
);
|
|
199
207
|
await editorPage.checkErrorMessage('a', 'Variable `a` not defined');
|
|
200
|
-
})
|
|
208
|
+
});
|
|
201
209
|
|
|
202
210
|
test('Strikethroughs are shown for deprecated functions', async ({
|
|
203
211
|
page,
|
|
@@ -209,7 +217,9 @@ test('Strikethroughs are shown for deprecated functions', async ({
|
|
|
209
217
|
await mount(<CypherEditor value={query} schema={testData.mockSchema} />);
|
|
210
218
|
await expect(
|
|
211
219
|
editorPage.page.locator('.cm-deprecated-element').last(),
|
|
212
|
-
).toBeVisible({
|
|
220
|
+
).toBeVisible({
|
|
221
|
+
timeout: 10000,
|
|
222
|
+
});
|
|
213
223
|
await editorPage.checkWarningMessage('id', 'Function id is deprecated.');
|
|
214
224
|
});
|
|
215
225
|
|
|
@@ -223,7 +233,9 @@ test('Strikethroughs are shown for deprecated procedures', async ({
|
|
|
223
233
|
await mount(<CypherEditor value={query} schema={testData.mockSchema} />);
|
|
224
234
|
await expect(
|
|
225
235
|
editorPage.page.locator('.cm-deprecated-element').last(),
|
|
226
|
-
).toBeVisible({
|
|
236
|
+
).toBeVisible({
|
|
237
|
+
timeout: 10000,
|
|
238
|
+
});
|
|
227
239
|
|
|
228
240
|
await editorPage.checkWarningMessage(
|
|
229
241
|
'apoc.create.uuids',
|
|
@@ -235,11 +247,7 @@ test('Syntax validation depends on the Cypher version', async ({
|
|
|
235
247
|
page,
|
|
236
248
|
mount,
|
|
237
249
|
}) => {
|
|
238
|
-
await mount(
|
|
239
|
-
<CypherEditor
|
|
240
|
-
schema={testData.mockSchema}
|
|
241
|
-
/>,
|
|
242
|
-
);
|
|
250
|
+
await mount(<CypherEditor schema={testData.mockSchema} />);
|
|
243
251
|
|
|
244
252
|
const editorPage = new CypherEditorPage(page);
|
|
245
253
|
const textField = page.getByRole('textbox');
|
|
@@ -62,7 +62,7 @@ export const cypherAutocomplete: (config: CypherConfig) => CompletionSource =
|
|
|
62
62
|
(config) => (context) => {
|
|
63
63
|
const documentText = context.state.doc.toString();
|
|
64
64
|
const offset = context.pos;
|
|
65
|
-
const triggerCharacters = ['.', ':', '{', '$', ')', ']'];
|
|
65
|
+
const triggerCharacters = ['.', ':', '{', '$', ')', ']', '-', '<'];
|
|
66
66
|
const lastCharacter = documentText.at(offset - 1);
|
|
67
67
|
const yieldTriggered = shouldAutoCompleteYield(documentText, offset);
|
|
68
68
|
const lastWord = context.matchBefore(/\w*/);
|
|
@@ -160,24 +160,16 @@ export const createCypherTheme = ({
|
|
|
160
160
|
border: 'none',
|
|
161
161
|
verticalAlign: 'middle',
|
|
162
162
|
'&[name=next]::before': {
|
|
163
|
-
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
164
|
-
downArrowSvg,
|
|
165
|
-
)}")`,
|
|
163
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(downArrowSvg)}")`,
|
|
166
164
|
},
|
|
167
165
|
'&[name=prev]::before': {
|
|
168
|
-
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
169
|
-
upArrowSvg,
|
|
170
|
-
)}")`,
|
|
166
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(upArrowSvg)}")`,
|
|
171
167
|
},
|
|
172
168
|
'&[name=replace]::before': {
|
|
173
|
-
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
174
|
-
replaceSvg,
|
|
175
|
-
)}")`,
|
|
169
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(replaceSvg)}")`,
|
|
176
170
|
},
|
|
177
171
|
'&[name=replaceAll]::before': {
|
|
178
|
-
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
179
|
-
replaceAllSvg,
|
|
180
|
-
)}")`,
|
|
172
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(replaceAllSvg)}")`,
|
|
181
173
|
},
|
|
182
174
|
width: '20px',
|
|
183
175
|
height: '20px',
|
|
@@ -209,19 +201,13 @@ export const createCypherTheme = ({
|
|
|
209
201
|
borderRadius: '4px',
|
|
210
202
|
|
|
211
203
|
'&[name=case]::before': {
|
|
212
|
-
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
213
|
-
caseSensitiveSvg,
|
|
214
|
-
)}")`,
|
|
204
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(caseSensitiveSvg)}")`,
|
|
215
205
|
},
|
|
216
206
|
'&[name=re]::before': {
|
|
217
|
-
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
218
|
-
regexSvg,
|
|
219
|
-
)}")`,
|
|
207
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(regexSvg)}")`,
|
|
220
208
|
},
|
|
221
209
|
'&[name=word]::before': {
|
|
222
|
-
content: `url("data:image/svg+xml;base64,${window.btoa(
|
|
223
|
-
byWordSvg,
|
|
224
|
-
)}")`,
|
|
210
|
+
content: `url("data:image/svg+xml;base64,${window.btoa(byWordSvg)}")`,
|
|
225
211
|
},
|
|
226
212
|
'&:hover': {
|
|
227
213
|
backgroundColor: settings.searchPanel.buttonHoverBackground,
|