@wildwinter/expr-editor 0.12.2 → 0.13.0

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.cjs CHANGED
@@ -93,6 +93,16 @@ function findChoicePeer(ast, path) {
93
93
  return other.kind === "scopedvar" ? { scope: other.scope, name: other.name } : null;
94
94
  }
95
95
  var findEnumPeer = findChoicePeer;
96
+ var normaliseRef = (ref, defaultScope) => {
97
+ const low = ref.toLowerCase();
98
+ const prefix = `@${defaultScope.toLowerCase()}.`;
99
+ return low.startsWith(prefix) ? `@${low.slice(prefix.length)}` : low;
100
+ };
101
+ function advancedRef(node, defaultScope) {
102
+ if (node.kind !== "call" || node.name !== "advance" || node.args.length !== 1) return null;
103
+ const arg = node.args[0];
104
+ return arg.kind === "scopedvar" ? normaliseRef(`@${arg.scope}.${arg.name}`, defaultScope) : null;
105
+ }
96
106
  function firstEmptyLeafPath(node, base = []) {
97
107
  switch (node.kind) {
98
108
  case "string":
@@ -221,6 +231,22 @@ var BINARY_LABEL = {
221
231
  "/": "\xF7"
222
232
  };
223
233
  var UNARY_LABEL = { not: "NOT", neg: "\u2212" };
234
+ var OP_WORD = {
235
+ "==": "equals",
236
+ "!=": "not equal to",
237
+ ">": "greater than",
238
+ ">=": "at least",
239
+ "<": "less than",
240
+ "<=": "at most",
241
+ and: "and",
242
+ or: "or",
243
+ "+": "plus",
244
+ "-": "minus",
245
+ "*": "times",
246
+ "/": "divided by",
247
+ not: "not",
248
+ neg: "negative"
249
+ };
224
250
  var COMPARISON_OPS = ["==", "!=", ">", ">=", "<", "<="];
225
251
  var ARITHMETIC_OPS = ["+", "-", "*", "/"];
226
252
  var COMPARABLE_TYPES = ["boolean", "number", "string", "enum", "quality"];
@@ -406,20 +432,6 @@ function pill(kind, label, onClick, opts = {}) {
406
432
  if (opts.path) b.dataset["exedPath"] = opts.path.join("/");
407
433
  return b;
408
434
  }
409
- var OP_ARIA = {
410
- "==": "equals",
411
- "!=": "not equal to",
412
- ">": "greater than",
413
- ">=": "at least",
414
- "<": "less than",
415
- "<=": "at most",
416
- "and": "and",
417
- "or": "or",
418
- "+": "plus",
419
- "-": "minus",
420
- "*": "times",
421
- "/": "divided by"
422
- };
423
435
  var issueText = (ctx, path) => {
424
436
  const list = issuesAt(ctx.byPath, path);
425
437
  return list.length ? list.map((i) => i.message).join("; ") : void 0;
@@ -461,6 +473,16 @@ function renderNode(node, path, ctx, parentOp, side) {
461
473
  for (let i = 1; i < node.args.length; i++) row2.append(renderNode(node.args[i], [...path, "args", i], ctx));
462
474
  return row2;
463
475
  }
476
+ if (ctx.selfAdvanceRef && advancedRef(node, ctx.defaultScope) === normaliseRef(ctx.selfAdvanceRef, ctx.defaultScope)) {
477
+ const row2 = el("span", "exed-call exed-call-advance");
478
+ row2.append(pill(
479
+ "advance",
480
+ "advances",
481
+ (b) => callEditor(ctx, path, node, b),
482
+ { issue, title: "advance one stage", aria: "advances one stage" }
483
+ ));
484
+ return row2;
485
+ }
464
486
  const row = el("span", "exed-call");
465
487
  row.append(pill("func", node.name, (b) => callEditor(ctx, path, node, b), { issue }));
466
488
  row.append(el("span", "exed-paren", ["("]));
@@ -473,7 +495,7 @@ function renderNode(node, path, ctx, parentOp, side) {
473
495
  }
474
496
  case "unary": {
475
497
  const row = el("span", "exed-unary");
476
- row.append(pill("op", UNARY_LABEL[node.op], () => ctx.apply(deleteOrUnwrapNot(ctx, path, node)), { title: "remove", aria: `${OP_ARIA[node.op] ?? node.op} (click to remove)` }));
498
+ row.append(pill("op", UNARY_LABEL[node.op], () => ctx.apply(deleteOrUnwrapNot(ctx, path, node)), { title: "remove", aria: `${OP_WORD[node.op]} (click to remove)` }));
477
499
  row.append(renderNode(node.operand, [...path, "operand"], ctx));
478
500
  return row;
479
501
  }
@@ -485,7 +507,7 @@ function renderNode(node, path, ctx, parentOp, side) {
485
507
  const swap = opSwapGroup(node.op);
486
508
  const opPill = pill("op", BINARY_LABEL[node.op], (b) => {
487
509
  if (swap) operatorEditor(ctx, path, node.op, swap, b);
488
- }, { title: swap ? "swap operator" : void 0, aria: OP_ARIA[node.op] ?? node.op });
510
+ }, { title: swap ? "swap operator" : void 0, aria: OP_WORD[node.op] });
489
511
  if (!swap) opPill.classList.add("exed-op-structural");
490
512
  row.append(opPill);
491
513
  row.append(renderNode(node.right, [...path, "right"], ctx, node.op, "right"));
@@ -596,12 +618,14 @@ function operatorEditor(ctx, path, current, group, anchor) {
596
618
  const wrap = el("div", "exed-menu");
597
619
  wrap.append(el("div", "exed-menu-head", ["Operator"]));
598
620
  for (const op of group) {
599
- wrap.append(button(`exed-opt${op === current ? " sel" : ""}`, `${BINARY_LABEL[op]} ${op}`, () => {
621
+ const row = button(`exed-opt exed-opt-op${op === current ? " sel" : ""}`, "", () => {
600
622
  const node = ctx.getAst();
601
623
  const cur = setNodeAt(node, path, { ...getBinary(ctx, path), op });
602
624
  ctx.apply(cur);
603
625
  close();
604
- }));
626
+ });
627
+ row.append(el("span", "exed-opt-name", [BINARY_LABEL[op]]), el("span", "exed-opt-purpose", [OP_WORD[op]]));
628
+ wrap.append(row);
605
629
  }
606
630
  return wrap;
607
631
  });
@@ -717,17 +741,9 @@ function propertyPicker(ctx, opts) {
717
741
  }
718
742
 
719
743
  // src/clausewizard.ts
720
- var OP_WORD = {
721
- "==": "equals",
722
- "!=": "not equal to",
723
- ">": "greater than",
724
- ">=": "at least",
725
- "<": "less than",
726
- "<=": "at most"
727
- };
728
744
  var opButton = (o, onClick) => {
729
- const b = button("exed-opt", "", onClick);
730
- b.append(el("span", "exed-opt-name", [BINARY_LABEL[o]]), el("span", "exed-opt-purpose", [OP_WORD[o] ?? ""]));
745
+ const b = button("exed-opt exed-opt-op", "", onClick);
746
+ b.append(el("span", "exed-opt-name", [BINARY_LABEL[o]]), el("span", "exed-opt-purpose", [OP_WORD[o]]));
731
747
  return b;
732
748
  };
733
749
  function header(host, title, back, cancel) {
@@ -1150,6 +1166,7 @@ function mountExpressionEditor(host, opts) {
1150
1166
  },
1151
1167
  ...opts.requireNonEmpty ? { requireNonEmpty: true } : {},
1152
1168
  ...opts.valueEnumValues ? { valueEnumValues: opts.valueEnumValues } : {},
1169
+ ...opts.selfAdvanceRef ? { selfAdvanceRef: opts.selfAdvanceRef } : {},
1153
1170
  ...opts.propertyActions ? { propertyActions: opts.propertyActions } : {},
1154
1171
  ...opts.pickNode ? { pickNode: opts.pickNode } : {},
1155
1172
  ...opts.nodeLabel ? { nodeLabel: opts.nodeLabel } : {}
@@ -1413,6 +1430,14 @@ function valueWizard(opts) {
1413
1430
  }
1414
1431
 
1415
1432
  // src/effects.ts
1433
+ function isSelfAdvance(eff, defaultScope, dialect) {
1434
+ if (eff.kind !== "set" || !eff.target) return false;
1435
+ try {
1436
+ return advancedRef(expr.parse(eff.value, dialect), defaultScope) === normaliseRef(eff.target, defaultScope);
1437
+ } catch {
1438
+ return false;
1439
+ }
1440
+ }
1416
1441
  var clone = (list) => list.map((e) => e.kind === "set" ? { ...e } : { ...e, args: [...e.args] });
1417
1442
  var addSet = (list, target, value) => [...clone(list), { kind: "set", target, value }];
1418
1443
  var addEmit = (list, event) => [...clone(list), { kind: "emit", event, args: [] }];
@@ -1509,7 +1534,7 @@ function mountEffectsEditor(host, opts) {
1509
1534
  }), { container: opts.popoverContainer });
