@neo4j-cypher/react-codemirror 2.0.0-alpha.0 → 2.0.0-next.1

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE.md +201 -0
  3. package/README.md +24 -4
  4. package/dist/{index.cjs → cjs/index.cjs} +187 -74
  5. package/dist/cjs/index.cjs.map +7 -0
  6. package/{esm → dist/esm}/index.mjs +196 -66
  7. package/dist/esm/index.mjs.map +7 -0
  8. package/dist/types/CypherEditor.d.ts +118 -0
  9. package/dist/types/e2e_tests/auto-completion.spec.d.ts +1 -0
  10. package/dist/types/e2e_tests/e2e-utils.d.ts +12 -0
  11. package/dist/types/e2e_tests/extra-keybindings.spec.d.ts +1 -0
  12. package/dist/types/e2e_tests/history-navigation.spec.d.ts +1 -0
  13. package/dist/types/e2e_tests/mock-data.d.ts +3779 -0
  14. package/dist/types/e2e_tests/performance-test.spec.d.ts +1 -0
  15. package/dist/types/e2e_tests/sanity-checks.spec.d.ts +1 -0
  16. package/dist/types/e2e_tests/syntax-highlighting.spec.d.ts +1 -0
  17. package/dist/types/e2e_tests/syntax-validation.spec.d.ts +1 -0
  18. package/dist/types/icons.d.ts +2 -0
  19. package/dist/types/index.d.ts +4 -0
  20. package/dist/types/lang-cypher/ParserAdapter.d.ts +14 -0
  21. package/dist/types/lang-cypher/autocomplete.d.ts +3 -0
  22. package/dist/types/lang-cypher/constants.d.ts +31 -0
  23. package/dist/types/lang-cypher/contants.test.d.ts +1 -0
  24. package/dist/types/lang-cypher/create-cypher-theme.d.ts +26 -0
  25. package/dist/types/lang-cypher/lang-cypher.d.ts +7 -0
  26. package/dist/types/lang-cypher/syntax-validation.d.ts +3 -0
  27. package/dist/types/lang-cypher/theme-icons.d.ts +7 -0
  28. package/dist/types/ndl-tokens-copy.d.ts +379 -0
  29. package/dist/types/ndl-tokens-copy.test.d.ts +1 -0
  30. package/dist/types/neo4j-setup.d.ts +2 -0
  31. package/dist/types/repl-mode.d.ts +8 -0
  32. package/dist/types/themes.d.ts +11 -0
  33. package/dist/types/tsconfig.tsbuildinfo +1 -0
  34. package/package.json +48 -15
  35. package/src/CypherEditor.tsx +316 -0
  36. package/src/e2e_tests/auto-completion.spec.tsx +232 -0
  37. package/src/e2e_tests/e2e-utils.ts +75 -0
  38. package/src/e2e_tests/extra-keybindings.spec.tsx +57 -0
  39. package/src/e2e_tests/history-navigation.spec.tsx +144 -0
  40. package/src/e2e_tests/mock-data.ts +4310 -0
  41. package/src/e2e_tests/performance-test.spec.tsx +71 -0
  42. package/src/e2e_tests/sanity-checks.spec.tsx +87 -0
  43. package/src/e2e_tests/syntax-highlighting.spec.tsx +198 -0
  44. package/src/e2e_tests/syntax-validation.spec.tsx +157 -0
  45. package/src/icons.ts +87 -0
  46. package/src/index.ts +4 -0
  47. package/src/lang-cypher/ParserAdapter.ts +92 -0
  48. package/src/lang-cypher/autocomplete.ts +65 -0
  49. package/src/lang-cypher/constants.ts +61 -0
  50. package/src/lang-cypher/contants.test.ts +104 -0
  51. package/src/lang-cypher/create-cypher-theme.ts +207 -0
  52. package/src/lang-cypher/lang-cypher.ts +32 -0
  53. package/src/lang-cypher/syntax-validation.ts +24 -0
  54. package/src/lang-cypher/theme-icons.ts +27 -0
  55. package/src/ndl-tokens-copy.test.ts +11 -0
  56. package/src/ndl-tokens-copy.ts +379 -0
  57. package/src/neo4j-setup.tsx +129 -0
  58. package/src/repl-mode.ts +214 -0
  59. package/src/themes.ts +130 -0
@@ -1,11 +1,18 @@
1
1
  // src/index.ts
2
2
  import { CypherParser, parse } from "@neo4j-cypher/language-support";
3
- import * as ReactCodemirror from "@uiw/react-codemirror";
4
3
 
5
4
  // src/CypherEditor.tsx
