@neo4j-cypher/react-codemirror 2.0.0-next.39 → 2.0.0-next.40
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 +15 -0
- package/dist/src/CypherEditor.js +24 -8
- package/dist/src/CypherEditor.js.map +1 -1
- package/dist/src/e2e_tests/sanityChecks.spec.js +11 -0
- package/dist/src/e2e_tests/sanityChecks.spec.js.map +1 -1
- package/dist/src/editorActions.js +4 -1
- package/dist/src/editorActions.js.map +1 -1
- package/dist/src/inlinePanel.d.ts +1 -1
- package/dist/src/lang-cypher/constants.d.ts +3 -1
- package/dist/src/lang-cypher/constants.js +5 -0
- package/dist/src/lang-cypher/constants.js.map +1 -1
- package/dist/src/lang-cypher/lintWorker.mjs +148 -146
- package/dist/src/themes.d.ts +0 -6
- package/dist/src/themes.js +2 -2
- package/dist/src/themes.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -6
- package/src/CypherEditor.tsx +27 -8
- package/src/e2e_tests/sanityChecks.spec.tsx +23 -0
- package/src/editorActions.ts +4 -1
- package/src/inlinePanel.ts +1 -1
- package/src/lang-cypher/constants.ts +6 -1
- package/src/lang-cypher/lintWorker.mjs +148 -146
- 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.40",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"codemirror",
|
|
6
6
|
"codemirror 6",
|
|
@@ -41,15 +41,13 @@
|
|
|
41
41
|
"@lezer/common": "^1.0.2",
|
|
42
42
|
"@lezer/highlight": "^1.1.3",
|
|
43
43
|
"@types/prismjs": "^1.26.3",
|
|
44
|
-
"@types/workerpool": "^6.4.7",
|
|
45
|
-
"fastest-levenshtein": "^1.0.16",
|
|
46
44
|
"lodash.debounce": "^4.0.8",
|
|
47
45
|
"prismjs": "^1.29.0",
|
|
48
46
|
"style-mod": "^4.1.2",
|
|
49
47
|
"vscode-languageserver-types": "^3.17.3",
|
|
50
48
|
"workerpool": "^9.3.3",
|
|
51
|
-
"@neo4j-cypher/
|
|
52
|
-
"@neo4j-cypher/
|
|
49
|
+
"@neo4j-cypher/language-support": "2.0.0-next.37",
|
|
50
|
+
"@neo4j-cypher/lint-worker": "1.10.1-next.14"
|
|
53
51
|
},
|
|
54
52
|
"devDependencies": {
|
|
55
53
|
"@neo4j-ndl/base": "^3.2.10",
|
|
@@ -61,7 +59,6 @@
|
|
|
61
59
|
"@vitejs/plugin-react": "^4.3.1",
|
|
62
60
|
"copyfiles": "^2.4.1",
|
|
63
61
|
"jsdom": "^24.1.1",
|
|
64
|
-
"lodash": "^4.17.21",
|
|
65
62
|
"playwright": "^1.55.1",
|
|
66
63
|
"react": "^18.2.0",
|
|
67
64
|
"react-dom": "^18.2.0",
|
package/src/CypherEditor.tsx
CHANGED
|
@@ -46,6 +46,19 @@ import { LintWorker } from '@neo4j-cypher/lint-worker';
|
|
|
46
46
|
import workerpool from 'workerpool';
|
|
47
47
|
|
|
48
48
|
type DomEventHandlers = Parameters<typeof EditorView.domEventHandlers>[0];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Normalize CRLF line endings to LF, which is what CodeMirror expects.
|
|
52
|
+
* CodeMirror collapses `\r\n` into a single line break, so a raw `value`
|
|
53
|
+
* string with CRLF endings is longer than the resulting document. Normalizing
|
|
54
|
+
* up front keeps the document length in sync with the value length used for
|
|
55
|
+
* cursor placement, and avoids "Selection points outside of document" errors.
|
|
56
|
+
* https://codemirror.net/docs/ref/#state.EditorState^lineSeparator
|
|
57
|
+
*/
|
|
58
|
+
function normalizeLineEndings(value: string): string {
|
|
59
|
+
return value.replace(/\r\n/g, '\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
49
62
|
export interface CypherEditorProps {
|
|
50
63
|
/**
|
|
51
64
|
* The prompt to show on single line editors
|
|
@@ -438,8 +451,17 @@ export class CypherEditor extends Component<
|
|
|
438
451
|
* For example, to move the cursor to the end of the editor, use `value.length`
|
|
439
452
|
*/
|
|
440
453
|
updateCursorPosition(position: number) {
|
|
441
|
-
this.editorView.current
|
|
442
|
-
|
|
454
|
+
const view = this.editorView.current;
|
|
455
|
+
if (!view) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Clamp to the doc length; `position` may come from a raw value longer than
|
|
460
|
+
// the doc (e.g. CRLF endings), which would throw "Selection points outside
|
|
461
|
+
// of document".
|
|
462
|
+
const clamped = Math.max(0, Math.min(position, view.state.doc.length));
|
|
463
|
+
view.dispatch({
|
|
464
|
+
selection: { anchor: clamped, head: clamped },
|
|
443
465
|
});
|
|
444
466
|
}
|
|
445
467
|
|
|
@@ -448,10 +470,7 @@ export class CypherEditor extends Component<
|
|
|
448
470
|
*/
|
|
449
471
|
setValueAndFocus(value = '') {
|
|
450
472
|
const currentCmValue = this.editorView.current.state?.doc.toString() ?? '';
|
|
451
|
-
|
|
452
|
-
// Prevents issues with inserted values that contain CRLF line endings.
|
|
453
|
-
// https://codemirror.net/docs/ref/?utm_source=chatgpt.com#state.EditorState^lineSeparator
|
|
454
|
-
const normalizedValue = value.replace(/\r\n/g, '\n');
|
|
473
|
+
const normalizedValue = normalizeLineEndings(value);
|
|
455
474
|
this.editorView.current.dispatch({
|
|
456
475
|
changes: {
|
|
457
476
|
from: 0,
|
|
@@ -603,7 +622,7 @@ export class CypherEditor extends Component<
|
|
|
603
622
|
),
|
|
604
623
|
this.editorActionsController.extension,
|
|
605
624
|
],
|
|
606
|
-
doc: this.props.value,
|
|
625
|
+
doc: normalizeLineEndings(this.props.value ?? ''),
|
|
607
626
|
});
|
|
608
627
|
|
|
609
628
|
this.editorView.current = new EditorView({
|
|
@@ -701,7 +720,7 @@ export class CypherEditor extends Component<
|
|
|
701
720
|
changes: {
|
|
702
721
|
from: 0,
|
|
703
722
|
to: currentCmValue.length,
|
|
704
|
-
insert: this.props.value ?? '',
|
|
723
|
+
insert: normalizeLineEndings(this.props.value ?? ''),
|
|
705
724
|
},
|
|
706
725
|
annotations: [ExternalEdit.of(true)],
|
|
707
726
|
});
|
|
@@ -42,6 +42,29 @@ test('the editor can report changes to the text ', async ({ mount, page }) => {
|
|
|
42
42
|
}).toPass({ intervals: [300, 300, 1000] });
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
test('can mount an autofocused editor with CRLF line endings without crashing', async ({
|
|
46
|
+
mount,
|
|
47
|
+
}) => {
|
|
48
|
+
const value = 'MATCH (n)\r\nRETURN n;';
|
|
49
|
+
|
|
50
|
+
const component = await mount(<CypherEditor value={value} autofocus />);
|
|
51
|
+
|
|
52
|
+
await expect(component).toContainText('MATCH (n)');
|
|
53
|
+
await expect(component).toContainText('RETURN n;');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('can externally update to a value with CRLF line endings without crashing', async ({
|
|
57
|
+
mount,
|
|
58
|
+
}) => {
|
|
59
|
+
const component = await mount(<CypherEditor value="MATCH (n)" autofocus />);
|
|
60
|
+
|
|
61
|
+
await component.update(
|
|
62
|
+
<CypherEditor value={'MATCH (n)\r\nRETURN n;'} autofocus />,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
await expect(component).toContainText('RETURN n;');
|
|
66
|
+
});
|
|
67
|
+
|
|
45
68
|
test('can complete RETURN', async ({ page, mount }) => {
|
|
46
69
|
await mount(<CypherEditor />);
|
|
47
70
|
const textField = page.getByRole('textbox');
|
package/src/editorActions.ts
CHANGED
|
@@ -82,7 +82,7 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
82
82
|
const spacer = EditorView.decorations.compute(
|
|
83
83
|
['doc', activeField],
|
|
84
84
|
(state) => {
|
|
85
|
-
if (!state.field(activeField)
|
|
85
|
+
if (!state.field(activeField)) {
|
|
86
86
|
return Decoration.none;
|
|
87
87
|
}
|
|
88
88
|
return Decoration.set([
|
|
@@ -230,6 +230,9 @@ export function createEditorActionsController(): EditorActionsController {
|
|
|
230
230
|
WebkitUserSelect: 'none',
|
|
231
231
|
pointerEvents: 'none',
|
|
232
232
|
},
|
|
233
|
+
'.cm-placeholder': {
|
|
234
|
+
display: 'inline',
|
|
235
|
+
},
|
|
233
236
|
'.cm-editor-actions': {
|
|
234
237
|
position: 'absolute',
|
|
235
238
|
top: 'var(--cm-editor-actions-top, 0px)',
|
package/src/inlinePanel.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { HostPortalCallbacks } from './hostCallbacks';
|
|
|
11
11
|
/** Lifecycle callbacks for an inline panel. See {@link HostPortalCallbacks}. */
|
|
12
12
|
export type InlinePanelCallbacks = HostPortalCallbacks;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
type InlinePanelShowOptions = {
|
|
15
15
|
/** Document position the panel anchors to. */
|
|
16
16
|
pos: number;
|
|
17
17
|
/**
|
|
@@ -42,9 +42,13 @@ export const cypherTokenTypeToNode = (facet: Facet<unknown>) => ({
|
|
|
42
42
|
number: NodeType.define({ id: 28, name: 'numberLiteral' }),
|
|
43
43
|
setting: NodeType.define({ id: 29, name: 'setting' }),
|
|
44
44
|
settingValue: NodeType.define({ id: 30, name: 'settingValue' }),
|
|
45
|
+
interpolationDelimiter: NodeType.define({
|
|
46
|
+
id: 31,
|
|
47
|
+
name: 'interpolationDelimiter',
|
|
48
|
+
}),
|
|
45
49
|
});
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
type PrismSpecificTokenType =
|
|
48
52
|
| 'class-name'
|
|
49
53
|
| 'identifier'
|
|
50
54
|
| 'string'
|
|
@@ -68,6 +72,7 @@ export const tokenTypeToStyleTag: Record<HighlightedCypherTokenTypes, Tag> = {
|
|
|
68
72
|
variable: tags.variableName,
|
|
69
73
|
paramDollar: tags.atom,
|
|
70
74
|
paramValue: tags.atom,
|
|
75
|
+
interpolationDelimiter: tags.atom,
|
|
71
76
|
symbolicName: tags.variableName,
|
|
72
77
|
operator: tags.operator,
|
|
73
78
|
stringLiteral: tags.string,
|