1510
1535
  };
1511
1536
  const targetType = (target) => opts.catalogue.find((e) => refOf(e, defaultScope) === target)?.type;
1512
- const valueDisplay = (value, onChange, type) => {
1537
+ const valueDisplay = (value, onChange, type, selfAdvanceRef) => {
1513
1538
  const sub = el("div", "exed-effect-value");
1514
1539
  const addTerm = type === "number" ? "arithmetic" : type === "boolean" ? "boolean" : void 0;
1515
1540
  inner.push(mountExpressionEditor(sub, {
@@ -1521,6 +1546,7 @@ function mountEffectsEditor(host, opts) {
1521
1546
  functions: opts.functions,
1522
1547
  mode: "flat",
1523
1548
  ...addTerm ? { addTerm } : {},
1549
+ ...selfAdvanceRef ? { selfAdvanceRef } : {},
1524
1550
  ...opts.popoverContainer ? { popoverContainer: opts.popoverContainer } : {},
1525
1551
  text: textMode,
1526
1552
  onChange
@@ -1571,11 +1597,18 @@ function mountEffectsEditor(host, opts) {
1571
1597
  commit(updateAt(effects, i, { target: refOf(entry, defaultScope), value: initialValueFor(entry, defaultScope).src }));
1572
1598
  });
1573
1599
  }, "change the target property");
1574
- row.append(targetBtn, el("span", "exed-effect-eq", ["="]));
1600
+ const advances = isSelfAdvance(eff, defaultScope, opts.dialect);
1601
+ row.append(targetBtn);
1602
+ if (!advances) row.append(el("span", "exed-effect-eq", ["="]));
1575
1603
  row.append(valueDisplay(eff.value, (src) => {
1576
- effects = updateAt(effects, i, { value: src });
1604
+ const next = updateAt(effects, i, { value: src });
1605
+ if (isSelfAdvance(next[i], defaultScope, opts.dialect) !== advances) {
1606
+ commit(next);
1607
+ return;
1608
+ }
1609
+ effects = next;
1577
1610
  opts.onChange(effects);
1578
- }, targetType(eff.target)));
1611
+ }, targetType(eff.target), advances ? eff.target : void 0));
1579
1612
  row.append(rowActions2(i));
