react-dsl-editor 0.4.3 → 0.4.5

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/dist/index.d.ts CHANGED
@@ -28,7 +28,7 @@ export declare interface DSL<T extends string> {
28
28
  errors: DSLError[];
29
29
  }
30
30
 
31
- export declare function DslEditor<T extends string>({ code, onChange, onParsed, grammar, wrap, tooltipProps, suggestions: clientSuggestions, className, syntaxColors, ...textareaProps }: {
31
+ export declare function DslEditor<T extends string>({ code, onChange, onParsed, grammar, wrap, tooltipProps, suggestions: clientSuggestions, validate, className, syntaxColors, ...textareaProps }: {
32
32
  code: string;
33
33
  onChange: (text: string) => void;
34
34
  onParsed?: (dsl: DSL<T>) => void;
@@ -36,6 +36,7 @@ export declare function DslEditor<T extends string>({ code, onChange, onParsed,
36
36
  wrap?: boolean;
37
37
  className?: string;
38
38
  tooltipProps?: HTMLAttributes<HTMLElement>;
39
+ validate?: (node: T, text: string) => string | undefined;
39
40
  suggestions?: (node: CSTNode<T>) => string[] | undefined;
40
41
  syntaxColors?: SyntaxColorsProvider;
41
42
  } & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'wrap' | 'onChange'>): JSX.Element;
@@ -45,7 +46,6 @@ export declare interface DSLError {
45
46
  expected?: string[];
46
47
  start: number;
47
48
  end: number;
48
- depth: number;
49
49
  }
50
50
 
51
51
  export declare class DSLParser<T extends string> {
@@ -54,6 +54,8 @@ export declare class DSLParser<T extends string> {
54
54
  private _parseStrict;
55
55
  parseStrict(input: string): ParserSuccess<T> | undefined;
56
56
  parse(input: string): DSL<T>;
57
+ protected validate(node: T, text: string): string | undefined;
58
+ private validateCST;
57
59
  }
58
60
 
59
61
  export declare const eof: GrammarNode<never>;
@@ -64,7 +66,10 @@ export declare type FaultToleranceMode = 'skip-parser' | 'skip-input' | 'fuzzy-m
64
66
 
65
67
  export declare interface GrammarNode<T extends string = never> {
66
68
  type: T;
67
- suggestions(): string[];
69
+ suggestions(): {
70
+ text: string;
71
+ node: GrammarNode<T>;
72
+ }[];
68
73
  parse(text: string, context: ParserContext<T>): ParserResult<T>;
69
74
  children: GrammarNode<T>[];
70
75
  meta?: Record<string, unknown>;
@@ -82,7 +87,7 @@ export declare function nodeName<T extends string>(node: {
82
87
  grammar: GrammarNode<T>;
83
88
  }): T | undefined;
84
89
 
85
- export declare type NodeTypes<T> = _NodeTypes<T> | 'error';
90
+ export declare type NodeTypes<T> = _NodeTypes<T>;
86
91
 
87
92
  declare type _NodeTypes<T> = T extends GrammarNode<infer U> ? U : never;
88
93
 
@@ -137,6 +142,11 @@ declare const themes: {
137
142
  readonly dark: string[];
138
143
  };
139
144
 
145
+ export declare interface ValidationError<T extends string> {
146
+ node: CSTNode<T>;
147
+ message: string;
148
+ }
149
+
140
150
  export declare function visit<T extends string, V = string>(parserResult: ParserSuccess<T>, type: T[], extractor?: (node: ParserSuccess<T>) => V): V[];
141
151
 
142
152
  export declare function visitPredicate<T extends string, V = string>(parserResult: ParserSuccess<T>, filter: (v: ParserSuccess<T>) => boolean, extractor?: (node: ParserSuccess<T>) => V): V[];
@@ -830,9 +830,12 @@ function indexOf(o, h, g = 0) {
830
830
  }
831
831
  function pattern(o) {
832
832
  let h = new import_randexp.default(o);
833
- h.randInt = (o) => o;
833
+ h.randInt = (o) => o, h.defaultRange.subtract(-Infinity, Infinity);
834
834
  function g() {
835
- return t$1(t$3(0, 10), t$2(() => h.gen()), n$1());
835
+ return t$1(t$3(0, 10), t$2(() => h.gen()), n$1(), t$2((o) => ({
836
+ node: _,
837
+ text: o
838
+ })));
836
839
  }
837
840
  let _ = {
838
841
  type: "pattern",
@@ -890,7 +893,7 @@ function repeat(o, h = 1, g = 1e3) {
890
893
  x += y.text.length, S.push(y);
891
894
  }
892
895
  let w = C < h ? error({
893
- expected: o.suggestions(),
896
+ expected: o.suggestions().map((o) => o.text),
894
897
  got: v,
895
898
  grammar: _,
896
899
  offset: x,
@@ -938,7 +941,7 @@ function sequence(...o) {
938
941
  let h = [];
939
942
  for (let g of o) {
940
943
  let o = g.suggestions();
941
- if (h.push(...o.filter((o) => o !== "")), !o.includes("")) break;
944
+ h.push(...o.filter((o) => o.text !== ""));
942
945
  }
943
946
  return h;
944
947
  },
@@ -1003,10 +1006,16 @@ function topError(o) {
1003
1006
  message: o.expected.map((o) => JSON.stringify(o)).join(" or ") + ` expected, but got ${JSON.stringify(o.got)} (${pathToString(o.path)})`,
1004
1007
  expected: o.expected,
1005
1008
  start: o.offset,
1006
- end: o.offset + Math.max(1, o.got.length),
1007
- depth: 1
1009
+ end: o.offset + Math.max(1, o.got.length)
1008
1010
  }];
1009
1011
  }
1012
+ function validationErrors(o) {
1013
+ return o.map((o) => ({
1014
+ start: o.node.offset,
1015
+ end: o.node.end,
1016
+ message: o.message
1017
+ }));
1018
+ }
1010
1019
  var DSLParser = class {
1011
1020
  grammar;
1012
1021
  constructor(o) {
@@ -1020,7 +1029,7 @@ var DSLParser = class {
1020
1029
  return h.length < v.length && (h = v), error({
1021
1030
  grammar: _,
1022
1031
  offset: 0,
1023
- expected: _.suggestions(),
1032
+ expected: _.suggestions().map((o) => o.text),
1024
1033
  got: o,
1025
1034
  path: v
1026
1035
  });
@@ -1044,9 +1053,25 @@ var DSLParser = class {
1044
1053
  terminals: _,
1045
1054
  result: h,
1046
1055
  strictResult: isParserSuccess(h) ? h : void 0,
1047
- errors: [...topError(h), ...topError(h.errorLabel)]
1056
+ errors: [
1057
+ ...topError(h),
1058
+ ...topError(h.errorLabel),
1059
+ ...validationErrors(this.validateCST(g))
1060
+ ]
1048
1061
  };
1049
1062
  }
1063
+ validate(o, h) {}
1064
+ validateCST(o) {
1065
+ let h = t$1(o.children ?? [], t((o) => this.validateCST(o))), g = nodeName(o);
1066
+ if (g) {
1067
+ let _ = this.validate(g, o.text);
1068
+ _ && h.push({
1069
+ node: o,
1070
+ message: _
1071
+ });
1072
+ }
1073
+ return h;
1074
+ }
1050
1075
  };
1051
1076
  function visit(o, h, g = (o) => o.text) {
1052
1077
  return visitPredicate(o, (o) => {
@@ -1067,15 +1092,16 @@ function getSuggestions(o, h, g = () => void 0) {
1067
1092
  }));
1068
1093
  }
1069
1094
  return o.grammar.suggestions().map((h) => ({
1070
- node: o,
1071
- suggestion: h
1095
+ node: _.find((o) => o.grammar === h.node) ?? o,
1096
+ suggestion: h.text
1072
1097
  }));
1073
1098
  }));
1074
1099
  return t$1(v, t((o) => {
1075
1100
  let g = o.node.text.substring(0, h - o.node.offset);
1076
1101
  return o.suggestion.startsWith(g) && o.suggestion.trim().length > 0 ? [{
1077
1102
  suggestion: o.suggestion,
1078
- prefix: g
1103
+ prefix: g,
1104
+ node: o.node
1079
1105
  }] : [];
1080
1106
  }), n((o) => o.suggestion));
1081
1107
  }
@@ -1271,84 +1297,114 @@ function SuggestionsMenu({ suggestions: o, onSelect: h, style: g, selectedIndex:
1271
1297
  }, g))
1272
1298
  });
1273
1299
  }
1274
- function DslEditor({ code: o, onChange: h, onParsed: v, grammar: S, wrap: T = !1, tooltipProps: E = { style: { backgroundColor: "darkGray" } }, suggestions: D, className: O = DslEditor.name, syntaxColors: k = defaultSyntaxColors("light"),...A }) {
1275
- let [j, M] = useState([]), [N, F] = useState(), [I, L] = useState({
1300
+ function getCursorCoordinates(o) {
1301
+ return o?.getCursorPosition?.() ?? {
1302
+ top: 0,
1303
+ left: 0
1304
+ };
1305
+ }
1306
+ function DslEditor({ code: o, onChange: h, onParsed: v, grammar: S, wrap: T = !1, tooltipProps: E = { style: { backgroundColor: "darkGray" } }, suggestions: D, validate: O, className: k = DslEditor.name, syntaxColors: A = defaultSyntaxColors("light"),...j }) {
1307
+ let [M, N] = useState([]), [F, I] = useState(), [L, R] = useState({
1276
1308
  top: 0,
1277
1309
  left: 0,
1310
+ cursorPosition: !1,
1278
1311
  visible: !1
1279
- }), [R, z] = useState(0), [B, V] = useState(""), H = useRef(null), U = useRef(null), W = useRef(null), G = useRef(null), K = useCallback((o) => {
1280
- let h = H.current?.selectionStart ?? 0, g = getSuggestions(o, h, D);
1281
- return M(g), g;
1282
- }, [D]), q = useCallback(() => {
1283
- N && V(H.current?.value?.substring(0, H.current?.selectionStart ?? 0) ?? "");
1284
- }, [N]);
1312
+ }), [z, B] = useState(0), [V, H] = useState(""), U = useRef(null), W = useRef(null), G = useRef(null), K = useRef(null), q = useCallback((o) => {
1313
+ let h = U.current?.selectionStart ?? 0, g = getSuggestions(o, h, D);
1314
+ return N(g), g;
1315
+ }, [D]), J = useCallback(() => {
1316
+ F && H(U.current?.value?.substring(0, U.current?.selectionStart ?? 0) ?? "");
1317
+ }, [F]);
1285
1318
  useEffect(() => {
1286
- let h = new DSLParser(S).parse(o);
1287
- F(h), v?.(h), K(h.cst), V(o.substring(0, H.current?.selectionStart ?? 0));
1319
+ let h = (O ? new class extends DSLParser {
1320
+ constructor() {
1321
+ super(S);
1322
+ }
1323
+ validate(o, h) {
1324
+ return O(o, h);
1325
+ }
1326
+ }() : new DSLParser(S)).parse(o);
1327
+ I(h), v?.(h), q(h.cst), H(o.substring(0, U.current?.selectionStart ?? 0));
1288
1328
  }, [
1289
1329
  o,
1290
1330
  v,
1291
1331
  S,
1292
- K
1332
+ q,
1333
+ O
1293
1334
  ]);
1294
- let J = useCallback(() => U.current?.getCursorPosition?.() ?? {
1295
- top: 0,
1296
- left: 0
1297
- }, []), Y = useCallback((g) => {
1298
- if (!H.current) return;
1299
- let { selectionStart: _, selectionEnd: v } = H.current, { prefix: y } = g, b = o.substring(0, _ - y.length) + g.suggestion + o.substring(v);
1300
- h(b), setTimeout(() => {
1301
- H.current && (H.current.focus(), H.current.selectionStart = H.current.selectionEnd = _ - y.length + g.suggestion.length);
1302
- }, 0), L((o) => ({
1335
+ let Y = useCallback((g) => {
1336
+ if (!U.current || !F?.cst) return;
1337
+ let { selectionStart: _, selectionEnd: v } = U.current, { prefix: y } = g;
1338
+ if (_ === v) {
1339
+ let _ = o.substring(0, g.node.offset) + g.suggestion + o.substring(g.node.end);
1340
+ h(_);
1341
+ } else {
1342
+ let b = o.substring(0, _ - y.length) + g.suggestion + o.substring(v);
1343
+ h(b);
1344
+ }
1345
+ setTimeout(() => {
1346
+ U.current && (U.current.focus(), U.current.selectionStart = U.current.selectionEnd = _ - y.length + g.suggestion.length);
1347
+ }, 0), R((o) => ({
1303
1348
  ...o,
1304
1349
  visible: !1
1305
1350
  }));
1306
- }, [o, h]), X = useMemo(() => ({
1351
+ }, [
1352
+ o,
1353
+ h,
1354
+ F?.cst
1355
+ ]), X = useMemo(() => ({
1307
1356
  ArrowDown() {
1308
- z((o) => (o + 1) % j.length);
1357
+ B((o) => (o + 1) % M.length);
1309
1358
  },
1310
1359
  ArrowUp() {
1311
- z((o) => (o - 1 + j.length) % j.length);
1360
+ B((o) => (o - 1 + M.length) % M.length);
1312
1361
  },
1313
1362
  Enter() {
1314
- j[R] && Y(j[R]);
1363
+ M[z] && Y(M[z]);
1315
1364
  },
1316
1365
  Escape() {
1317
- L((o) => ({
1366
+ R((o) => ({
1318
1367
  ...o,
1319
1368
  visible: !1
1320
1369
  }));
1321
1370
  }
1322
1371
  }), [
1323
1372
  Y,
1324
- R,
1325
- j
1373
+ z,
1374
+ M
1326
1375
  ]), Z = useMemo(() => ({ CtrlSpace() {
1327
- if (!N?.cst) return;
1328
- let o = K(N?.cst);
1329
- if (e(o)) return;
1330
- let { top: h, left: g } = J();
1331
- z(0), L({
1332
- visible: !0,
1333
- top: h,
1334
- left: g
1376
+ if (!F?.cst) return;
1377
+ let o = q(F?.cst);
1378
+ e(o) || R({
1379
+ cursorPosition: !0,
1380
+ visible: !1,
1381
+ top: 0,
1382
+ left: 0
1335
1383
  });
1336
- } }), [
1337
- J,
1338
- N?.cst,
1339
- K
1340
- ]), Q = useCallback((o) => {
1341
- let h = (I.visible ? X : Z)[shortcutName(o)];
1384
+ } }), [F?.cst, q]);
1385
+ useEffect(() => {
1386
+ if (L.cursorPosition) {
1387
+ let { top: o, left: h } = getCursorCoordinates(W.current);
1388
+ B(0), R({
1389
+ cursorPosition: !1,
1390
+ visible: !0,
1391
+ top: o,
1392
+ left: h
1393
+ });
1394
+ }
1395
+ }, [L.cursorPosition]);
1396
+ let Q = useCallback((o) => {
1397
+ let h = (L.visible ? X : Z)[shortcutName(o)];
1342
1398
  h && (o.preventDefault(), h());
1343
1399
  }, [
1344
- I.visible,
1400
+ L.visible,
1345
1401
  X,
1346
1402
  Z
1347
1403
  ]), $ = useCallback((o) => {
1348
- h(o.target.value), L((o) => ({
1404
+ h(o.target.value), R((o) => ({
1349
1405
  ...o,
1350
1406
  visible: !1
1351
- })), V(o.target.value.substring(0, o.target.selectionStart));
1407
+ })), H(o.target.value.substring(0, o.target.selectionStart));
1352
1408
  }, [h]);
1353
1409
  return /* @__PURE__ */ jsx("div", {
1354
1410
  style: {
@@ -1358,7 +1414,7 @@ function DslEditor({ code: o, onChange: h, onParsed: v, grammar: S, wrap: T = !1
1358
1414
  width: "100%",
1359
1415
  height: "100%"
1360
1416
  },
1361
- className: O,
1417
+ className: k,
1362
1418
  children: /* @__PURE__ */ jsxs("div", {
1363
1419
  style: {
1364
1420
  position: "relative",
@@ -1367,44 +1423,44 @@ function DslEditor({ code: o, onChange: h, onParsed: v, grammar: S, wrap: T = !1
1367
1423
  },
1368
1424
  children: [
1369
1425
  /* @__PURE__ */ jsx("textarea", {
1370
- ref: H,
1426
+ ref: U,
1371
1427
  spellCheck: !1,
1372
1428
  wrap: T ? "soft" : "off",
1373
1429
  style: textStyle,
1374
1430
  value: o,
1375
- onSelect: q,
1431
+ onSelect: J,
1376
1432
  onChange: $,
1377
- onScroll: useSyncScroll(W, G),
1433
+ onScroll: useSyncScroll(G, K),
1378
1434
  onKeyDown: Q,
1379
- ...A
1435
+ ...j
1380
1436
  }),
1381
- N && /* @__PURE__ */ jsx(SyntaxHighlighter, {
1382
- cstRoot: N.cst,
1383
- ref: W,
1437
+ F && /* @__PURE__ */ jsx(SyntaxHighlighter, {
1438
+ cstRoot: F.cst,
1439
+ ref: G,
1384
1440
  wrap: T,
1385
- syntaxColors: k
1441
+ syntaxColors: A
1386
1442
  }),
1387
- !e(N?.errors ?? []) && /* @__PURE__ */ jsx(ErrorHighlighter, {
1388
- ref: G,
1389
- errors: N?.errors ?? [],
1443
+ !e(F?.errors ?? []) && /* @__PURE__ */ jsx(ErrorHighlighter, {
1444
+ ref: K,
1445
+ errors: F?.errors ?? [],
1390
1446
  tooltipProps: E,
1391
1447
  wrap: T,
1392
1448
  children: o
1393
1449
  }),
1394
- /* @__PURE__ */ jsx(CursorPosition, {
1395
- ref: U,
1396
- text: B,
1450
+ L.cursorPosition && /* @__PURE__ */ jsx(CursorPosition, {
1451
+ ref: W,
1452
+ text: V,
1397
1453
  wrap: T
1398
1454
  }),
1399
- I.visible && j.length > 0 && /* @__PURE__ */ jsx(SuggestionsMenu, {
1400
- suggestions: j,
1455
+ L.visible && /* @__PURE__ */ jsx(SuggestionsMenu, {
1456
+ suggestions: M,
1401
1457
  onSelect: Y,
1402
1458
  style: {
1403
- top: I.top,
1404
- left: I.left
1459
+ top: L.top,
1460
+ left: L.left
1405
1461
  },
1406
- selectedIndex: R,
1407
- onHover: z
1462
+ selectedIndex: z,
1463
+ onHover: B
1408
1464
  })
1409
1465
  ]
1410
1466
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-dsl-editor",
3
3
  "license": "MIT",
4
- "version": "0.4.3",
4
+ "version": "0.4.5",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/rzymek/react-dsl-editor.git"
7
7
  },