@rohal12/spindle-lsp 0.13.2 → 0.13.3

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/bin.js CHANGED
@@ -6174,7 +6174,7 @@ function isRawMacro(name) {
6174
6174
  return RAW_BODY_MACROS.has(name);
6175
6175
  }
6176
6176
  function isBlockMacro(name) {
6177
- return BLOCK_MACROS.has(name);
6177
+ return BLOCK_MACROS.has(name) || BLOCK_WIDGETS.has(name);
6178
6178
  }
6179
6179
  function buildAst(nodes) {
6180
6180
  return nodes.map(astNode);
@@ -6445,7 +6445,7 @@ function getLineInfo(input, offset2) {
6445
6445
  }
6446
6446
  function getOptions(opts) {
6447
6447
  var options = {};
6448
- for (var opt in defaultOptions) options[opt] = opts && hasOwn(opts, opt) ? opts[opt] : defaultOptions[opt];
6448
+ for (var opt in defaultOptions) options[opt] = opts && hasOwn$1(opts, opt) ? opts[opt] : defaultOptions[opt];
6449
6449
  if (options.ecmaVersion === "latest") options.ecmaVersion = 1e8;
6450
6450
  else if (options.ecmaVersion == null) {
6451
6451
  if (!warnedAboutEcmaVersion && typeof console === "object" && console.warn) {
@@ -6617,7 +6617,7 @@ function transientAssignment(p, end) {
6617
6617
  if (t === types$1.eof) return false;
6618
6618
  if (depth > 0) {
6619
6619
  if (OPENERS.has(t)) depth++;
6620
- else if (CLOSERS.has(t)) depth--;
6620
+ else if (CLOSERS$1.has(t)) depth--;
6621
6621
  } else if (t === types$1.bracketL) depth = 1;
6622
6622
  else if (t === types$1.dot) {
6623
6623
  q2.nextToken();
@@ -6628,6 +6628,9 @@ function transientAssignment(p, end) {
6628
6628
  return false;
6629
6629
  }
6630
6630
  }
6631
+ function isMemberDot(type) {
6632
+ return type === types$1.dot || type === types$1.questionDot;
6633
+ }
6631
6634
  function tokenizerAt(src, start, goal, { onComment, afterOperand = false } = {}) {
6632
6635
  const p = new SigilParser(onComment ? {
6633
6636
  ...OPTIONS,
@@ -6699,6 +6702,9 @@ function bracketsBefore(src, pos) {
6699
6702
  };
6700
6703
  }
6701
6704
  function parseCode(src, goal = "expression") {
6705
+ return parseWithAst(src, goal).out;
6706
+ }
6707
+ function parseWithAst(src, goal) {
6702
6708
  const percentAt = /* @__PURE__ */ new Map();
6703
6709
  for (; ; ) {
6704
6710
  const transientsRead = /* @__PURE__ */ new Set();
@@ -6710,7 +6716,10 @@ function parseCode(src, goal = "expression") {
6710
6716
  };
6711
6717
  walk(ast, src, out, false, false);
6712
6718
  out.refs.sort((a, b) => a.start - b.start);
6713
- return out;
6719
+ return {
6720
+ out,
6721
+ ast
6722
+ };
6714
6723
  } catch (error48) {
6715
6724
  const pos = error48.pos ?? -1;
6716
6725
  if (!(error48 instanceof SyntaxError) || percentAt.has(pos)) throw error48;
@@ -7035,7 +7044,7 @@ function walkTokens(src, from, handlers, nesting, goal, oneTemplate) {
7035
7044
  if (t === types$1.name) {
7036
7045
  const sigil = sigilAt(src, tok.start, tok.value);
7037
7046
  const property = prev === types$1.dot || prev === types$1.questionDot || (sigil === "$" || sigil === "_") && keyPosition(src, tok.end, prev);
7038
- if (sigil && !property) handlers.variable?.(sigil, src.slice(tok.start + 1, tok.end), tok.start, nesting);
7047
+ if (sigil && !property) handlers.variable?.(sigil, tok.value.slice(1), tok.start, nesting, tok.end);
7039
7048
  else code(tok.start, tok.end);
7040
7049
  } else if (t === types$1.string || t === types$1.regexp || t === types$1.template || t === types$1.invalidTemplate) literal3(tok.start, tok.end);
7041
7050
  else if (t === types$1.backQuote) {
@@ -7416,15 +7425,15 @@ function topLevelIndices(src, isSeparator) {
7416
7425
  } });
7417
7426
  return indices;
7418
7427
  }
7419
- function splitTopLevel(src, isSeparator) {
7420
- const segments = [];
7428
+ function topLevelSpans(src, isSeparator) {
7429
+ const spans = [];
7421
7430
  let from = 0;
7422
7431
  for (const i2 of topLevelIndices(src, isSeparator)) {
7423
- segments.push(src.slice(from, i2));
7432
+ spans.push([from, i2]);
7424
7433
  from = i2 + 1;
7425
7434
  }
7426
- segments.push(src.slice(from));
7427
- return segments;
7435
+ spans.push([from, src.length]);
7436
+ return spans;
7428
7437
  }
7429
7438
  function isStandaloneValue(token) {
7430
7439
  return startsLikeValue(token) && !endsWithOperator(token);
@@ -7441,21 +7450,31 @@ function startsLikeValue(token) {
7441
7450
  return false;
7442
7451
  }
7443
7452
  function trySplitOnWhitespace(raw) {
7444
- const args2 = splitTopLevel(raw, isWhitespace).filter(Boolean);
7453
+ const args2 = topLevelSpans(raw, isWhitespace).filter(([a, b]) => b > a);
7445
7454
  if (args2.length < 2) return null;
7446
- for (const arg of args2) if (!isStandaloneValue(arg)) return null;
7455
+ for (const [a, b] of args2) if (!isStandaloneValue(raw.slice(a, b))) return null;
7447
7456
  return args2;
7448
7457
  }
7449
- function splitArgs(raw) {
7450
- const args2 = splitTopLevel(raw, (ch) => ch === ",").map((a) => a.trim());
7458
+ function trimmed$1(src, [start, end]) {
7459
+ const text2 = src.slice(start, end);
7460
+ const lead = text2.length - text2.trimStart().length;
7461
+ return [start + lead, Math.max(start + lead, end - (text2.length - text2.trimEnd().length))];
7462
+ }
7463
+ function splitArgSpans(raw) {
7464
+ const args2 = topLevelSpans(raw, (ch) => ch === ",").map((span) => trimmed$1(raw, span));
7451
7465
  const hasComma = args2.length > 1;
7452
- if (args2[args2.length - 1] === "") args2.pop();
7466
+ const last2 = args2[args2.length - 1];
7467
+ if (last2[0] === last2[1]) args2.pop();
7453
7468
  if (!hasComma && args2.length === 1) {
7454
- const split = trySplitOnWhitespace(args2[0]);
7455
- if (split) return split;
7469
+ const [start, end] = args2[0];
7470
+ const split = trySplitOnWhitespace(raw.slice(start, end));
7471
+ if (split) return split.map(([a, b]) => [start + a, start + b]);
7456
7472
  }
7457
7473
  return args2;
7458
7474
  }
7475
+ function splitArgs(raw) {
7476
+ return splitArgSpans(raw).map(([a, b]) => raw.slice(a, b));
7477
+ }
7459
7478
  function terms(src) {
7460
7479
  const spans = [];
7461
7480
  let start = 0;
@@ -7529,8 +7548,9 @@ function readOptions(params, src, { at: at3, spans }) {
7529
7548
  }
7530
7549
  return options;
7531
7550
  }
7532
- function takeFlag(src, word) {
7551
+ function takeFlag(src, word, alone) {
7533
7552
  const spans = terms(src);
7553
+ if (spans.length === 1) return alone && slice(src, spans[0]) === word ? [src.length, src.length] : null;
7534
7554
  if (spans.length < 2) return null;
7535
7555
  if (slice(src, spans[spans.length - 1]) === word) {
7536
7556
  const rest = [0, spans[spans.length - 2][1]];
@@ -7565,8 +7585,9 @@ function trimmed(src, at3) {
7565
7585
  }
7566
7586
  function readGroup(params, src, args2, { at: srcAt, spans }) {
7567
7587
  let [text2, at3] = trimmed(src ?? "", srcAt);
7588
+ const alone = !params.some((p) => p.type !== "flag" && p.required);
7568
7589
  for (const flag of params.filter((p) => p.type === "flag")) {
7569
- const rest2 = src === null ? null : takeFlag(text2, flag.name);
7590
+ const rest2 = src === null ? null : takeFlag(text2, flag.name, alone);
7570
7591
  args2[flag.name] = rest2 !== null;
7571
7592
  if (rest2) [text2, at3] = trimmed(slice(text2, rest2), at3 + rest2[0]);
7572
7593
  }
@@ -7637,9 +7658,40 @@ function passageTarget(arg) {
7637
7658
  name
7638
7659
  };
7639
7660
  }
7661
+ function checkParameterTypes(macro, parameters) {
7662
+ const holdsFix = `Give a \`string\` or \`text\` parameter one of ${STRING_HOLDS.join(", ")} to hold (see docs/custom-macros.md#what-a-string-holds).`;
7663
+ for (const param of parameters) {
7664
+ const type = param.type;
7665
+ if (!PARAMETER_TYPES.includes(type)) throw parameterError(macro, param, type === void 0 ? "has no type" : `has the unknown type ${JSON.stringify(type)}`, `Give it one of the types ${PARAMETER_TYPES.join(", ")} (see docs/custom-macros.md#parameter-types), or declare no parameters and read props.rawArgs.`);
7666
+ const holds = param.holds;
7667
+ if (holds !== void 0 && type !== "string" && type !== "text") throw parameterError(macro, param, `is of the type ${type}, which holds what it is`, holdsFix);
7668
+ if (holds !== void 0 && !STRING_HOLDS.includes(holds)) throw parameterError(macro, param, `holds the unknown ${JSON.stringify(holds)}`, holdsFix);
7669
+ if (param.parameters) checkParameterTypes(macro, param.parameters);
7670
+ }
7671
+ }
7672
+ function parameterError(macro, param, problem, fix) {
7673
+ return /* @__PURE__ */ new Error(`spindle: The parameter "${param.name}" of the macro {${macro}} ${problem}. ${fix}`);
7674
+ }
7675
+ function getMacroRegistry() {
7676
+ return Array.from(metadataRegistry.values());
7677
+ }
7640
7678
  function subMacroParameters(name) {
7641
7679
  return SUB_MACRO_PARAMETERS.get(name.toLowerCase());
7642
7680
  }
7681
+ function variableNameError(name, label2 = name) {
7682
+ return name === "__proto__" ? `"${label2}" cannot be used as a variable name (${RESERVED_NAME} is reserved)` : void 0;
7683
+ }
7684
+ function ownEntries(sources, keep = () => true) {
7685
+ const record2 = /* @__PURE__ */ Object.create(null);
7686
+ for (const source of sources) {
7687
+ if (!source) continue;
7688
+ for (const key of Object.keys(source)) {
7689
+ const value = source[key];
7690
+ if (keep(value)) record2[key] = value;
7691
+ }
7692
+ }
7693
+ return record2;
7694
+ }
7643
7695
  function withParseCache(fn) {
7644
7696
  const outer = parses;
7645
7697
  parses = /* @__PURE__ */ new Map();
@@ -7668,6 +7720,13 @@ function pieceOffset(piece, index) {
7668
7720
  function extentOf(piece) {
7669
7721
  return piece.kind === "code" ? piece.code.length : piece.kind === "text" ? piece.text.length : piece.length;
7670
7722
  }
7723
+ function widgetParameters(params, holds = {}) {
7724
+ if (!params.some((param) => hasOwn(holds, param))) return void 0;
7725
+ return params.map((name) => ({
7726
+ name,
7727
+ ...hasOwn(holds, name) ? { holds: holds[name] } : {}
7728
+ }));
7729
+ }
7671
7730
  function withHolds(params, holds) {
7672
7731
  return params.map((param) => {
7673
7732
  if (param.parameters) return {
@@ -7682,8 +7741,13 @@ function withHolds(params, holds) {
7682
7741
  }
7683
7742
  function parameterLookup(macros) {
7684
7743
  const parameters = new NameMap();
7685
- for (const { name, parameters: params, interpolate } of macros) if (params) parameters.set(name, withHolds(params, holdsByDefault(interpolate)));
7686
- return (name) => {
7744
+ const widgets = new NameMap();
7745
+ for (const { name, parameters: params, interpolate, widget } of macros) {
7746
+ if (params) parameters.set(name, withHolds(params, holdsByDefault(interpolate)));
7747
+ const declared = widget && widgetParameters(widget.params, widget.holds);
7748
+ if (declared) widgets.set(name, declared);
7749
+ }
7750
+ const lookup = (name) => {
7687
7751
  let params = parameters.get(name);
7688
7752
  if (!params) {
7689
7753
  const sub = subMacroParameters(name);
@@ -7691,6 +7755,8 @@ function parameterLookup(macros) {
7691
7755
  }
7692
7756
  return params;
7693
7757
  };
7758
+ if (widgets.size > 0) lookup.widget = (name) => widgets.get(name);
7759
+ return lookup;
7694
7760
  }
7695
7761
  function codeGoal(param) {
7696
7762
  if (param.type === "expression" || param.type === "passage") return "expression";
@@ -7781,6 +7847,8 @@ function* codeAndText(src, tokens2, parametersOf2) {
7781
7847
  }
7782
7848
  const params = parametersOf2(name);
7783
7849
  if (params) yield* argPieces(args2, argsAt, params, token.name, label2);
7850
+ const widget = parametersOf2.widget?.(name);
7851
+ if (widget) yield* widgetPieces(args2, argsAt, widget, token.name, label2);
7784
7852
  }
7785
7853
  }
7786
7854
  }
@@ -7836,23 +7904,42 @@ function nestedPiece(piece, toRoot, where2) {
7836
7904
  ...nested
7837
7905
  };
7838
7906
  }
7839
- function escapeOffsets(raw, at3, value) {
7907
+ function escapeAt(raw, i2, escapes) {
7908
+ if (escapes === "quotes") {
7909
+ const next = raw.charAt(i2 + 1);
7910
+ return /["'\\]/.test(next) ? {
7911
+ length: 2,
7912
+ text: next
7913
+ } : void 0;
7914
+ }
7915
+ JS_ESCAPE_RE.lastIndex = i2;
7916
+ const written2 = JS_ESCAPE_RE.exec(raw)?.[0];
7917
+ const text2 = written2 && stringLiteralValue(`"${written2}"`);
7918
+ return typeof text2 === "string" ? {
7919
+ length: written2.length,
7920
+ text: text2
7921
+ } : void 0;
7922
+ }
7923
+ function escapeOffsets(raw, at3, value, escapes) {
7840
7924
  const offsets = [];
7841
7925
  let escaped = false;
7842
7926
  let i2 = 0;
7843
7927
  while (offsets.length < value.length) {
7844
7928
  if (i2 >= raw.length) return void 0;
7845
- offsets.push(at3 + i2);
7846
- if (raw[i2] === "\\" && /["'\\]/.test(raw.charAt(i2 + 1))) {
7847
- escaped = true;
7848
- i2 += 2;
7849
- } else i2++;
7929
+ const escape2 = raw[i2] === "\\" ? escapeAt(raw, i2, escapes) : void 0;
7930
+ if (!escape2) {
7931
+ offsets.push(at3 + i2++);
7932
+ continue;
7933
+ }
7934
+ if (!value.startsWith(escape2.text, offsets.length)) return void 0;
7935
+ for (let k2 = 0; k2 < escape2.text.length; k2++) offsets.push(at3 + i2);
7936
+ escaped = true;
7937
+ i2 += escape2.length;
7850
7938
  }
7851
7939
  offsets.push(at3 + i2);
7852
7940
  return escaped ? offsets : void 0;
7853
7941
  }
7854
7942
  function* argPieces(args2, offset2, params, macro, label2) {
7855
- const name = macro.toLowerCase();
7856
7943
  let values;
7857
7944
  const spans = /* @__PURE__ */ new Map();
7858
7945
  try {
@@ -7865,10 +7952,34 @@ function* argPieces(args2, offset2, params, macro, label2) {
7865
7952
  offset: offset2,
7866
7953
  length: args2.length,
7867
7954
  label: label2,
7868
- macro: name
7955
+ macro: macro.toLowerCase()
7869
7956
  };
7870
7957
  return;
7871
7958
  }
7959
+ yield* valuePieces(args2, offset2, params, values, spans, macro, label2, "quotes");
7960
+ }
7961
+ function* widgetPieces(args2, offset2, params, macro, label2) {
7962
+ const written2 = splitArgSpans(args2);
7963
+ const declared = [];
7964
+ const values = {};
7965
+ const spans = /* @__PURE__ */ new Map();
7966
+ params.forEach(({ name, holds }, i2) => {
7967
+ const span = written2[i2];
7968
+ const value = span && stringLiteralValue(args2.slice(...span));
7969
+ if (!holds || value == null) return;
7970
+ const param = {
7971
+ name,
7972
+ type: "string",
7973
+ holds
7974
+ };
7975
+ declared.push(param);
7976
+ values[name] = value;
7977
+ spans.set(param, span);
7978
+ });
7979
+ yield* valuePieces(args2, offset2, declared, values, spans, macro, label2, "javascript");
7980
+ }
7981
+ function* valuePieces(args2, offset2, params, values, spans, macro, label2, escapes) {
7982
+ const name = macro.toLowerCase();
7872
7983
  function* visit(list3, from) {
7873
7984
  for (const param of list3) {
7874
7985
  const value = from[param.name];
@@ -7913,7 +8024,7 @@ function* argPieces(args2, offset2, params, macro, label2) {
7913
8024
  }
7914
8025
  const at3 = offset2 + start + (value !== written2 && /^["']/.test(written2) ? 1 : 0);
7915
8026
  const holds = stringHolds(param);
7916
- const sourceOffsets = escapeOffsets(args2.slice(at3 - offset2), at3, value);
8027
+ const sourceOffsets = escapeOffsets(args2.slice(at3 - offset2), at3, value, escapes);
7917
8028
  if (holds === "expression" || holds === "statements") {
7918
8029
  const piece = {
7919
8030
  kind: "code",
@@ -7966,15 +8077,13 @@ function attributeValues(src, token) {
7966
8077
  function sigilExpressions(value) {
7967
8078
  const found = [];
7968
8079
  let from = 0;
7969
- for (const part of splitSigilTemplate(value)) {
7970
- if (!("expr" in part)) continue;
7971
- const at3 = locate(value, part.expr, from);
7972
- from = at3 + part.expr.length;
8080
+ for (const part of splitSigilTemplate(value)) if ("expr" in part) {
7973
8081
  found.push({
7974
8082
  expr: part.expr,
7975
- at: at3
8083
+ at: from + 1
7976
8084
  });
7977
- }
8085
+ from += part.expr.length + 2;
8086
+ } else from += ("text" in part ? part.text : part.verbatim).length;
7978
8087
  return found;
7979
8088
  }
7980
8089
  function locate(text2, part, from) {
@@ -7983,23 +8092,6 @@ function locate(text2, part, from) {
7983
8092
  const anywhere = text2.indexOf(part);
7984
8093
  return anywhere >= 0 ? anywhere : from;
7985
8094
  }
7986
- function checkParameterTypes(macro, parameters) {
7987
- const holdsFix = `Give a \`string\` or \`text\` parameter one of ${STRING_HOLDS.join(", ")} to hold (see docs/custom-macros.md#what-a-string-holds).`;
7988
- for (const param of parameters) {
7989
- const type = param.type;
7990
- if (!PARAMETER_TYPES.includes(type)) throw parameterError(macro, param, type === void 0 ? "has no type" : `has the unknown type ${JSON.stringify(type)}`, `Give it one of the types ${PARAMETER_TYPES.join(", ")} (see docs/custom-macros.md#parameter-types), or declare no parameters and read props.rawArgs.`);
7991
- const holds = param.holds;
7992
- if (holds !== void 0 && type !== "string" && type !== "text") throw parameterError(macro, param, `is of the type ${type}, which holds what it is`, holdsFix);
7993
- if (holds !== void 0 && !STRING_HOLDS.includes(holds)) throw parameterError(macro, param, `holds the unknown ${JSON.stringify(holds)}`, holdsFix);
7994
- if (param.parameters) checkParameterTypes(macro, param.parameters);
7995
- }
7996
- }
7997
- function parameterError(macro, param, problem, fix) {
7998
- return /* @__PURE__ */ new Error(`spindle: The parameter "${param.name}" of the macro {${macro}} ${problem}. ${fix}`);
7999
- }
8000
- function getMacroRegistry() {
8001
- return Array.from(metadataRegistry.values());
8002
- }
8003
8095
  function textContainsChildren(text2, parametersOf2) {
8004
8096
  if (!text2.includes("{")) return false;
8005
8097
  let nodes;
@@ -8030,6 +8122,7 @@ function astContainsChildren(nodes, parametersOf2 = registeredParameters) {
8030
8122
  }, parametersOf2) || astContainsChildren(node.children, parametersOf2)) return true;
8031
8123
  }
8032
8124
  if (node.type === "macro") {
8125
+ if (node.name.toLowerCase() === "widget") continue;
8033
8126
  if (tokenTextContainsChildren({
8034
8127
  isClose: false,
8035
8128
  nameStart: 0,
@@ -8048,9 +8141,25 @@ function astContainsChildren(nodes, parametersOf2 = registeredParameters) {
8048
8141
  return false;
8049
8142
  }
8050
8143
  function widgetDef({ name = "", parameters = "" }) {
8144
+ const params = [];
8145
+ const holds = {};
8146
+ const badHolds = [];
8147
+ for (const word of parameters.split(/[\s,]+/)) {
8148
+ if (!word.startsWith("@")) continue;
8149
+ const [param = word, written2] = word.split(/:(.*)/s);
8150
+ params.push(param);
8151
+ if (written2 === void 0) continue;
8152
+ if (STRING_HOLDS.includes(written2)) holds[param] = written2;
8153
+ else badHolds.push({
8154
+ param,
8155
+ written: written2
8156
+ });
8157
+ }
8051
8158
  return {
8052
8159
  name,
8053
- params: parameters.split(/[\s,]+/).filter((word) => word.startsWith("@"))
8160
+ params,
8161
+ ...Object.keys(holds).length > 0 ? { holds } : {},
8162
+ ...badHolds.length > 0 ? { badHolds } : {}
8054
8163
  };
8055
8164
  }
8056
8165
  function parseWidgetDef(rawArgs) {
@@ -8102,8 +8211,11 @@ function widgetDefinitions$1(passages, parametersOf2 = registeredParameters) {
8102
8211
  }
8103
8212
  return found;
8104
8213
  }
8105
- function blockWidgetNames(passages, parametersOf2 = registeredParameters) {
8106
- return widgetDefinitions$1(passages, parametersOf2).filter((def) => def.block && def.closeStart !== void 0).map((def) => def.name);
8214
+ function effectiveWidgetDefinitions(definitions) {
8215
+ const inInit = (def) => def.passage === "StoryInit";
8216
+ const effective = new NameMap();
8217
+ for (const def of [...definitions.filter((def2) => !inInit(def2)), ...definitions.filter(inInit)]) if (def.closeStart !== void 0) effective.set(def.name, def);
8218
+ return [...effective.values()];
8107
8219
  }
8108
8220
  function distance(a, b) {
8109
8221
  let prev = Array.from({ length: b.length + 1 }, (_2, j2) => j2);
@@ -8257,20 +8369,6 @@ function validateMarkup(passages, options) {
8257
8369
  for (const [passage, tokens2] of parsed) withParseCache(() => checkPassage(passage, tokens2));
8258
8370
  return diagnostics;
8259
8371
  }
8260
- function variableNameError(name, label2 = name) {
8261
- return name === "__proto__" ? `"${label2}" cannot be used as a variable name (${RESERVED_NAME} is reserved)` : void 0;
8262
- }
8263
- function ownEntries(sources, keep = () => true) {
8264
- const record2 = /* @__PURE__ */ Object.create(null);
8265
- for (const source of sources) {
8266
- if (!source) continue;
8267
- for (const key of Object.keys(source)) {
8268
- const value = source[key];
8269
- if (keep(value)) record2[key] = value;
8270
- }
8271
- }
8272
- return record2;
8273
- }
8274
8372
  function staticLiteral(src) {
8275
8373
  const reader = new Reader(src);
8276
8374
  const value = reader.value();
@@ -8281,6 +8379,17 @@ function entryOrder(keys) {
8281
8379
  const all = [...keys];
8282
8380
  return [...all.filter((key) => ARRAY_INDEX_RE.test(key)).sort((a, b) => Number(a) - Number(b)), ...all.filter((key) => !ARRAY_INDEX_RE.test(key))];
8283
8381
  }
8382
+ function regexLiterals(src, goal = "expression", handlers = {}) {
8383
+ const ends = /* @__PURE__ */ new Map();
8384
+ lexJs(src, {
8385
+ ...handlers,
8386
+ literal(text2, index, nesting) {
8387
+ if (text2.charAt(0) === "/" && !/^\/[/*]/.test(text2)) ends.set(index, index + text2.length);
8388
+ handlers.literal?.(text2, index, nesting);
8389
+ }
8390
+ }, goal);
8391
+ return ends;
8392
+ }
8284
8393
  function bigintValue(digits) {
8285
8394
  try {
8286
8395
  return BigInt(digits.replace(/_/g, "")).toString();
@@ -8406,6 +8515,7 @@ function checkReference(ref2, declared) {
8406
8515
  const part = ref2.path[i2];
8407
8516
  if (current2.type === "array") return null;
8408
8517
  if (current2.type === "null") return null;
8518
+ if (current2.type === "string" && isArrayIndex(part)) continue;
8409
8519
  const sample = PRIMITIVE_SAMPLES[current2.type];
8410
8520
  if (sample && part in sample) {
8411
8521
  const memberType = typeof sample[part];
@@ -8421,9 +8531,20 @@ function checkReference(ref2, declared) {
8421
8531
  }
8422
8532
  return null;
8423
8533
  }
8424
- function referenceAt(code, index, at3) {
8534
+ function referenceAt(code, sigil, name, start, end, at3) {
8535
+ FIELD_PATH_RE.lastIndex = end;
8536
+ const fields = FIELD_PATH_RE.exec(code)[0];
8537
+ return {
8538
+ sigil,
8539
+ name,
8540
+ path: fields ? fields.slice(1).split(".") : [],
8541
+ start: at3(start),
8542
+ end: at3(end + fields.length)
8543
+ };
8544
+ }
8545
+ function termReferenceAt(term, index, at3) {
8425
8546
  VAR_PATH_RE.lastIndex = index;
8426
- const match = VAR_PATH_RE.exec(code);
8547
+ const match = VAR_PATH_RE.exec(term);
8427
8548
  if (!match) return void 0;
8428
8549
  const [name, ...path] = match[2].split(".");
8429
8550
  return {
@@ -8449,39 +8570,42 @@ function scanCode(code, at3, emit, goal = "expression") {
8449
8570
  }
8450
8571
  for (const ref2 of parsed.refs) {
8451
8572
  if (ref2.sigil !== "$" && ref2.sigil !== "%") continue;
8452
- const found = referenceAt(code, ref2.start, at3);
8453
- if (found) emit(found);
8573
+ emit(referenceAt(code, ref2.sigil, ref2.name, ref2.start, ref2.end, at3));
8454
8574
  }
8455
8575
  }
8456
8576
  function scanCodeLeniently(code, at3, emit, goal) {
8457
8577
  lexJs(code, {
8458
- variable(sigil, _name, index) {
8578
+ variable(sigil, name, index, _nesting, end) {
8459
8579
  if (sigil !== "$" && sigil !== "%") return;
8460
- const found = referenceAt(code, index, at3);
8461
- if (found) emit(found);
8580
+ emit(referenceAt(code, sigil, name, index, end, at3));
8462
8581
  },
8463
8582
  literal() {
8464
8583
  }
8465
8584
  }, goal);
8466
8585
  }
8467
- function scanArgs(token, params, at3, emit, options) {
8586
+ function scanPieces(pieces, at3, emit, options) {
8587
+ for (const piece of pieces) {
8588
+ const pieceAt = (i2) => at3(pieceOffset(piece, i2));
8589
+ if (piece.kind === "code") scanCode(piece.code, pieceAt, emit, piece.goal);
8590
+ else if (piece.kind === "text") scanInterpolations(piece.text, pieceAt, emit, {
8591
+ ...options,
8592
+ storeVarMacros: NO_STORE_VAR_MACROS
8593
+ });
8594
+ }
8595
+ }
8596
+ function scanArgs(token, parametersOf2, at3, emit, options) {
8468
8597
  if (!token.rawArgs) return;
8598
+ const params = parametersOf2(token.name);
8599
+ let byParameters = false;
8469
8600
  if (params) {
8470
8601
  if (options.all && !options.storeVarMacros.has(token.name.toLowerCase())) scanVariableParameters(token, params, at3, emit);
8471
8602
  const pieces = [...argPieces(token.rawArgs, token.argsStart, params, token.name, "")];
8472
- if (!pieces.some((piece) => piece.kind === "argument-error")) {
8473
- for (const piece of pieces) {
8474
- const pieceAt = (i2) => at3(pieceOffset(piece, i2));
8475
- if (piece.kind === "code") scanCode(piece.code, pieceAt, emit, piece.goal);
8476
- else if (piece.kind === "text") scanInterpolations(piece.text, pieceAt, emit, {
8477
- ...options,
8478
- storeVarMacros: NO_STORE_VAR_MACROS
8479
- });
8480
- }
8481
- return;
8482
- }
8603
+ byParameters = !pieces.some((piece) => piece.kind === "argument-error");
8604
+ if (byParameters) scanPieces(pieces, at3, emit, options);
8483
8605
  }
8484
- scanCode(token.rawArgs, (i2) => at3(token.argsStart + i2), emit);
8606
+ if (!byParameters) scanCode(token.rawArgs, (i2) => at3(token.argsStart + i2), emit);
8607
+ const widget = parametersOf2.widget?.(token.name);
8608
+ if (widget) scanPieces(widgetPieces(token.rawArgs, token.argsStart, widget, token.name, ""), at3, emit, options);
8485
8609
  }
8486
8610
  function scanVariableParameters(token, params, at3, emit) {
8487
8611
  const spans = /* @__PURE__ */ new Map();
@@ -8494,7 +8618,7 @@ function scanVariableParameters(token, params, at3, emit) {
8494
8618
  for (const param of params) {
8495
8619
  const span = spans.get(param);
8496
8620
  if (param.type !== "variable" || !span) continue;
8497
- const ref2 = referenceAt(token.rawArgs.slice(span[0], span[1]), 0, (i2) => at3(token.argsStart + span[0] + i2));
8621
+ const ref2 = termReferenceAt(token.rawArgs.slice(span[0], span[1]), 0, (i2) => at3(token.argsStart + span[0] + i2));
8498
8622
  if (ref2) emit(ref2);
8499
8623
  }
8500
8624
  }
@@ -8533,7 +8657,7 @@ function collectTokenRefs(content, tokens2, at3, emit, options) {
8533
8657
  else scanInterpolations(value, valueIn, emit, options);
8534
8658
  }
8535
8659
  else if (token.type === "macro" && !token.isClose) {
8536
- scanArgs(token, parametersOf2(token.name), at3, emit, options);
8660
+ scanArgs(token, parametersOf2, at3, emit, options);
8537
8661
  scanSelectors(content, token, at3, emit, options);
8538
8662
  if (storeVarMacros.has(token.name.toLowerCase())) {
8539
8663
  const lead = token.rawArgs.length - token.rawArgs.trimStart().length;
@@ -8569,7 +8693,7 @@ function collectTokenRefs(content, tokens2, at3, emit, options) {
8569
8693
  function collectVariableReferences(content, options) {
8570
8694
  const found = [];
8571
8695
  withParseCache(() => collectTokenRefs(content, tokensOf(content, false, options), (i2) => i2, (ref2) => found.push(ref2), options));
8572
- return found;
8696
+ return found.sort((a, b) => a.start - b.start);
8573
8697
  }
8574
8698
  function checkVariableReferences(passages, declared, options) {
8575
8699
  const diagnostics = [];
@@ -8593,10 +8717,13 @@ function checkVariableReferences(passages, declared, options) {
8593
8717
  }
8594
8718
  function codeOnly(source) {
8595
8719
  const chars = Array.from({ length: source.length }, () => " ");
8596
- lexJs(source, { code(ch, index) {
8720
+ const regexes = regexLiterals(source, "statements", { code(ch, index) {
8597
8721
  for (let k2 = 0; k2 < ch.length; k2++) chars[index + k2] = ch[k2];
8598
- } }, "statements");
8599
- return chars.join("");
8722
+ } });
8723
+ return {
8724
+ code: chars.join(""),
8725
+ regexes
8726
+ };
8600
8727
  }
8601
8728
  function written(props, key, read) {
8602
8729
  const value = read(props.get(key));
@@ -8659,18 +8786,23 @@ function macroOf(config2) {
8659
8786
  return macro;
8660
8787
  }
8661
8788
  function discoverMacros(source) {
8662
- const code = codeOnly(source);
8789
+ const { code, regexes } = codeOnly(source);
8663
8790
  const found = [];
8791
+ let bindings;
8664
8792
  for (const call of code.matchAll(CALL_RE)) {
8665
8793
  let at3 = call.index + call[0].length;
8666
8794
  if (code.charAt(at3) !== "{") {
8667
8795
  const variable = IDENTIFIER_ARGUMENT_RE.exec(code.slice(at3))?.[1];
8668
8796
  if (!variable) continue;
8669
- const declaration = new RegExp(`(?<![\\w$.])(?:const|let|var)\\s+${variable.replace(/\$/g, "\\$")}\\s*=\\s*(?=\\{)`).exec(code);
8670
- if (!declaration) continue;
8671
- at3 = declaration.index + declaration[0].length;
8672
- }
8673
- const reader = new ConfigReader(source);
8797
+ bindings ?? (bindings = new Bindings(code));
8798
+ const object3 = bindings.objectOf(variable, call.index, CONFIG_KEYS);
8799
+ if (object3 === void 0) continue;
8800
+ at3 = object3;
8801
+ }
8802
+ const reader = new ConfigReader(source, {
8803
+ goal: "statements",
8804
+ regexes
8805
+ });
8674
8806
  reader.i = at3;
8675
8807
  const config2 = reader.datum();
8676
8808
  const macro = config2 && macroOf(config2);
@@ -8682,8 +8814,9 @@ function validateStoryMarkup(passages, macros, options = {}) {
8682
8814
  const list3 = [...passages];
8683
8815
  const all = [...macros];
8684
8816
  const known = /* @__PURE__ */ new Set();
8685
- const parametersOf2 = parameterLookup(all);
8686
- const blocks = new Set(blockWidgetNames(list3, parametersOf2).map((n7) => n7.toLowerCase()));
8817
+ const widgets = widgetDefinitions$1(list3, parameterLookup(all));
8818
+ const blocks = new Set(effectiveWidgetDefinitions(widgets).filter((def) => def.block).map((def) => def.name.toLowerCase()));
8819
+ const parametersOf2 = parameterLookup([...all, ...definedWidgetMacros(widgets)]);
8687
8820
  for (const macro of all) {
8688
8821
  const name = macro.name.toLowerCase();
8689
8822
  known.add(name);
@@ -8699,6 +8832,12 @@ function validateStoryMarkup(passages, macros, options = {}) {
8699
8832
  tolerant: options.tolerant
8700
8833
  });
8701
8834
  }
8835
+ function definedWidgetMacros(widgets) {
8836
+ return widgets.map((widget) => ({
8837
+ name: widget.name,
8838
+ widget
8839
+ }));
8840
+ }
8702
8841
  function blockLookup(macros) {
8703
8842
  const blocks = new Set(macros.filter((m) => m.block).map((m) => m.name.toLowerCase()));
8704
8843
  return (name) => blocks.has(name.toLowerCase()) || isBlockMacro(name);
@@ -8711,12 +8850,13 @@ function passagePieces(source, macros = []) {
8711
8850
  function widgetDefinitions(passages, macros = []) {
8712
8851
  return widgetDefinitions$1(passages, parameterLookup([...macros]));
8713
8852
  }
8714
- function referenceOptions(macros, all = false) {
8853
+ function referenceOptions(macros, all = false, passages = []) {
8715
8854
  const list3 = [...macros];
8855
+ const widgets = widgetDefinitions$1(passages, parameterLookup(list3));
8716
8856
  return {
8717
8857
  all,
8718
8858
  storeVarMacros: new Set(list3.filter((m) => m.storeVar).map((m) => m.name.toLowerCase())),
8719
- parametersOf: parameterLookup(list3),
8859
+ parametersOf: parameterLookup([...list3, ...definedWidgetMacros(widgets)]),
8720
8860
  tolerant: true
8721
8861
  };
8722
8862
  }
@@ -8724,9 +8864,10 @@ function variableReferences(source, macros = [], options = {}) {
8724
8864
  return collectVariableReferences(source, referenceOptions(macros, options.all));
8725
8865
  }
8726
8866
  function validateVariableReferences(passages, declarations, macros = []) {
8727
- return checkVariableReferences([...passages].filter((p) => !p.tags?.some((t) => NOT_MARKUP_TAGS.includes(t))), declarations, referenceOptions(macros));
8867
+ const list3 = [...passages];
8868
+ return checkVariableReferences(list3.filter((p) => !p.tags?.some((t) => NOT_MARKUP_TAGS.includes(t))), declarations, referenceOptions(macros, false, list3));
8728
8869
  }
8729
- var SIGIL_SCOPES, SIGIL_CHARS, grammar_shared_default, LB, RB, SIGIL_SCOPES$1, withSelectors, VOID_TAGS, BRANCH_PARENT$1, LINK_SEPARATORS, NO_SELECTORS, EMAIL_AUTOLINK, peg$SyntaxError, NameMap, NameSet, BLOCK_MACROS, RAW_BODY_MACROS, BRANCHING, quoteArg, last$1, BRANCH_PARENT, isBranchTag, lowerName, nameOf, closerOf, closes, astralIdentifierCodes, astralIdentifierStartCodes, nonASCIIidentifierChars, nonASCIIidentifierStartChars, reservedWords, ecma5AndLessKeywords, keywords$1, keywordRelationalOperator, nonASCIIidentifierStart, nonASCIIidentifier, TokenType, beforeExpr, startsExpr, keywords, types$1, lineBreak, lineBreakG, nonASCIIwhitespace, skipWhiteSpace, ref, hasOwnProperty, toString, hasOwn, isArray, regexpCache, loneSurrogate, Position, SourceLocation, defaultOptions, warnedAboutEcmaVersion, SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_ARROW, SCOPE_SIMPLE_CATCH, SCOPE_SUPER, SCOPE_DIRECT_SUPER, SCOPE_CLASS_STATIC_BLOCK, SCOPE_CLASS_FIELD_INIT, SCOPE_SWITCH, SCOPE_VAR, BIND_NONE, BIND_VAR, BIND_LEXICAL, BIND_FUNCTION, BIND_SIMPLE_CATCH, BIND_OUTSIDE, Parser, prototypeAccessors, pp$9, literal, DestructuringErrors, pp$8, loopLabel, switchLabel, empty$1, FUNC_STATEMENT, FUNC_HANGING_STATEMENT, FUNC_NULLABLE_ID, pp$7, TokContext, types, pp$6, pp$5, empty, pp$4, pp$3, Scope, Node, pp$2, scriptValuesAddedInUnicode, ecma9BinaryProperties, ecma10BinaryProperties, ecma11BinaryProperties, ecma12BinaryProperties, ecma13BinaryProperties, unicodeBinaryProperties, unicodeBinaryPropertiesOfStrings, unicodeGeneralCategoryValues, ecma9ScriptValues, ecma10ScriptValues, ecma11ScriptValues, ecma12ScriptValues, ecma13ScriptValues, unicodeScriptValues, data, ecmaVersion, i, list, pp$1, BranchID, RegExpValidationState, CharSetNone, CharSetOk, CharSetString, Token, pp, INVALID_TEMPLATE_ESCAPE_ERROR, OPTIONS, AT_NAME_RE, TRANS_NAME_RE, SIGIL_IDENT_RE, LINE_BREAK_RE, NOT_A_BODY_RE, OPERAND_ENDS, SigilParser, OPENERS, CLOSERS, CodeSyntaxError, last, CLOSER, isNode, NAMESPACE_NAME, WORD_RE$2, FLAGS_RE, WORDS_BEFORE_STRING, NON_STRING_QUOTE_PREFIX, memoInput, memo, defaultCodeEnd, defaultHooks, MarkupError, OFFSET_FIELD, listeners, TRAILING_OPERATOR_RE, slice, MacroArgumentError, WORD_RE$1, NUMBER_RE$1, WORD_OR_PUNCT_RE, LEADING_OPERATOR_RE, takesRest, SUB_MACRO_PARAMETERS, parses, CONDITION_MACROS, PASSAGE_BODIES, holdsByDefault, textPiece, PARAMETER_TYPES, STRING_HOLDS, metadataRegistry, registeredParameters, WIDGET_PARAMETERS, NOT_MARKUP, NOT_MARKUP_TAGS$1, BRANCH_MACROS, didYouMean, PASSAGE_ARGUMENT_HINT, BARE_WORD_RE, RESERVED_NAME, createNamespace, NUMBER_RE, IDENT_RE, KEY_RE, ARROW_RE, ARRAY_INDEX_RE, UNSUPPORTED_UNDEFINED, Reader, VAR_PATH_RE, QUOTED_VAR_ARG_RE, VALID_VAR_TYPES, PRIMITIVE_SAMPLES, UNSUPPORTED_TYPE, NO_STORE_VAR_MACROS, CALL_RE, IDENTIFIER_ARGUMENT_RE, WORD_RE, ConfigReader, stringOf, booleanOf, textOf, stringsOf, NOT_MARKUP_TAGS;
8870
+ var SIGIL_SCOPES, SIGIL_CHARS, grammar_shared_default, LB, RB, SIGIL_SCOPES$1, withSelectors, VOID_TAGS, BRANCH_PARENT$1, LINK_SEPARATORS, NO_SELECTORS, EMAIL_AUTOLINK, peg$SyntaxError, NameMap, NameSet, BLOCK_MACROS, BLOCK_WIDGETS, RAW_BODY_MACROS, BRANCHING, quoteArg, last$1, BRANCH_PARENT, isBranchTag, lowerName, nameOf, closerOf, closes, astralIdentifierCodes, astralIdentifierStartCodes, nonASCIIidentifierChars, nonASCIIidentifierStartChars, reservedWords, ecma5AndLessKeywords, keywords$1, keywordRelationalOperator, nonASCIIidentifierStart, nonASCIIidentifier, TokenType, beforeExpr, startsExpr, keywords, types$1, lineBreak, lineBreakG, nonASCIIwhitespace, skipWhiteSpace, ref, hasOwnProperty, toString, hasOwn$1, isArray, regexpCache, loneSurrogate, Position, SourceLocation, defaultOptions, warnedAboutEcmaVersion, SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_ARROW, SCOPE_SIMPLE_CATCH, SCOPE_SUPER, SCOPE_DIRECT_SUPER, SCOPE_CLASS_STATIC_BLOCK, SCOPE_CLASS_FIELD_INIT, SCOPE_SWITCH, SCOPE_VAR, BIND_NONE, BIND_VAR, BIND_LEXICAL, BIND_FUNCTION, BIND_SIMPLE_CATCH, BIND_OUTSIDE, Parser, prototypeAccessors, pp$9, literal, DestructuringErrors, pp$8, loopLabel, switchLabel, empty$1, FUNC_STATEMENT, FUNC_HANGING_STATEMENT, FUNC_NULLABLE_ID, pp$7, TokContext, types, pp$6, pp$5, empty, pp$4, pp$3, Scope, Node, pp$2, scriptValuesAddedInUnicode, ecma9BinaryProperties, ecma10BinaryProperties, ecma11BinaryProperties, ecma12BinaryProperties, ecma13BinaryProperties, unicodeBinaryProperties, unicodeBinaryPropertiesOfStrings, unicodeGeneralCategoryValues, ecma9ScriptValues, ecma10ScriptValues, ecma11ScriptValues, ecma12ScriptValues, ecma13ScriptValues, unicodeScriptValues, data, ecmaVersion, i, list, pp$1, BranchID, RegExpValidationState, CharSetNone, CharSetOk, CharSetString, Token, pp, INVALID_TEMPLATE_ESCAPE_ERROR, OPTIONS, AT_NAME_RE, TRANS_NAME_RE, SIGIL_IDENT_RE, LINE_BREAK_RE, NOT_A_BODY_RE, OPERAND_ENDS, SigilParser, OPENERS, CLOSERS$1, CodeSyntaxError, last, CLOSER, isNode, NAMESPACE_NAME, WORD_RE$2, FLAGS_RE, WORDS_BEFORE_STRING, NON_STRING_QUOTE_PREFIX, memoInput, memo, defaultCodeEnd, defaultHooks, MarkupError, OFFSET_FIELD, listeners, TRAILING_OPERATOR_RE, slice, MacroArgumentError, WORD_RE$1, NUMBER_RE$1, WORD_OR_PUNCT_RE, LEADING_OPERATOR_RE, takesRest, PARAMETER_TYPES, STRING_HOLDS, metadataRegistry, SUB_MACRO_PARAMETERS, RESERVED_NAME, isArrayIndex, hasOwn, createNamespace, parses, CONDITION_MACROS, PASSAGE_BODIES, holdsByDefault, JS_ESCAPE_RE, textPiece, registeredParameters, WIDGET_PARAMETERS, NOT_MARKUP, NOT_MARKUP_TAGS$1, BRANCH_MACROS, didYouMean, PASSAGE_ARGUMENT_HINT, BARE_WORD_RE, NUMBER_RE, IDENT_RE, KEY_RE, ARROW_RE, LINE_END_RE, ARRAY_INDEX_RE, UNSUPPORTED_UNDEFINED, Reader, VAR_PATH_RE, QUOTED_VAR_ARG_RE, VALID_VAR_TYPES, PRIMITIVE_SAMPLES, UNSUPPORTED_TYPE, NO_STORE_VAR_MACROS, FIELD_PATH_RE, CLOSERS, BLOCK_WORDS, NOT_PARAMETERS, DECLARATIONS, WRITE_RE, MUTATORS, MUTATING_CALL_RE, BEFORE_BINDING, isWordChar, Bindings, CALL_RE, IDENTIFIER_ARGUMENT_RE, WORD_RE, MODIFIER_RE, KEY_START_RE, CONFIG_KEYS, ConfigReader, stringOf, booleanOf, textOf, stringsOf, NOT_MARKUP_TAGS;
8730
8871
  var init_story_variables = __esm({
8731
8872
  "node_modules/@rohal12/spindle/dist/pkg/story-variables.js"() {
8732
8873
  SIGIL_SCOPES = {
@@ -8891,6 +9032,9 @@ var init_story_variables = __esm({
8891
9032
  delete(name) {
8892
9033
  this.names.delete(name);
8893
9034
  }
9035
+ clear() {
9036
+ this.names.clear();
9037
+ }
8894
9038
  };
8895
9039
  BLOCK_MACROS = new NameSet([
8896
9040
  "if",
@@ -8908,6 +9052,7 @@ var init_story_variables = __esm({
8908
9052
  "span",
8909
9053
  "nobr"
8910
9054
  ]);
9055
+ BLOCK_WIDGETS = new NameSet();
8911
9056
  RAW_BODY_MACROS = /* @__PURE__ */ new Set(["do"]);
8912
9057
  BRANCHING = /* @__PURE__ */ new Set([
8913
9058
  "if",
@@ -10085,7 +10230,7 @@ var init_story_variables = __esm({
10085
10230
  ref = Object.prototype;
10086
10231
  hasOwnProperty = ref.hasOwnProperty;
10087
10232
  toString = ref.toString;
10088
- hasOwn = Object.hasOwn || (function(obj, propName) {
10233
+ hasOwn$1 = Object.hasOwn || (function(obj, propName) {
10089
10234
  return hasOwnProperty.call(obj, propName);
10090
10235
  });
10091
10236
  isArray = Array.isArray || (function(obj) {
@@ -11015,7 +11160,7 @@ var init_story_variables = __esm({
11015
11160
  var parent = len === 0 ? null : this.privateNameStack[len - 1];
11016
11161
  for (var i2 = 0; i2 < used.length; ++i2) {
11017
11162
  var id = used[i2];
11018
- if (!hasOwn(declared, id.name)) {
11163
+ if (!hasOwn$1(declared, id.name)) {
11019
11164
  if (parent) parent.used.push(id);
11020
11165
  else this.raiseRecoverable(id.start, "Private field '#" + id.name + "' must be declared in an enclosing class");
11021
11166
  }
@@ -11093,7 +11238,7 @@ var init_story_variables = __esm({
11093
11238
  pp$8.checkExport = function(exports$1, name, pos) {
11094
11239
  if (!exports$1) return;
11095
11240
  if (typeof name !== "string") name = name.type === "Identifier" ? name.name : name.value;
11096
- if (hasOwn(exports$1, name)) this.raiseRecoverable(pos, "Duplicate export '" + name + "'");
11241
+ if (hasOwn$1(exports$1, name)) this.raiseRecoverable(pos, "Duplicate export '" + name + "'");
11097
11242
  exports$1[name] = true;
11098
11243
  };
11099
11244
  pp$8.checkPatternExport = function(exports$1, pat) {
@@ -11212,7 +11357,7 @@ var init_story_variables = __esm({
11212
11357
  } else first = false;
11213
11358
  var attr = this.parseImportAttribute();
11214
11359
  var keyName = attr.key.type === "Identifier" ? attr.key.name : attr.key.value;
11215
- if (hasOwn(attributeKeys, keyName)) this.raiseRecoverable(attr.key.start, "Duplicate attribute key '" + keyName + "'");
11360
+ if (hasOwn$1(attributeKeys, keyName)) this.raiseRecoverable(attr.key.start, "Duplicate attribute key '" + keyName + "'");
11216
11361
  attributeKeys[keyName] = true;
11217
11362
  nodes.push(attr);
11218
11363
  }
@@ -11374,7 +11519,7 @@ var init_story_variables = __esm({
11374
11519
  if (isBind) {
11375
11520
  if (bindingType === BIND_LEXICAL && expr.name === "let") this.raiseRecoverable(expr.start, "let is disallowed as a lexically bound name");
11376
11521
  if (checkClashes) {
11377
- if (hasOwn(checkClashes, expr.name)) this.raiseRecoverable(expr.start, "Argument name clash");
11522
+ if (hasOwn$1(checkClashes, expr.name)) this.raiseRecoverable(expr.start, "Argument name clash");
11378
11523
  checkClashes[expr.name] = true;
11379
11524
  }
11380
11525
  if (bindingType !== BIND_OUTSIDE) this.declareName(expr.name, bindingType, expr.start);
@@ -13034,7 +13179,7 @@ var init_story_variables = __esm({
13034
13179
  return CharSetNone;
13035
13180
  };
13036
13181
  pp$1.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) {
13037
- if (!hasOwn(state.unicodeProperties.nonBinary, name)) state.raise("Invalid property name");
13182
+ if (!hasOwn$1(state.unicodeProperties.nonBinary, name)) state.raise("Invalid property name");
13038
13183
  if (!state.unicodeProperties.nonBinary[name].test(value)) state.raise("Invalid property value");
13039
13184
  };
13040
13185
  pp$1.regexp_validateUnicodePropertyNameOrValue = function(state, nameOrValue) {
@@ -14026,15 +14171,15 @@ var init_story_variables = __esm({
14026
14171
  }
14027
14172
  const self = this;
14028
14173
  const prevType = self.type;
14029
- this.prevValue = self.value;
14030
- const property = type.keyword !== void 0 && (prevType === types$1.dot || prevType === types$1.questionDot) || (type === types$1._function || type === types$1._class) && notABody(self.input, self.pos);
14174
+ this.prevValue = isMemberDot(this.typeBeforeLast) ? void 0 : self.value;
14175
+ const property = type.keyword !== void 0 && isMemberDot(prevType) || (type === types$1._function || type === types$1._class) && notABody(self.input, self.pos);
14031
14176
  if (!property && (type === types$1._function || type === types$1._class)) {
14032
14177
  const before = type === types$1._function && prevType === types$1.name && this.prevValue === "async" && !this.breakHere ? this.typeBeforeLast : prevType;
14033
14178
  self.type = before === types$1.colon && this.colonEndsTernary ? types$1.parenL : before;
14034
14179
  }
14035
14180
  this.typeBeforeLast = prevType;
14036
14181
  super.finishToken(property ? types$1.name : type, value);
14037
- if (type === types$1.name && value === "of" && (prevType === types$1._const || prevType === types$1._var || this.prevValue === "let")) self.exprAllowed = false;
14182
+ if (type === types$1.name && (isMemberDot(prevType) || value === "of" && (prevType === types$1._const || prevType === types$1._var || this.prevValue === "let"))) self.exprAllowed = false;
14038
14183
  }
14039
14184
  braceIsBlock(prevType) {
14040
14185
  if (prevType === types$1.colon && this.colonEndsTernary) return false;
@@ -14054,7 +14199,7 @@ var init_story_variables = __esm({
14054
14199
  types$1.braceL,
14055
14200
  types$1.dollarBraceL
14056
14201
  ]);
14057
- CLOSERS = /* @__PURE__ */ new Set([
14202
+ CLOSERS$1 = /* @__PURE__ */ new Set([
14058
14203
  types$1.parenR,
14059
14204
  types$1.bracketR,
14060
14205
  types$1.braceR
@@ -14163,28 +14308,6 @@ var init_story_variables = __esm({
14163
14308
  WORD_OR_PUNCT_RE = /\S+/y;
14164
14309
  LEADING_OPERATOR_RE = /^(?:[-+*/&|^=<>?:,.]|%(?![A-Za-z_]))/;
14165
14310
  takesRest = (param) => param.type === "expression" || param.type === "statements" || param.type === "passage" || param.type === "text";
14166
- SUB_MACRO_PARAMETERS = /* @__PURE__ */ new Map([["option", [{
14167
- name: "value",
14168
- type: "string",
14169
- holds: "text",
14170
- required: true
14171
- }]]]);
14172
- parses = null;
14173
- CONDITION_MACROS = /* @__PURE__ */ new Set([
14174
- "if",
14175
- "elseif",
14176
- "case"
14177
- ]);
14178
- PASSAGE_BODIES = /* @__PURE__ */ new Set(["dialog"]);
14179
- holdsByDefault = (interpolate) => interpolate ? "markup" : "text";
14180
- textPiece = (text2, offset2, where2) => ({
14181
- kind: "text",
14182
- text: text2,
14183
- offset: offset2,
14184
- where: where2,
14185
- tokens: [],
14186
- errors: []
14187
- });
14188
14311
  new NameMap();
14189
14312
  new NameMap();
14190
14313
  new NameSet();
@@ -14210,6 +14333,34 @@ var init_story_variables = __esm({
14210
14333
  "statements"
14211
14334
  ];
14212
14335
  metadataRegistry = new NameMap();
14336
+ SUB_MACRO_PARAMETERS = /* @__PURE__ */ new Map([["option", [{
14337
+ name: "value",
14338
+ type: "string",
14339
+ holds: "text",
14340
+ required: true
14341
+ }]]]);
14342
+ RESERVED_NAME = "__proto__";
14343
+ isArrayIndex = (key) => /^(?:0|[1-9]\d*)$/.test(key);
14344
+ hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
14345
+ createNamespace = (...sources) => ownEntries(sources);
14346
+ Object.freeze(createNamespace());
14347
+ parses = null;
14348
+ CONDITION_MACROS = /* @__PURE__ */ new Set([
14349
+ "if",
14350
+ "elseif",
14351
+ "case"
14352
+ ]);
14353
+ PASSAGE_BODIES = /* @__PURE__ */ new Set(["dialog"]);
14354
+ holdsByDefault = (interpolate) => interpolate ? "markup" : "text";
14355
+ JS_ESCAPE_RE = /\\(?:u\{[0-9a-fA-F]+\}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{2}|[0-3][0-7]{0,2}|[4-7][0-7]?|\r\n|[^])/y;
14356
+ textPiece = (text2, offset2, where2) => ({
14357
+ kind: "text",
14358
+ text: text2,
14359
+ offset: offset2,
14360
+ where: where2,
14361
+ tokens: [],
14362
+ errors: []
14363
+ });
14213
14364
  registeredParameters = (name) => parameterLookup(getMacroRegistry())(name);
14214
14365
  WIDGET_PARAMETERS = [{
14215
14366
  name: "name",
@@ -14237,22 +14388,26 @@ var init_story_variables = __esm({
14237
14388
  didYouMean = (best, quote) => best ? ` Did you mean ${quote(best)}?` : "";
14238
14389
  PASSAGE_ARGUMENT_HINT = " (a passage name is a quoted string or an expression)";
14239
14390
  BARE_WORD_RE = /^[A-Za-z][\w$]*$/;
14240
- RESERVED_NAME = "__proto__";
14241
- createNamespace = (...sources) => ownEntries(sources);
14242
- Object.freeze(createNamespace());
14243
- NUMBER_RE = /(?:0[xX][\da-fA-F_]+|0[bB][01_]+|0[oO][0-7_]+|(?:\d[\d_]*\.?[\d_]*|\.\d[\d_]*)(?:[eE][+-]?\d+)?)(n?)/y;
14391
+ NUMBER_RE = /(?:0[xX][\da-fA-F_]+|0[bB][01_]+|0[oO][0-7_]+|(?:\d[\d_]*\.?[\d_]*|\.\d[\d_]*)(?:[eE][+-]?\d[\d_]*)?)(n?)/y;
14244
14392
  IDENT_RE = /[A-Za-z_$][\w$]*/y;
14245
14393
  KEY_RE = /[\w$]+/y;
14246
14394
  ARROW_RE = /(?:async\s+)?(?:\((?:[^()]|\([^()]*\))*\)|[A-Za-z_$][\w$]*)\s*=>/y;
14395
+ LINE_END_RE = /[\n\r\u2028\u2029]/g;
14247
14396
  ARRAY_INDEX_RE = /^(?:0|[1-9]\d{0,8})$/;
14248
14397
  UNSUPPORTED_UNDEFINED = {
14249
14398
  unsupported: "undefined",
14250
14399
  value: "undefined"
14251
14400
  };
14252
14401
  Reader = class {
14253
- constructor(src) {
14402
+ /**
14403
+ * `regexes` (see regexLiterals) are read from `src` when they are first
14404
+ * needed, unless the caller has them already.
14405
+ */
14406
+ constructor(src, options = {}) {
14254
14407
  this.src = src;
14255
14408
  this.i = 0;
14409
+ this.goal = options.goal ?? "expression";
14410
+ this.regexes = options.regexes;
14256
14411
  }
14257
14412
  /** Skip whitespace and comments. */
14258
14413
  space() {
@@ -14260,8 +14415,8 @@ var init_story_variables = __esm({
14260
14415
  for (; ; ) {
14261
14416
  while (/\s/.test(src.charAt(this.i))) this.i++;
14262
14417
  if (src.startsWith("//", this.i)) {
14263
- const end = src.indexOf("\n", this.i);
14264
- this.i = end < 0 ? src.length : end;
14418
+ LINE_END_RE.lastIndex = this.i;
14419
+ this.i = LINE_END_RE.test(src) ? LINE_END_RE.lastIndex - 1 : src.length;
14265
14420
  } else if (src.startsWith("/*", this.i)) {
14266
14421
  const end = src.indexOf("*/", this.i + 2);
14267
14422
  this.i = end < 0 ? src.length : end + 2;
@@ -14320,10 +14475,9 @@ var init_story_variables = __esm({
14320
14475
  i2++;
14321
14476
  while (/\s/.test(src.charAt(i2))) i2++;
14322
14477
  }
14323
- NUMBER_RE.lastIndex = i2;
14324
- const match = NUMBER_RE.exec(src);
14478
+ const match = this.numberAt(i2);
14325
14479
  if (match) {
14326
- this.i = NUMBER_RE.lastIndex;
14480
+ this.i = i2 + match[0].length;
14327
14481
  if (!match[1]) return { shape: { type: "number" } };
14328
14482
  if (sign === "+") return { throws: "Cannot convert a BigInt value to a number" };
14329
14483
  const digits = bigintValue(match[0].slice(0, -1));
@@ -14333,11 +14487,17 @@ var init_story_variables = __esm({
14333
14487
  };
14334
14488
  }
14335
14489
  IDENT_RE.lastIndex = i2;
14336
- if (IDENT_RE.exec(src)?.[0] === "Infinity") {
14490
+ const word = IDENT_RE.exec(src)?.[0];
14491
+ if (word === "Infinity" || word === "NaN") {
14337
14492
  this.i = IDENT_RE.lastIndex;
14338
14493
  return { shape: { type: "number" } };
14339
14494
  }
14340
14495
  }
14496
+ /** The numeric literal (a BigInt's `n` is `match[1]`) at `at`, if there is one. */
14497
+ numberAt(at3) {
14498
+ NUMBER_RE.lastIndex = at3;
14499
+ return NUMBER_RE.exec(this.src);
14500
+ }
14341
14501
  /** A keyword, a function, or a name (which is not static). */
14342
14502
  word() {
14343
14503
  const { src } = this;
@@ -14357,6 +14517,7 @@ var init_story_variables = __esm({
14357
14517
  this.i = end;
14358
14518
  return { shape: { type: "null" } };
14359
14519
  case "NaN":
14520
+ case "Infinity":
14360
14521
  this.i = end;
14361
14522
  return { shape: { type: "number" } };
14362
14523
  case "undefined":
@@ -14498,14 +14659,22 @@ var init_story_variables = __esm({
14498
14659
  const { src } = this;
14499
14660
  const c = src.charAt(this.i);
14500
14661
  if (c === '"' || c === "'") return this.stringValue();
14662
+ if (/[\d.]/.test(c)) return this.numberKey();
14501
14663
  KEY_RE.lastIndex = this.i;
14502
14664
  const found = KEY_RE.exec(src)?.[0];
14503
14665
  if (found === void 0) return void 0;
14504
14666
  this.i = KEY_RE.lastIndex;
14505
- if (!/^\d/.test(found)) return found;
14506
- const digits = found.replace(/_/g, "");
14667
+ return found;
14668
+ }
14669
+ /** A number as a key: the whole literal, named as the runtime names it. */
14670
+ numberKey() {
14671
+ const match = this.numberAt(this.i);
14672
+ if (!match) return void 0;
14673
+ this.i += match[0].length;
14674
+ if (/[\w$]/.test(this.src.charAt(this.i))) return void 0;
14675
+ const digits = match[0].replace(/_/g, "");
14507
14676
  if (/^0\d/.test(digits)) return void 0;
14508
- if (digits.endsWith("n") && !/^0[xX]/.test(digits)) return bigintValue(digits.slice(0, -1));
14677
+ if (match[1]) return bigintValue(digits.slice(0, -1));
14509
14678
  const value = Number(digits);
14510
14679
  return Number.isNaN(value) ? void 0 : String(value);
14511
14680
  }
@@ -14548,6 +14717,14 @@ var init_story_variables = __esm({
14548
14717
  this.space();
14549
14718
  return true;
14550
14719
  }
14720
+ if (c === "/") {
14721
+ this.regexes ?? (this.regexes = regexLiterals(src, this.goal));
14722
+ const end = this.regexes.get(this.i);
14723
+ if (end !== void 0) {
14724
+ this.i = end;
14725
+ return true;
14726
+ }
14727
+ }
14551
14728
  if (c === "[" || c === "{" || c === "(") return this.skipBalanced();
14552
14729
  if (c === "]" || c === "}" || c === ")") return false;
14553
14730
  this.i++;
@@ -14585,9 +14762,470 @@ var init_story_variables = __esm({
14585
14762
  };
14586
14763
  UNSUPPORTED_TYPE = "Expected number, string, boolean, array, or object.";
14587
14764
  NO_STORE_VAR_MACROS = /* @__PURE__ */ new Set();
14765
+ FIELD_PATH_RE = /(?:\.\w+)*/y;
14766
+ CLOSERS = {
14767
+ "}": "{",
14768
+ ")": "(",
14769
+ "]": "["
14770
+ };
14771
+ BLOCK_WORDS = /* @__PURE__ */ new Set([
14772
+ "if",
14773
+ "for",
14774
+ "while",
14775
+ "switch",
14776
+ "with",
14777
+ "catch"
14778
+ ]);
14779
+ NOT_PARAMETERS = /* @__PURE__ */ new Set([
14780
+ "if",
14781
+ "for",
14782
+ "while",
14783
+ "switch",
14784
+ "with"
14785
+ ]);
14786
+ DECLARATIONS = /* @__PURE__ */ new Set([
14787
+ "const",
14788
+ "let",
14789
+ "var"
14790
+ ]);
14791
+ WRITE_RE = /(?:(?:\*\*|<<|>>>?|&&|\|\||\?\?|[-+*/%&|^])=(?!=)|=(?![=>])|\+\+|--)/y;
14792
+ MUTATORS = /* @__PURE__ */ new Set([
14793
+ "push",
14794
+ "pop",
14795
+ "shift",
14796
+ "unshift",
14797
+ "splice",
14798
+ "sort",
14799
+ "reverse",
14800
+ "fill",
14801
+ "copyWithin"
14802
+ ]);
14803
+ MUTATING_CALL_RE = /(?:Object|Reflect)\s*\.\s*(?:assign|defineProperty|defineProperties|setPrototypeOf|set|deleteProperty)\s*$/;
14804
+ BEFORE_BINDING = {
14805
+ "{": "{,:",
14806
+ "[": "[,",
14807
+ "(": "(,"
14808
+ };
14809
+ isWordChar = (c) => !!c && /[\w$]/.test(c);
14810
+ Bindings = class {
14811
+ constructor(code) {
14812
+ this.code = code;
14813
+ this.groups = [];
14814
+ this.byOpen = /* @__PURE__ */ new Map();
14815
+ this.byClose = /* @__PURE__ */ new Map();
14816
+ this.functions = /* @__PURE__ */ new Map();
14817
+ this.top = {
14818
+ start: -1,
14819
+ end: Infinity
14820
+ };
14821
+ this.readGroups();
14822
+ }
14823
+ /**
14824
+ * The `{` of the object literal that `name`, used at `at`, is declared as
14825
+ * (`const`, `let` or `var`), when that is the declaration it names: the
14826
+ * innermost one in the scope of the use, that comes before it unless the
14827
+ * use is in a function of its own. `undefined` if it names another value,
14828
+ * more than one declaration is the innermost, or the value is written to
14829
+ * (given another, or one of its `properties` set) where the use can follow.
14830
+ */
14831
+ objectOf(name, at3, properties) {
14832
+ const { code } = this;
14833
+ const escaped = name.replace(/\$/g, "\\$");
14834
+ const bindings = [];
14835
+ for (const m of code.matchAll(new RegExp(`(?<![\\w$])${escaped}(?![\\w$])`, "g"))) {
14836
+ const binding = this.bindingAt(m.index, name.length, properties);
14837
+ if (binding) bindings.push(binding);
14838
+ }
14839
+ const declarations = bindings.filter((b) => !b.write);
14840
+ for (const b of bindings) if (b.write) b.scope = this.innermostVisible(declarations, b.at)[0]?.scope ?? this.top;
14841
+ const found = this.innermostVisible(bindings, at3);
14842
+ return found.length === 1 ? found[0].object : void 0;
14843
+ }
14844
+ /**
14845
+ * Of the `bindings` the use at `at` can see, those in the innermost scope:
14846
+ * the scope is the use's, and a binding comes before it, unless the use is
14847
+ * in a function of its own.
14848
+ */
14849
+ innermostVisible(bindings, at3) {
14850
+ const useFunction = this.functionStart(at3);
14851
+ const visible = bindings.filter((b) => b.scope.start <= at3 && at3 < b.scope.end && (!b.ordered || b.at < at3 || this.functionStart(b.at) !== useFunction));
14852
+ const innermost = Math.max(...visible.map((b) => b.scope.start));
14853
+ return visible.filter((b) => b.scope.start === innermost);
14854
+ }
14855
+ readGroups() {
14856
+ const { code, groups } = this;
14857
+ const stack = [];
14858
+ for (let i2 = 0; i2 < code.length; i2++) {
14859
+ const c = code.charAt(i2);
14860
+ if (c === "{" || c === "(" || c === "[") {
14861
+ const group = {
14862
+ kind: c,
14863
+ open: i2,
14864
+ close: code.length,
14865
+ parent: stack[stack.length - 1]
14866
+ };
14867
+ groups.push(group);
14868
+ stack.push(group);
14869
+ this.byOpen.set(i2, group);
14870
+ } else if (CLOSERS[c]) {
14871
+ let k2 = stack.length - 1;
14872
+ while (k2 >= 0 && stack[k2].kind !== CLOSERS[c]) k2--;
14873
+ if (k2 >= 0) {
14874
+ stack[k2].close = i2;
14875
+ this.byClose.set(i2, stack[k2]);
14876
+ stack.length = k2;
14877
+ }
14878
+ }
14879
+ }
14880
+ }
14881
+ /** The innermost group that has `index` inside it, of this kind if given. */
14882
+ innermost(index, kind) {
14883
+ const { groups } = this;
14884
+ for (let k2 = groups.length - 1; k2 >= 0; k2--) {
14885
+ const g = groups[k2];
14886
+ if (g.open < index && index < g.close && (!kind || g.kind === kind)) return g;
14887
+ }
14888
+ }
14889
+ /**
14890
+ * The word before a `(` group (`if`, `for`, `catch`, a function's name…);
14891
+ * `for await` is `for`.
14892
+ */
14893
+ ownerWord(paren) {
14894
+ const word = this.wordBefore(this.backFrom(paren.open) + 1);
14895
+ if (word.text !== "await") return word.text;
14896
+ const before = this.wordBefore(this.backFrom(word.start) + 1).text;
14897
+ return before === "for" ? before : word.text;
14898
+ }
14899
+ /**
14900
+ * Whether the `{` group is the body of a function (or method), or of a
14901
+ * class static block, which has the scope of a `var` of its own.
14902
+ */
14903
+ isFunctionBody(group) {
14904
+ if (group.kind !== "{") return false;
14905
+ let known = this.functions.get(group);
14906
+ if (known === void 0) {
14907
+ const before = this.backFrom(group.open);
14908
+ const { code } = this;
14909
+ if (code.charAt(before) === ")") {
14910
+ const params = this.byClose.get(before);
14911
+ known = !!params && !BLOCK_WORDS.has(this.ownerWord(params));
14912
+ } else known = code.slice(before - 1, before + 1) === "=>" || this.wordBefore(before + 1).text === "static";
14913
+ this.functions.set(group, known);
14914
+ }
14915
+ return known;
14916
+ }
14917
+ /** The start of the function that has `index` in its body, or -1. */
14918
+ functionStart(index) {
14919
+ for (let g = this.innermost(index, "{"); g; g = g.parent) if (g.kind === "{" && this.isFunctionBody(g)) return g.open;
14920
+ return -1;
14921
+ }
14922
+ scopeOf(group) {
14923
+ return group ? {
14924
+ start: group.open,
14925
+ end: group.close
14926
+ } : this.top;
14927
+ }
14928
+ /** The block `index` is in: what `let` and `const` declare for. */
14929
+ blockAt(index) {
14930
+ return this.scopeOf(this.innermost(index, "{"));
14931
+ }
14932
+ /** The function (or the whole text) `index` is in: what `var` declares for. */
14933
+ functionAt(index) {
14934
+ const start = this.functionStart(index);
14935
+ return start < 0 ? this.top : this.scopeOf(this.byOpen.get(start));
14936
+ }
14937
+ /**
14938
+ * What the declaration keyword `word` (`var`, `let`, `const`) declares for:
14939
+ * the function for `var`, the block for the others, or the `for` statement,
14940
+ * header and body, when the declaration is in its header.
14941
+ */
14942
+ declaredIn(word) {
14943
+ if (word.text === "var") return this.functionAt(word.start);
14944
+ const group = this.innermost(word.start);
14945
+ if (group?.kind === "(" && this.ownerWord(group) === "for") return {
14946
+ start: group.open,
14947
+ end: this.statementEnd(group.close + 1)
14948
+ };
14949
+ return this.blockAt(word.start);
14950
+ }
14951
+ /**
14952
+ * Where the statement that starts at `from` ends: a block, the body of an
14953
+ * `if`, `for`, `while` or `with` (and the `else` of an `if`), or else up to
14954
+ * a `;`, the closer of the group it is in, or a line break that is followed
14955
+ * by a statement of its own.
14956
+ */
14957
+ statementEnd(from) {
14958
+ const { code } = this;
14959
+ const start = this.forward(from);
14960
+ const block = this.byOpen.get(start);
14961
+ if (block?.kind === "{") return Math.min(block.close + 1, code.length);
14962
+ const word = this.wordAt(start);
14963
+ if (/^(?:if|for|while|with)$/.test(word)) {
14964
+ let paren = this.forward(start + word.length);
14965
+ if (word === "for" && this.wordAt(paren) === "await") paren = this.forward(paren + 5);
14966
+ const header = this.byOpen.get(paren);
14967
+ if (header) {
14968
+ const end = this.statementEnd(header.close + 1);
14969
+ const next = this.forward(end);
14970
+ return word === "if" && this.wordAt(next) === "else" ? this.statementEnd(next + 4) : end;
14971
+ }
14972
+ }
14973
+ for (let end = start; end < code.length; end++) {
14974
+ const c = code.charAt(end);
14975
+ if (c === ";") return end + 1;
14976
+ if (CLOSERS[c]) return end;
14977
+ const group = this.byOpen.get(end);
14978
+ if (group) end = group.close;
14979
+ else if (c === "\n" && this.startsStatement(end)) return end;
14980
+ }
14981
+ return code.length;
14982
+ }
14983
+ /** Whether the line break at `index` ends a statement, and one follows. */
14984
+ startsStatement(index) {
14985
+ const next = this.forward(index);
14986
+ return /[\w$)\]}]/.test(this.code.charAt(this.backFrom(index))) && isWordChar(this.code.charAt(next)) && !/^(?:in|of|instanceof|else)$/.test(this.wordAt(next));
14987
+ }
14988
+ /** What a parameter list, whose `)` is `close`, is the scope of: the body. */
14989
+ bodyAfter(close) {
14990
+ const next = this.forward(close + 1);
14991
+ if (this.code.charAt(next) === "{") return this.scopeOf(this.byOpen.get(next));
14992
+ return this.code.startsWith("=>", next) ? this.arrowBody(next + 2) : void 0;
14993
+ }
14994
+ /** The body of an arrow function, whose `=>` ends before `from`. */
14995
+ arrowBody(from) {
14996
+ const { code } = this;
14997
+ const start = this.forward(from);
14998
+ if (code.charAt(start) === "{") return this.scopeOf(this.byOpen.get(start));
14999
+ let end = start;
15000
+ while (end < code.length && !",;)]}".includes(code.charAt(end))) {
15001
+ end = this.byOpen.get(end)?.close ?? end;
15002
+ end++;
15003
+ }
15004
+ return {
15005
+ start,
15006
+ end
15007
+ };
15008
+ }
15009
+ /** The index of the last character that is no space before `index`, or -1. */
15010
+ backFrom(index) {
15011
+ let i2 = index - 1;
15012
+ while (i2 >= 0 && /\s/.test(this.code.charAt(i2))) i2--;
15013
+ return i2;
15014
+ }
15015
+ /** The index of the first character that is no space from `index` on. */
15016
+ forward(index) {
15017
+ let i2 = index;
15018
+ while (i2 < this.code.length && /\s/.test(this.code.charAt(i2))) i2++;
15019
+ return i2;
15020
+ }
15021
+ /** The word that starts at `index` (empty if there is none). */
15022
+ wordAt(index) {
15023
+ return this.code.slice(index, this.nameEnd(index));
15024
+ }
15025
+ /** The word that ends before `end`, and where it starts. */
15026
+ wordBefore(end) {
15027
+ let start = end;
15028
+ while (start > 0 && isWordChar(this.code.charAt(start - 1))) start--;
15029
+ return {
15030
+ text: this.code.slice(start, end),
15031
+ start
15032
+ };
15033
+ }
15034
+ /**
15035
+ * What the name of `length` characters at `at` declares, or writes to, if it
15036
+ * does. A write to a `properties` of it counts, one to another does not.
15037
+ */
15038
+ bindingAt(at3, length, properties) {
15039
+ const { code } = this;
15040
+ let before = this.backFrom(at3);
15041
+ const spread = code.slice(before - 2, before + 1) === "...";
15042
+ if (code.charAt(before) === "." && !spread) return void 0;
15043
+ if (spread) before = this.backFrom(before - 2);
15044
+ const after = this.forward(at3 + length);
15045
+ return this.declarationAt(at3, before, spread) ?? this.patternAt(at3, before, after) ?? this.arrowParameterAt(at3, after) ?? (spread ? void 0 : this.writeAt(at3, before, after, properties));
15046
+ }
15047
+ /** `const x`, `let x`, `var x`, a later `y` of `let x, y`, `function x`, `class x`. */
15048
+ declarationAt(at3, before, spread) {
15049
+ if (spread) return void 0;
15050
+ const word = this.code.charAt(before) === "," ? this.listKeyword(before) : this.wordBefore(before + 1);
15051
+ if (!word || this.code.charAt(word.start - 1) === ".") return void 0;
15052
+ if (DECLARATIONS.has(word.text)) {
15053
+ const scope = this.declaredIn(word);
15054
+ const init = /\s*=\s*(?=\{)/y;
15055
+ init.lastIndex = this.nameEnd(at3);
15056
+ return {
15057
+ scope,
15058
+ at: at3,
15059
+ ordered: true,
15060
+ object: init.test(this.code) ? init.lastIndex : void 0
15061
+ };
15062
+ }
15063
+ if (word.text === "function" || word.text === "class") return {
15064
+ scope: this.blockAt(word.start),
15065
+ at: at3,
15066
+ ordered: word.text === "class"
15067
+ };
15068
+ }
15069
+ /**
15070
+ * The `const`, `let` or `var` of the declaration list that the `,` at
15071
+ * `comma` is in (`let a = 1, b`, also in a `for` header): the nearest one
15072
+ * before it in the same brackets, that no `;` is between.
15073
+ */
15074
+ listKeyword(comma) {
15075
+ const { code } = this;
15076
+ const floor = this.innermost(comma)?.open ?? -1;
15077
+ for (let i2 = comma - 1; i2 > floor; i2--) {
15078
+ const c = code.charAt(i2);
15079
+ if (c === ";") return void 0;
15080
+ const group = CLOSERS[c] ? this.byClose.get(i2) : void 0;
15081
+ if (group) i2 = group.open;
15082
+ else if (isWordChar(c)) {
15083
+ const word = this.wordBefore(i2 + 1);
15084
+ if (DECLARATIONS.has(word.text) && code.charAt(word.start - 1) !== ".") return word;
15085
+ i2 = word.start;
15086
+ }
15087
+ }
15088
+ }
15089
+ nameEnd(at3) {
15090
+ let end = at3;
15091
+ while (isWordChar(this.code.charAt(end))) end++;
15092
+ return end;
15093
+ }
15094
+ /**
15095
+ * A name in a pattern (`const { x } = …`) or a parameter list; or one a
15096
+ * pattern assigns to (`({ x } = …)`).
15097
+ */
15098
+ patternAt(at3, before, after) {
15099
+ const { code } = this;
15100
+ const inner = this.innermost(at3);
15101
+ if (!inner || !BEFORE_BINDING[inner.kind].includes(code.charAt(before))) return;
15102
+ if (inner.kind === "{" && code.charAt(after) === ":") return void 0;
15103
+ let pattern;
15104
+ for (let group = inner; group; group = group.parent) {
15105
+ if (group.kind === "(") {
15106
+ const scope = NOT_PARAMETERS.has(this.ownerWord(group)) ? void 0 : this.bodyAfter(group.close);
15107
+ return scope ? {
15108
+ scope,
15109
+ at: at3,
15110
+ ordered: false
15111
+ } : this.patternWriteAt(at3, pattern);
15112
+ }
15113
+ pattern = group;
15114
+ const open = this.backFrom(group.open);
15115
+ const word = code.charAt(open) === "," ? this.listKeyword(open) : this.wordBefore(open + 1);
15116
+ if (word && DECLARATIONS.has(word.text)) return {
15117
+ scope: this.declaredIn(word),
15118
+ at: at3,
15119
+ ordered: true
15120
+ };
15121
+ if (!"({[,:".includes(code.charAt(open))) break;
15122
+ }
15123
+ return this.patternWriteAt(at3, pattern);
15124
+ }
15125
+ /** `[x] = …`, `({ x } = …)`, `for ({ x } of …)`: a name that is assigned to. */
15126
+ patternWriteAt(at3, pattern) {
15127
+ if (!pattern) return void 0;
15128
+ const { code } = this;
15129
+ const open = this.backFrom(pattern.open);
15130
+ if (/[\w$)\]]/.test(code.charAt(open))) return void 0;
15131
+ const next = this.forward(pattern.close + 1);
15132
+ return pattern.parent?.kind === "(" && this.ownerWord(pattern.parent) === "for" && /^(?:of|in)(?![\w$])/.test(code.slice(next)) || this.isWriteOperator(next) ? {
15133
+ scope: this.top,
15134
+ at: at3,
15135
+ ordered: true,
15136
+ write: true
15137
+ } : void 0;
15138
+ }
15139
+ /** `x => …`. */
15140
+ arrowParameterAt(at3, after) {
15141
+ return this.code.startsWith("=>", after) ? {
15142
+ scope: this.arrowBody(after + 2),
15143
+ at: at3,
15144
+ ordered: false
15145
+ } : void 0;
15146
+ }
15147
+ /** Whether an assignment, `++` or `--` operator is at `index`. */
15148
+ isWriteOperator(index) {
15149
+ WRITE_RE.lastIndex = index;
15150
+ return WRITE_RE.test(this.code);
15151
+ }
15152
+ /**
15153
+ * `x = …`, `x += …`, `x++`, `for (x of …)`, and the writes to what it
15154
+ * holds that change the config it is (`x.name = …`, `delete x.name`,
15155
+ * `x.parameters.push(…)`, `Object.assign(x, …)`): a value not known.
15156
+ */
15157
+ writeAt(at3, before, after, properties) {
15158
+ const { code } = this;
15159
+ if (this.isClassBody(this.innermost(at3))) return void 0;
15160
+ const { keys, end } = this.memberChain(after);
15161
+ const written2 = ["++", "--"].includes(code.slice(before - 1, before + 1)) || this.isWriteOperator(end) || keys.length > 0 && (this.wordBefore(before + 1).text === "delete" || keys.length > 1 && code.charAt(end) === "(" && MUTATORS.has(keys[keys.length - 1])) || keys.length === 0 && this.isForTarget(before, end) || keys.length === 0 && this.isMutatedArgument(before, end);
15162
+ const first = keys[0];
15163
+ if (!written2 || first && !properties.has(first)) return void 0;
15164
+ return {
15165
+ scope: this.top,
15166
+ at: at3,
15167
+ ordered: true,
15168
+ write: true
15169
+ };
15170
+ }
15171
+ /** Whether the group is the body of a class: its members are its own. */
15172
+ isClassBody(group) {
15173
+ if (group?.kind !== "{") return false;
15174
+ let start = group.open;
15175
+ while (start > 0 && !"{};".includes(this.code.charAt(start - 1))) start--;
15176
+ return /(?<![\w$.])class(?![\w$])/.test(this.code.slice(start, group.open));
15177
+ }
15178
+ /**
15179
+ * The properties read from a name at `from` (`.a.b[c]`, a key is null if
15180
+ * computed), and where the chain ends.
15181
+ */
15182
+ memberChain(from) {
15183
+ const { code } = this;
15184
+ const keys = [];
15185
+ let end = from;
15186
+ for (; ; ) {
15187
+ let i2 = end;
15188
+ if (code.startsWith("?.", i2)) i2 += 2;
15189
+ else if (code.charAt(i2) === "." && code.charAt(i2 + 1) !== ".") i2++;
15190
+ else if (code.charAt(i2) !== "[") break;
15191
+ i2 = this.forward(i2);
15192
+ const bracket = code.charAt(i2) === "[" ? this.byOpen.get(i2) : void 0;
15193
+ const key = bracket ? "" : this.wordAt(i2);
15194
+ if (!bracket && !key) break;
15195
+ keys.push(bracket ? null : key);
15196
+ end = this.forward(bracket ? bracket.close + 1 : i2 + key.length);
15197
+ }
15198
+ return {
15199
+ keys,
15200
+ end
15201
+ };
15202
+ }
15203
+ /** `for (x of …)`: the name is assigned each item. */
15204
+ isForTarget(before, end) {
15205
+ const group = this.byOpen.get(before);
15206
+ return group?.kind === "(" && this.ownerWord(group) === "for" && /^(?:of|in)(?![\w$])/.test(this.code.slice(end));
15207
+ }
15208
+ /** `Object.assign(x, …)` and the like: the name is the first argument. */
15209
+ isMutatedArgument(before, end) {
15210
+ const group = this.byOpen.get(before);
15211
+ return group?.kind === "(" && ",)".includes(this.code.charAt(end)) && MUTATING_CALL_RE.test(this.code.slice(0, group.open));
15212
+ }
15213
+ };
14588
15214
  CALL_RE = /(?<![\w$])defineMacro\s*\(\s*/g;
14589
15215
  IDENTIFIER_ARGUMENT_RE = /^([A-Za-z_$][\w$]*)\s*[,)]/;
14590
15216
  WORD_RE = /[A-Za-z_]+/y;
15217
+ MODIFIER_RE = /^(?:get|set|async)?$/;
15218
+ KEY_START_RE = /[\w$'"[.*]/;
15219
+ CONFIG_KEYS = /* @__PURE__ */ new Set([
15220
+ "name",
15221
+ "block",
15222
+ "subMacros",
15223
+ "parameters",
15224
+ "interpolate",
15225
+ "storeVar",
15226
+ "merged",
15227
+ "description"
15228
+ ]);
14591
15229
  ConfigReader = class extends Reader {
14592
15230
  constructor(..._args) {
14593
15231
  super(..._args);
@@ -14686,18 +15324,47 @@ var init_story_variables = __esm({
14686
15324
  }
14687
15325
  record() {
14688
15326
  const props = /* @__PURE__ */ new Map();
14689
- return this.elements("}", () => {
14690
- const key = this.key();
14691
- this.space();
14692
- if (key !== void 0 && this.src.charAt(this.i) === ":") {
14693
- this.i++;
14694
- props.set(key, this.member());
14695
- } else if (!this.skipMember()) this.fail();
14696
- }) ? {
15327
+ return this.elements("}", () => this.property(props)) ? {
14697
15328
  type: "object",
14698
15329
  props
14699
15330
  } : void 0;
14700
15331
  }
15332
+ /**
15333
+ * A member: what it sets replaces an earlier member of the key. One that is
15334
+ * not a literal (a method, an accessor, a shorthand) sets its key to a
15335
+ * value not known; a spread of an object literal sets the members of it, a
15336
+ * spread of anything else or a computed key can set any key.
15337
+ */
15338
+ property(props) {
15339
+ const { src } = this;
15340
+ if (src.startsWith("...", this.i)) {
15341
+ this.i += 3;
15342
+ this.space();
15343
+ const spread = src.charAt(this.i) === "{" ? this.record() : void 0;
15344
+ this.space();
15345
+ if (spread?.type === "object" && this.atElementEnd()) {
15346
+ for (const [key, value] of spread.props) props.set(key, value);
15347
+ return;
15348
+ }
15349
+ props.clear();
15350
+ } else {
15351
+ let key = this.key();
15352
+ this.space();
15353
+ if (key !== void 0 && src.charAt(this.i) === ":") {
15354
+ this.i++;
15355
+ props.set(key, this.member());
15356
+ return;
15357
+ }
15358
+ if (MODIFIER_RE.test(key ?? "") && KEY_START_RE.test(src.charAt(this.i))) {
15359
+ if (src.charAt(this.i) === "*") this.i++;
15360
+ this.space();
15361
+ key = this.key();
15362
+ }
15363
+ if (key === void 0) props.clear();
15364
+ else props.set(key, void 0);
15365
+ }
15366
+ if (!this.skipMember()) this.fail();
15367
+ }
14701
15368
  /** Stop reading: the text cannot be skipped through. */
14702
15369
  fail() {
14703
15370
  this.failed = true;
@@ -14956,48 +15623,56 @@ var init_macro_registry = __esm({
14956
15623
  "name": "story-title",
14957
15624
  "block": false,
14958
15625
  "subMacros": [],
15626
+ "interpolate": true,
14959
15627
  "source": "builtin"
14960
15628
  },
14961
15629
  {
14962
15630
  "name": "back",
14963
15631
  "block": false,
14964
15632
  "subMacros": [],
15633
+ "interpolate": true,
14965
15634
  "source": "builtin"
14966
15635
  },
14967
15636
  {
14968
15637
  "name": "forward",
14969
15638
  "block": false,
14970
15639
  "subMacros": [],
15640
+ "interpolate": true,
14971
15641
  "source": "builtin"
14972
15642
  },
14973
15643
  {
14974
15644
  "name": "quicksave",
14975
15645
  "block": false,
14976
15646
  "subMacros": [],
15647
+ "interpolate": true,
14977
15648
  "source": "builtin"
14978
15649
  },
14979
15650
  {
14980
15651
  "name": "quickload",
14981
15652
  "block": false,
14982
15653
  "subMacros": [],
15654
+ "interpolate": true,
14983
15655
  "source": "builtin"
14984
15656
  },
14985
15657
  {
14986
15658
  "name": "restart",
14987
15659
  "block": false,
14988
15660
  "subMacros": [],
15661
+ "interpolate": true,
14989
15662
  "source": "builtin"
14990
15663
  },
14991
15664
  {
14992
15665
  "name": "saves",
14993
15666
  "block": false,
14994
15667
  "subMacros": [],
15668
+ "interpolate": true,
14995
15669
  "source": "builtin"
14996
15670
  },
14997
15671
  {
14998
15672
  "name": "settings",
14999
15673
  "block": false,
15000
15674
  "subMacros": [],
15675
+ "interpolate": true,
15001
15676
  "source": "builtin"
15002
15677
  },
15003
15678
  {
@@ -15023,12 +15698,14 @@ var init_macro_registry = __esm({
15023
15698
  "name": "save-manager",
15024
15699
  "block": false,
15025
15700
  "subMacros": [],
15701
+ "interpolate": true,
15026
15702
  "source": "builtin"
15027
15703
  },
15028
15704
  {
15029
15705
  "name": "settings-controls",
15030
15706
  "block": false,
15031
15707
  "subMacros": [],
15708
+ "interpolate": true,
15032
15709
  "source": "builtin"
15033
15710
  },
15034
15711
  {
@@ -15054,6 +15731,7 @@ var init_macro_registry = __esm({
15054
15731
  "name": "nobr",
15055
15732
  "block": true,
15056
15733
  "subMacros": [],
15734
+ "interpolate": true,
15057
15735
  "source": "builtin"
15058
15736
  },
15059
15737
  {
@@ -15088,6 +15766,7 @@ var init_macro_registry = __esm({
15088
15766
  "block": false,
15089
15767
  "subMacros": [],
15090
15768
  "storeVar": true,
15769
+ "interpolate": true,
15091
15770
  "parameters": [
15092
15771
  {
15093
15772
  "name": "variable",
@@ -15107,6 +15786,7 @@ var init_macro_registry = __esm({
15107
15786
  "block": false,
15108
15787
  "subMacros": [],
15109
15788
  "storeVar": true,
15789
+ "interpolate": true,
15110
15790
  "parameters": [
15111
15791
  {
15112
15792
  "name": "variable",
@@ -15126,6 +15806,7 @@ var init_macro_registry = __esm({
15126
15806
  "block": false,
15127
15807
  "subMacros": [],
15128
15808
  "storeVar": true,
15809
+ "interpolate": true,
15129
15810
  "parameters": [
15130
15811
  {
15131
15812
  "name": "variable",
@@ -15145,6 +15826,7 @@ var init_macro_registry = __esm({
15145
15826
  "block": false,
15146
15827
  "subMacros": [],
15147
15828
  "storeVar": true,
15829
+ "interpolate": true,
15148
15830
  "parameters": [
15149
15831
  {
15150
15832
  "name": "variable",
@@ -15164,6 +15846,7 @@ var init_macro_registry = __esm({
15164
15846
  "block": false,
15165
15847
  "subMacros": [],
15166
15848
  "storeVar": true,
15849
+ "interpolate": true,
15167
15850
  "parameters": [
15168
15851
  {
15169
15852
  "name": "variable",
@@ -15191,6 +15874,7 @@ var init_macro_registry = __esm({
15191
15874
  "option"
15192
15875
  ],
15193
15876
  "storeVar": true,
15877
+ "interpolate": true,
15194
15878
  "parameters": [
15195
15879
  {
15196
15880
  "name": "variable",
@@ -15205,6 +15889,7 @@ var init_macro_registry = __esm({
15205
15889
  "block": true,
15206
15890
  "subMacros": [],
15207
15891
  "storeVar": true,
15892
+ "interpolate": true,
15208
15893
  "parameters": [
15209
15894
  {
15210
15895
  "name": "variable",
@@ -15218,6 +15903,7 @@ var init_macro_registry = __esm({
15218
15903
  "name": "span",
15219
15904
  "block": true,
15220
15905
  "subMacros": [],
15906
+ "interpolate": true,
15221
15907
  "source": "builtin"
15222
15908
  },
15223
15909
  {
@@ -71918,7 +72604,7 @@ var init_server4 = __esm({
71918
72604
 
71919
72605
  // src/bin.ts
71920
72606
  var args = process.argv.slice(2);
71921
- var VERSION = "0.13.2";
72607
+ var VERSION = "0.13.3";
71922
72608
  var HELP = `spindle-lsp v${VERSION}
71923
72609
  Language Server Protocol server for Spindle story format
71924
72610