@wildwinter/expr-editor 0.12.3 → 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":
@@ -463,6 +473,16 @@ function renderNode(node, path, ctx, parentOp, side) {
463
473
  for (let i = 1; i < node.args.length; i++) row2.append(renderNode(node.args[i], [...path, "args", i], ctx));
464
474
  return row2;
465
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
+ }
466
486
  const row = el("span", "exed-call");
467
487
  row.append(pill("func", node.name, (b) => callEditor(ctx, path, node, b), { issue }));
468
488
  row.append(el("span", "exed-paren", ["("]));
@@ -1146,6 +1166,7 @@ function mountExpressionEditor(host, opts) {
1146
1166
  },
1147
1167
  ...opts.requireNonEmpty ? { requireNonEmpty: true } : {},
1148
1168
  ...opts.valueEnumValues ? { valueEnumValues: opts.valueEnumValues } : {},
1169
+ ...opts.selfAdvanceRef ? { selfAdvanceRef: opts.selfAdvanceRef } : {},
1149
1170
  ...opts.propertyActions ? { propertyActions: opts.propertyActions } : {},
1150
1171
  ...opts.pickNode ? { pickNode: opts.pickNode } : {},
1151
1172
  ...opts.nodeLabel ? { nodeLabel: opts.nodeLabel } : {}
@@ -1409,6 +1430,14 @@ function valueWizard(opts) {
1409
1430
  }
1410
1431
 
1411
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
+ }
1412
1441
  var clone = (list) => list.map((e) => e.kind === "set" ? { ...e } : { ...e, args: [...e.args] });
1413
1442
  var addSet = (list, target, value) => [...clone(list), { kind: "set", target, value }];
1414
1443
  var addEmit = (list, event) => [...clone(list), { kind: "emit", event, args: [] }];
@@ -1505,7 +1534,7 @@ function mountEffectsEditor(host, opts) {
1505
1534
  }), { container: opts.popoverContainer });
1506
1535
  };
1507
1536
  const targetType = (target) => opts.catalogue.find((e) => refOf(e, defaultScope) === target)?.type;
1508
- const valueDisplay = (value, onChange, type) => {
1537
+ const valueDisplay = (value, onChange, type, selfAdvanceRef) => {
1509
1538
  const sub = el("div", "exed-effect-value");
1510
1539
  const addTerm = type === "number" ? "arithmetic" : type === "boolean" ? "boolean" : void 0;
1511
1540
  inner.push(mountExpressionEditor(sub, {
@@ -1517,6 +1546,7 @@ function mountEffectsEditor(host, opts) {
1517
1546
  functions: opts.functions,
1518
1547
  mode: "flat",
1519
1548
  ...addTerm ? { addTerm } : {},
1549
+ ...selfAdvanceRef ? { selfAdvanceRef } : {},
1520
1550
  ...opts.popoverContainer ? { popoverContainer: opts.popoverContainer } : {},
1521
1551
  text: textMode,
1522
1552
  onChange
@@ -1567,11 +1597,18 @@ function mountEffectsEditor(host, opts) {
1567
1597
  commit(updateAt(effects, i, { target: refOf(entry, defaultScope), value: initialValueFor(entry, defaultScope).src }));
1568
1598
  });
1569
1599
  }, "change the target property");
1570
- 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", ["="]));
1571
1603
  row.append(valueDisplay(eff.value, (src) => {
1572
- 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;
1573
1610
  opts.onChange(effects);
1574
- }, targetType(eff.target)));
1611
+ }, targetType(eff.target), advances ? eff.target : void 0));
1575
1612
  row.append(rowActions2(i));
1576
1613
  return row;
1577
1614
  }
@@ -1685,7 +1722,7 @@ function mountEffectsEditor(host, opts) {
1685
1722
  }
1686
1723
 
1687
1724
  // src/preview.ts
1688
- function frozenCtx(src, o) {
1725
+ function frozenCtx(src, o, selfAdvanceRef) {
1689
1726
  const v = validateSource(src, o.schema, o.dialect);
1690
1727
  const ctx = {
1691
1728
  schema: o.schema,
@@ -1704,6 +1741,7 @@ function frozenCtx(src, o) {
1704
1741
  pickNode: () => {
1705
1742
  },
1706
1743
  // enables labelled node pills; never fires (preview is read-only)
1744
+ ...selfAdvanceRef ? { selfAdvanceRef } : {},
1707
1745
  ...o.nodeLabel ? { nodeLabel: o.nodeLabel } : {},
1708
1746
  ...o.propertyActions ? {
1709
1747
  propertyActions: o.propertyActions,
@@ -1714,8 +1752,8 @@ function frozenCtx(src, o) {
1714
1752
  };
1715
1753
  return { ctx, ast: v.ast };
1716
1754
  }
1717
- function exprPills(src, o) {
1718
- const { ctx, ast } = frozenCtx(src, o);
1755
+ function exprPills(src, o, selfAdvanceRef) {
1756
+ const { ctx, ast } = frozenCtx(src, o, selfAdvanceRef);
1719
1757
  return ast ? renderNode(ast, [], ctx) : el("span", "exed-preview-raw", [src]);
1720
1758
  }
1721
1759
  function renderConditionPreview(src, o) {
@@ -1727,9 +1765,10 @@ function renderEffectsPreview(effects, o) {
1727
1765
  for (const eff of effects) {
1728
1766
  const row = el("div", "exed-preview-eff");
1729
1767
  if (eff.kind === "set") {
1768
+ const advances = isSelfAdvance(eff, o.dialect.defaultScope, o.dialect);
1730
1769
  row.append(el("span", "exed-pill exed-pill-prop", [eff.target || "(property)"]));
1731
- row.append(el("span", "exed-effect-eq", ["="]));
1732
- 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));
1733
1772
  } else {
1734
1773
  row.append(el("span", "exed-effect-kw", ["emit"]));
1735
1774
  row.append(el("span", "exed-pill exed-pill-event", [eff.event ? `"${eff.event}"` : "(name)"]));
@@ -1756,6 +1795,7 @@ exports.addArg = addArg;
1756
1795
  exports.addChildToContainer = addChildToContainer;
1757
1796
  exports.addEmit = addEmit;
1758
1797
  exports.addSet = addSet;
1798
+ exports.advancedRef = advancedRef;
1759
1799
  exports.astToTree = astToTree;
1760
1800
  exports.binary = binary;
1761
1801
  exports.boolLit = boolLit;
@@ -1786,6 +1826,7 @@ exports.mountExpressionEditor = mountExpressionEditor;
1786
1826
  exports.moveAt = moveAt;
1787
1827
  exports.moveChildInContainer = moveChildInContainer;
1788
1828
  exports.needsParens = needsParens;
1829
+ exports.normaliseRef = normaliseRef;
1789
1830
  exports.notNode = notNode;
1790
1831
  exports.numLit = numLit;
1791
1832
  exports.opSwapGroup = opSwapGroup;