1580
1613
  return row;
1581
1614
  }
@@ -1689,7 +1722,7 @@ function mountEffectsEditor(host, opts) {
1689
1722
  }
1690
1723
 
1691
1724
  // src/preview.ts
1692
- function frozenCtx(src, o) {
1725
+ function frozenCtx(src, o, selfAdvanceRef) {
1693
1726
  const v = validateSource(src, o.schema, o.dialect);
1694
1727
  const ctx = {
1695
1728
  schema: o.schema,
@@ -1708,6 +1741,7 @@ function frozenCtx(src, o) {
1708
1741
  pickNode: () => {
1709
1742
  },
1710
1743
  // enables labelled node pills; never fires (preview is read-only)
1744
+ ...selfAdvanceRef ? { selfAdvanceRef } : {},
1711
1745
  ...o.nodeLabel ? { nodeLabel: o.nodeLabel } : {},
1712
1746
  ...o.propertyActions ? {
1713
1747
  propertyActions: o.propertyActions,
@@ -1718,8 +1752,8 @@ function frozenCtx(src, o) {
1718
1752
  };
1719
1753
  return { ctx, ast: v.ast };
1720
1754
  }
1721
- function exprPills(src, o) {
1722
- const { ctx, ast } = frozenCtx(src, o);
1755
+ function exprPills(src, o, selfAdvanceRef) {
1756
+ const { ctx, ast } = frozenCtx(src, o, selfAdvanceRef);
1723
1757
  return ast ? renderNode(ast, [], ctx) : el("span", "exed-preview-raw", [src]);
1724
1758
  }
1725
1759
  function renderConditionPreview(src, o) {
@@ -1731,9 +1765,10 @@ function renderEffectsPreview(effects, o) {
1731
1765
  for (const eff of effects) {
1732
1766
  const row = el("div", "exed-preview-eff");
1733
1767
  if (eff.kind === "set") {
1768
+ const advances = isSelfAdvance(eff, o.dialect.defaultScope, o.dialect);
1734
1769
  row.append(el("span", "exed-pill exed-pill-prop", [eff.target || "(property)"]));
1735
- row.append(el("span", "exed-effect-eq", ["="]));
1736
- row.append(exprPills(eff.value, o));
1770
+ if (!advances) row.append(el("span", "exed-effect-eq", ["="]));
1771
+ row.append(exprPills(eff.value, o, advances ? eff.target : void 0));
1737
1772
  } else {
1738
1773
  row.append(el("span", "exed-effect-kw", ["emit"]));
1739
1774
  row.append(el("span", "exed-pill exed-pill-event", [eff.event ? `"${eff.event}"` : "(name)"]));
@@ -1754,11 +1789,13 @@ exports.BINARY_LABEL = BINARY_LABEL;
1754
1789
  exports.COMPARABLE_TYPES = COMPARABLE_TYPES;
1755
1790
  exports.COMPARISON_OPS = COMPARISON_OPS;
1756
1791
  exports.EQUALITY_OPS = EQUALITY_OPS;
1792
+ exports.OP_WORD = OP_WORD;
1757
1793
  exports.UNARY_LABEL = UNARY_LABEL;
1758
1794
  exports.addArg = addArg;
1759
1795
  exports.addChildToContainer = addChildToContainer;
1760
1796
  exports.addEmit = addEmit;
1761
1797
  exports.addSet = addSet;
1798
+ exports.advancedRef = advancedRef;
1762
1799
  exports.astToTree = astToTree;
1763
1800
  exports.binary = binary;
1764
1801
  exports.boolLit = boolLit;
@@ -1789,6 +1826,7 @@ exports.mountExpressionEditor = mountExpressionEditor;
1789
1826
  exports.moveAt = moveAt;
1790
1827
  exports.moveChildInContainer = moveChildInContainer;
1791
1828
  exports.needsParens = needsParens;
1829
+ exports.normaliseRef = normaliseRef;
1792
1830
  exports.notNode = notNode;
1793
1831
  exports.numLit = numLit;
1794
1832
  exports.opSwapGroup = opSwapGroup;