6
- import { EditorView as EditorView4, keymap as keymap3 } from "@codemirror/view";
7
- import CodeEditor from "@uiw/react-codemirror";
8
- import React from "react";
5
+ import {
6
+ Annotation,
7
+ Compartment,
8
+ EditorState as EditorState2
9
+ } from "@codemirror/state";
10
+ import {
11
+ EditorView as EditorView4,
12
+ keymap as keymap3,
13
+ lineNumbers
14
+ } from "@codemirror/view";
15
+ import { Component, createRef } from "react";
9
16
 
10
17
  // src/lang-cypher/lang-cypher.ts
11
18
  import {
@@ -47,8 +54,7 @@ var completionKindToCodemirrorIcon = (c) => {
47
54
  };
48
55
  return map[c];
49
56
  };
50
- var emptySchema = {};
51
- var cypherAutocomplete = (schema) => (context) => {
57
+ var cypherAutocomplete = (config) => (context) => {
52
58
  const textUntilCursor = context.state.doc.toString().slice(0, context.pos);
53
59
  const triggerCharacters = [".", ":", "{", "$"];
54
60
  const lastCharacter = textUntilCursor.slice(-1);
@@ -58,7 +64,7 @@ var cypherAutocomplete = (schema) => (context) => {
58
64
  if (!shouldTriggerCompletion) {
59
65
  return null;
60
66
  }
61
- const options = autocomplete(textUntilCursor, schema ?? emptySchema);
67
+ const options = autocomplete(textUntilCursor, config.schema ?? {});
62
68
  return {
63
69
  from: context.matchBefore(/(\w|\$)*$/).from,
64
70
  options: options.map((o) => ({
@@ -202,17 +208,18 @@ var ParserAdapter = class extends Parser {
202
208
  import { linter } from "@codemirror/lint";
203
209
  import { validateSyntax } from "@neo4j-cypher/language-support";
204
210
  import { DiagnosticSeverity } from "vscode-languageserver-types";
205
- var cypherLinter = (schema) => linter((view) => {
206
- const diagnostics = [];
207
- validateSyntax(view.state.doc.toString(), schema).forEach((diagnostic) => {
208
- diagnostics.push({
211
+ var cypherLinter = (config) => linter((view) => {
212
+ if (!config.lint) {
213
+ return [];
214
+ }
215
+ return validateSyntax(view.state.doc.toString(), config.schema).map(
216
+ (diagnostic) => ({
209
217
  from: diagnostic.offsets.start,
210
218
  to: diagnostic.offsets.end,
211
219
  severity: diagnostic.severity === DiagnosticSeverity.Error ? "error" : "warning",
212
220
  message: diagnostic.message
213
- });
214
- });
215
- return diagnostics;
221
+ })
222
+ );
216
223
  });
217
224
 
218
225
  // src/lang-cypher/lang-cypher.ts
@@ -222,12 +229,12 @@ var facet = defineLanguageFacet({
222
229
  });
223
230
  var parserAdapter = new ParserAdapter(facet);
224
231
  var cypherLanguage = new Language(facet, parserAdapter, [], "cypher");
225
- function cypher({ lint, schema = {} }) {
232
+ function cypher(config) {
226
233
  return new LanguageSupport(cypherLanguage, [
227
234
  cypherLanguage.data.of({
228
- autocomplete: cypherAutocomplete(schema)
235
+ autocomplete: cypherAutocomplete(config)
229
236
  }),
230
- ...lint ? [cypherLinter(schema)] : []
237
+ cypherLinter(config)
231
238
  ]);
232
239
  }
233
240
 
@@ -262,7 +269,6 @@ import {
262
269
  EditorView,
263
270
  highlightSpecialChars,
264
271
  keymap,
265
- lineNumbers,
266
272
  rectangularSelection
267
273
  } from "@codemirror/view";
268
274
  import { lintKeymap } from "@codemirror/lint";
@@ -346,7 +352,7 @@ var insertTab = (cmd) => {
346
352
  }
347
353
  return true;
348
354
  };
349
- var basicNeo4jSetup = (prompt) => {
355
+ var basicNeo4jSetup = () => {
350
356
  const keymaps = [
351
357
  closeBracketsKeymap,
352
358
  defaultKeymap,
@@ -372,16 +378,6 @@ var basicNeo4jSetup = (prompt) => {
372
378
  }
373
379
  ].flat();
374
380
  const extensions = [];
375
- extensions.push(
376
- lineNumbers({
377
- formatNumber(a, state) {
378
- if (state.doc.lines === 1 && prompt !== void 0) {
379
- return prompt;
380
- }
381
- return a.toString();
382
- }
383
- })
384
- );
385
381
  extensions.push(highlightSpecialChars());
386
382
  extensions.push(history());
387
383
  extensions.push(drawSelection());
@@ -396,6 +392,7 @@ var basicNeo4jSetup = (prompt) => {
396
392
  extensions.push(
397
393
  autocompletion({
398
394
  icons: false,
395
+ interactionDelay: 5,
399
396
  addToOptions: [
400
397
  {
401
398
  render(completion, state) {
@@ -538,8 +535,7 @@ var replMode = ({
538
535
  onExecute?.(doc);
539
536
  onNewHistoryEntry?.(doc);
540
537
  view.dispatch({
541
- effects: pushToHistory.of(doc),
542
- changes: { from: 0, to: view.state.doc.length, insert: "" }
538
+ effects: pushToHistory.of(doc)
543
539
  });
544
540
  }
545
541
  return true;
@@ -1286,51 +1282,185 @@ function getThemeExtension(theme, inheritBgColor) {
1286
1282
 
1287
1283
  // src/CypherEditor.tsx
1288
1284
  import { jsx } from "react/jsx-runtime";
1289
- var CypherEditor = React.forwardRef((props, ref) => {
1290
- const {
1291
- theme = "light",
1292
- extensions = [],
1293
- prompt,
1294
- onExecute,
1295
- initialHistory = [],
1296
- onNewHistoryEntry,
1297
- extraKeybindings = [],
1298
- lineWrap = false,
1299
- overrideThemeBackgroundColor = false,
1300
- schema = {},
1301
- lint = true,
1302
- ...rest
1303
- } = props;
1304
- const maybeReplMode = onExecute ? replMode({
1305
- onExecute,
1306
- initialHistory,
1307
- onNewHistoryEntry
1308
- }) : [];
1309
- return /* @__PURE__ */ jsx(
1310
- CodeEditor,
1311
- {
1312
- ref,
1313
- theme: getThemeExtension(theme, overrideThemeBackgroundColor),
1285
+ var themeCompartment = new Compartment();
1286
+ var keyBindingCompartment = new Compartment();
1287
+ var ExternalEdit = Annotation.define();
1288
+ var CypherEditor = class extends Component {
1289
+ /**
1290
+ * The codemirror editor container.
1291
+ */
1292
+ editorContainer = createRef();
1293
+ /**
1294
+ * The codemirror editor state.
1295
+ */
1296
+ editorState = createRef();
1297
+ /**
1298
+ * The codemirror editor view.
1299
+ */
1300
+ editorView = createRef();
1301
+ schemaRef = createRef();
1302
+ /**
1303
+ * Focus the editor
1304
+ */
1305
+ focus() {
1306
+ this.editorView.current?.focus();
1307
+ }
1308
+ /**
1309
+ * Move the cursor to the supplied position.
1310
+ * For example, to move the cursor to the end of the editor, use `value.length`
1311
+ */
1312
+ updateCursorPosition(position) {
1313
+ this.editorView.current?.dispatch({
1314
+ selection: { anchor: position, head: position }
1315
+ });
1316
+ }
1317
+ /**
1318
+ * Externally set the editor value and focus the editor.
1319
+ */
1320
+ setValueAndFocus(value = "") {
1321
+ const currentCmValue = this.editorView.current.state?.doc.toString() ?? "";
1322
+ this.editorView.current.dispatch({
1323
+ changes: {
1324
+ from: 0,
1325
+ to: currentCmValue.length,
1326
+ insert: value
1327
+ },
1328
+ selection: { anchor: value.length, head: value.length }
1329
+ });
1330
+ this.editorView.current?.focus();
1331
+ }
1332
+ static defaultProps = {
1333
+ lint: true,
1334
+ schema: {},
1335
+ overrideThemeBackgroundColor: false,
1336
+ lineWrap: false,
1337
+ extraKeybindings: [],
1338
+ initialHistory: [],
1339
+ theme: "light"
1340
+ };
1341
+ componentDidMount() {
1342
+ const {
1343
+ theme,
1344
+ onExecute,
1345
+ initialHistory,
1346
+ onNewHistoryEntry,
1347
+ extraKeybindings,
1348
+ lineWrap,
1349
+ overrideThemeBackgroundColor,
1350
+ schema,
1351
+ lint,
1352
+ onChange
1353
+ } = this.props;
1354
+ this.schemaRef.current = { schema, lint };
1355
+ const maybeReplMode = onExecute ? replMode({
1356
+ onExecute,
1357
+ initialHistory,
1358
+ onNewHistoryEntry
1359
+ }) : [];
1360
+ const themeExtension = getThemeExtension(
1361
+ theme,
1362
+ overrideThemeBackgroundColor
1363
+ );
1364
+ const changeListener = onChange ? [
1365
+ EditorView4.updateListener.of((upt) => {
1366
+ const wasUserEdit = !upt.transactions.some(
1367
+ (tr) => tr.annotation(ExternalEdit)
1368
+ );
1369
+ if (upt.docChanged && wasUserEdit) {
1370
+ const doc = upt.state.doc;
1371
+ const value = doc.toString();
1372
+ onChange(value, upt);
1373
+ }
1374
+ })
1375
+ ] : [];
1376
+ this.editorState.current = EditorState2.create({
1314
1377
  extensions: [
1315
- cypher({ lint, schema }),
1316
- keymap3.of(extraKeybindings),
1317
1378
  maybeReplMode,
1318
- basicNeo4jSetup(prompt),
1379
+ basicNeo4jSetup(),
1380
+ themeCompartment.of(themeExtension),
1381
+ changeListener,
1382
+ cypher(this.schemaRef.current),
1383
+ keyBindingCompartment.of(keymap3.of(extraKeybindings)),
1319
1384
  lineWrap ? EditorView4.lineWrapping : [],
1320
- ...extensions
1385
+ lineNumbers({
1386
+ formatNumber: (a, state) => {
1387
+ if (state.doc.lines === 1 && this.props.prompt !== void 0) {
1388
+ return this.props.prompt;
1389
+ }
1390
+ return a.toString();
1391
+ }
1392
+ })
1321
1393
  ],
1322
- basicSetup: false,
1323
- indentWithTab: false,
1324
- ...rest
1394
+ doc: this.props.value
1395
+ });
1396
+ this.editorView.current = new EditorView4({
1397
+ state: this.editorState.current,
1398
+ parent: this.editorContainer.current
1399
+ });
1400
+ if (this.props.autofocus) {
1401
+ this.focus();
1402
+ if (this.props.value) {
1403
+ this.updateCursorPosition(this.props.value.length);
1404
+ }
1325
1405
  }
1326
- );
1327
- });
1406
+ }
1407
+ componentDidUpdate(prevProps) {
1408
+ if (!this.editorView.current) {
1409
+ return;
1410
+ }
1411
+ const currentCmValue = this.editorView.current.state?.doc.toString() ?? "";
1412
+ if (this.props.value !== void 0 && currentCmValue !== this.props.value) {
1413
+ this.editorView.current.dispatch({
1414
+ changes: {
1415
+ from: 0,
1416
+ to: currentCmValue.length,
1417
+ insert: this.props.value ?? ""
1418
+ },
1419
+ annotations: [ExternalEdit.of(true)]
1420
+ });
1421
+ }
1422
+ const didChangeTheme = prevProps.theme !== this.props.theme || prevProps.overrideThemeBackgroundColor !== this.props.overrideThemeBackgroundColor;
1423
+ if (didChangeTheme) {
1424
+ this.editorView.current.dispatch({
1425
+ effects: themeCompartment.reconfigure(
1426
+ getThemeExtension(
1427
+ this.props.theme,
1428
+ this.props.overrideThemeBackgroundColor
1429
+ )
1430
+ )
1431
+ });
1432
+ }
1433
+ if (prevProps.extraKeybindings !== this.props.extraKeybindings) {
1434
+ this.editorView.current.dispatch({
1435
+ effects: keyBindingCompartment.reconfigure(
1436
+ keymap3.of(this.props.extraKeybindings)
1437
+ )
1438
+ });
1439
+ }
1440
+ this.schemaRef.current.schema = this.props.schema;
1441
+ this.schemaRef.current.lint = this.props.lint;
1442
+ }
1443
+ componentWillUnmount() {
1444
+ this.editorView.current?.destroy();
1445
+ }
1446
+ render() {
1447
+ const { className, theme } = this.props;
1448
+ const themeClass = typeof theme === "string" ? `cm-theme-${theme}` : "cm-theme";
1449
+ return /* @__PURE__ */ jsx(
1450
+ "div",
1451
+ {
1452
+ ref: this.editorContainer,
1453
+ className: `${themeClass}${className ? ` ${className}` : ""}`
1454
+ }
1455
+ );
1456
+ }
1457
+ };
1328
1458
  export {
1329
1459
  CypherEditor,
1330
1460
  CypherParser,
1331
- ReactCodemirror,
1332
1461
  cypher,
1333
1462
  darkThemeConstants,
1334
1463
  lightThemeConstants,
1335
1464
  parse
1336
1465
  };
1466
+ //# sourceMappingURL=index.mjs.map