@rohal12/spindle-lsp 0.15.13 → 0.15.14

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
@@ -55,7 +55,7 @@ var VERSION;
55
55
  var init_version = __esm({
56
56
  "src/version.ts"() {
57
57
  "use strict";
58
- VERSION = false ? pkg.version : "0.15.13";
58
+ VERSION = false ? pkg.version : "0.15.14";
59
59
  }
60
60
  });
61
61
 
@@ -10618,27 +10618,6 @@ var init_file_inputs = __esm({
10618
10618
  });
10619
10619
 
10620
10620
  // node_modules/@rohal12/spindle/dist/pkg/story-variables.js
10621
- function checkParameterTypes(macro, parameters) {
10622
- const holdsFix = `Give a \`string\` or \`text\` parameter one of ${STRING_HOLDS.join(", ")} to hold (see docs/custom-macros.md#what-a-string-holds).`;
10623
- for (const param of parameters) {
10624
- const type = param.type;
10625
- 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.`);
10626
- const holds = param.holds;
10627
- if (holds !== void 0 && type !== "string" && type !== "text") throw parameterError(macro, param, `is of the type ${type}, which holds what it is`, holdsFix);
10628
- if (holds !== void 0 && !STRING_HOLDS.includes(holds)) throw parameterError(macro, param, `holds the unknown ${JSON.stringify(holds)}`, holdsFix);
10629
- if (param.parameters) checkParameterTypes(macro, param.parameters);
10630
- }
10631
- }
10632
- function parameterError(macro, param, problem, fix) {
10633
- return /* @__PURE__ */ new Error(`spindle: The parameter "${param.name}" of the macro {${macro}} ${problem}. ${fix}`);
10634
- }
10635
- function branchParents(name, macros) {
10636
- if (!macros) return [...companionParents.get(name) ?? []].filter((parent) => parent !== "listbox");
10637
- return macros.filter((macro) => macro.name.toLowerCase() !== "listbox" && macro.subMacros.some((sub) => sub.toLowerCase() === name.toLowerCase())).map((macro) => macro.name.toLowerCase());
10638
- }
10639
- function getMacroRegistry() {
10640
- return Array.from(metadataRegistry.values());
10641
- }
10642
10621
  function isSigil(c) {
10643
10622
  return c !== void 0 && SIGIL_CHARS.has(c);
10644
10623
  }
@@ -11111,23 +11090,29 @@ function peg$parse(input, options) {
11111
11090
  return [body ? lexeme("text", loc, { value: body }) : null, end];
11112
11091
  }
11113
11092
  function peg$f16() {
11093
+ return completionMode;
11094
+ }
11095
+ function peg$f17(body) {
11096
+ return lexeme("text", location(), { value: body });
11097
+ }
11098
+ function peg$f18() {
11114
11099
  return offset2();
11115
11100
  }
11116
- function peg$f17(value) {
11101
+ function peg$f19(value) {
11117
11102
  return lexeme("text", location(), { value });
11118
11103
  }
11119
- function peg$f18(value) {
11104
+ function peg$f20(value) {
11120
11105
  const node = lexeme("text", location(), { value });
11121
11106
  node.comment = true;
11122
11107
  return node;
11123
11108
  }
11124
- function peg$f19(bs2) {
11109
+ function peg$f21(bs2) {
11125
11110
  return bs2.length % 2 === 1;
11126
11111
  }
11127
- function peg$f20(bs2, c) {
11112
+ function peg$f22(bs2, c) {
11128
11113
  return c;
11129
11114
  }
11130
- function peg$f21(bs2, brace) {
11115
+ function peg$f23(bs2, brace) {
11131
11116
  const loc = location();
11132
11117
  const next = input.charAt(loc.end.offset);
11133
11118
  if (textMode) {
@@ -11144,48 +11129,48 @@ function peg$parse(input, options) {
11144
11129
  escaped.escape = true;
11145
11130
  return bs2.length > 1 ? [before, escaped] : escaped;
11146
11131
  }
11147
- function peg$f22() {
11132
+ function peg$f24() {
11148
11133
  return lexeme("text", location(), { value: LB });
11149
11134
  }
11150
- function peg$f23() {
11135
+ function peg$f25() {
11151
11136
  return !textMode;
11152
11137
  }
11153
- function peg$f24() {
11138
+ function peg$f26() {
11154
11139
  return lexeme("text", location(), { value: "<" });
11155
11140
  }
11156
- function peg$f25() {
11141
+ function peg$f27() {
11157
11142
  return lexeme("text", location(), { value: "[" });
11158
11143
  }
11159
- function peg$f26() {
11144
+ function peg$f28() {
11160
11145
  return !textMode;
11161
11146
  }
11162
- function peg$f27(ss2, s, at3, inner) {
11147
+ function peg$f29(ss2, s, at3, inner) {
11163
11148
  const link = lexeme("link", location(), parseLink(inner, at3, (separator) => linkSeparatorIndex(inner, at3, separator)));
11164
11149
  link.selectorSpan = selectorSpan(s, ss2, at3);
11165
11150
  return withSelectors(link, s);
11166
11151
  }
11167
- function peg$f28() {
11152
+ function peg$f30() {
11168
11153
  return !textMode;
11169
11154
  }
11170
- function peg$f29() {
11155
+ function peg$f31() {
11171
11156
  fail("Unclosed link: [[ without ]]", location(), "unclosed-link");
11172
11157
  }
11173
- function peg$f30(parts) {
11158
+ function peg$f32(parts) {
11174
11159
  return selectorsOf(parts);
11175
11160
  }
11176
- function peg$f31() {
11161
+ function peg$f33() {
11177
11162
  return NO_SELECTORS;
11178
11163
  }
11179
- function peg$f32(s, end) {
11164
+ function peg$f34(s, end) {
11180
11165
  return {
11181
11166
  ...s,
11182
11167
  end
11183
11168
  };
11184
11169
  }
11185
- function peg$f33(prefix, name) {
11170
+ function peg$f35(prefix, name) {
11186
11171
  return [prefix, name];
11187
11172
  }
11188
- function peg$f34(ss2, s, sigil, nameStart, name) {
11173
+ function peg$f36(ss2, s, sigil, nameStart, name) {
11189
11174
  const variable = lexeme("variable", location(), {
11190
11175
  name,
11191
11176
  nameStart,
@@ -11195,7 +11180,7 @@ function peg$parse(input, options) {
11195
11180
  variable.selectorSpan = selectorSpan(s, ss2, nameStart - 1);
11196
11181
  return withSelectors(variable, s);
11197
11182
  }
11198
- function peg$f35(ss2, s, after, expression) {
11183
+ function peg$f37(ss2, s, after, expression) {
11199
11184
  const node = lexeme("expression", location(), {
11200
11185
  expression,
11201
11186
  expressionStart: after,
@@ -11204,28 +11189,33 @@ function peg$parse(input, options) {
11204
11189
  node.selectorSpan = selectorSpan(s, ss2, after);
11205
11190
  return withSelectors(node, s);
11206
11191
  }
11207
- function peg$f36() {
11192
+ function peg$f38() {
11208
11193
  fail("Unclosed {" + input.substr(location().end.offset, 1) + "\u2026: no } ends it", location(), "unclosed-expression");
11209
11194
  }
11210
- function peg$f37(start) {
11211
- return skipTo(hooks.closeBrace(input, start, start));
11195
+ function peg$f39(start) {
11196
+ const end = hooks.closeBrace(input, start, start);
11197
+ return skipTo(end < 0 && completionMode ? input.length : end);
11212
11198
  }
11213
- function peg$f38(start) {
11199
+ function peg$f40(start) {
11214
11200
  return skipEnd - offset2();
11215
11201
  }
11216
- function peg$f39(start, code) {
11202
+ function peg$f41(start, code) {
11217
11203
  return code;
11218
11204
  }
11219
- function peg$f40(ss2, s, start, close, name, nameEnd) {
11220
- return skipTo(hooks.closeBrace(input, nameEnd, start));
11205
+ function peg$f42() {
11206
+ return completionMode;
11221
11207
  }
11222
- function peg$f41(ss2, s, start, close, name, nameEnd) {
11208
+ function peg$f43(ss2, s, start, close, name, nameEnd) {
11209
+ const end = hooks.closeBrace(input, nameEnd, start);
11210
+ return skipTo(end < 0 && completionMode ? input.length : end);
11211
+ }
11212
+ function peg$f44(ss2, s, start, close, name, nameEnd) {
11223
11213
  return skipEnd - offset2();
11224
11214
  }
11225
- function peg$f42(ss2, s, start, close, name, nameEnd, args2) {
11215
+ function peg$f45(ss2, s, start, close, name, nameEnd, args2) {
11226
11216
  return args2;
11227
11217
  }
11228
- function peg$f43(ss2, s, start, close, name, nameEnd, tail) {
11218
+ function peg$f46(ss2, s, start, close, name, nameEnd, tail) {
11229
11219
  const loc = location();
11230
11220
  const lower = name.toLowerCase();
11231
11221
  const tag = lexeme("macro", loc, {
@@ -11238,7 +11228,7 @@ function peg$parse(input, options) {
11238
11228
  tag.nameEnd = nameEnd;
11239
11229
  tag.selectorSpan = selectorSpan(s, ss2, start);
11240
11230
  if (tail === null) fail("Unclosed {" + (close ? "/" : "") + name + "\u2026: no } ends the macro", loc, "unclosed-macro");
11241
- tag.rawArgs = tail.trim();
11231
+ tag.rawArgs = completionMode ? tail.trimStart() : tail.trim();
11242
11232
  tag.argsStart = tag.rawArgs ? nameEnd + tail.length - tail.trimStart().length : nameEnd;
11243
11233
  tag.argsEnd = tag.argsStart + tag.rawArgs.length;
11244
11234
  if (close) {
@@ -11251,19 +11241,19 @@ function peg$parse(input, options) {
11251
11241
  }
11252
11242
  return tag;
11253
11243
  }
11254
- function peg$f44(s) {
11244
+ function peg$f47(s) {
11255
11245
  fail("A closing tag starts with a letter after {/", location(), "invalid-closer");
11256
11246
  }
11257
- function peg$f45(value) {
11247
+ function peg$f48(value) {
11258
11248
  return EMAIL_AUTOLINK.test(value);
11259
11249
  }
11260
- function peg$f46(value) {
11250
+ function peg$f49(value) {
11261
11251
  return lexeme("text", location(), { value });
11262
11252
  }
11263
- function peg$f47(value) {
11253
+ function peg$f50(value) {
11264
11254
  return lexeme("text", location(), { value });
11265
11255
  }
11266
- function peg$f48(tagStart, tag, list3, slash) {
11256
+ function peg$f51(tagStart, tag, list3, slash) {
11267
11257
  const lower = tag.toLowerCase();
11268
11258
  return lexeme("html", location(), {
11269
11259
  tag,
@@ -11276,7 +11266,7 @@ function peg$parse(input, options) {
11276
11266
  isSelfClose: !!slash || VOID_TAGS.has(lower)
11277
11267
  });
11278
11268
  }
11279
- function peg$f49(tag, bad) {
11269
+ function peg$f52(tag, bad) {
11280
11270
  const c = input.charAt(bad);
11281
11271
  if (!c) fail("Unclosed tag <" + tag + ": no > ends it (write &lt; for a < that starts no tag)", location(), "unclosed-tag");
11282
11272
  const at3 = {
@@ -11294,7 +11284,7 @@ function peg$parse(input, options) {
11294
11284
  };
11295
11285
  fail("Unexpected " + JSON.stringify(c) + " in the tag <" + tag + ">: expected an attribute, or > to end the tag", at3, "unexpected-character");
11296
11286
  }
11297
- function peg$f50(tagStart, tag) {
11287
+ function peg$f53(tagStart, tag) {
11298
11288
  return lexeme("html", location(), {
11299
11289
  tag,
11300
11290
  lower: tag.toLowerCase(),
@@ -11306,14 +11296,14 @@ function peg$parse(input, options) {
11306
11296
  isSelfClose: false
11307
11297
  });
11308
11298
  }
11309
- function peg$f51(nameStart, name) {
11299
+ function peg$f54(nameStart, name) {
11310
11300
  attrCode = hooks.isCodeAttribute(name);
11311
11301
  return true;
11312
11302
  }
11313
- function peg$f52(nameStart, name, v2) {
11303
+ function peg$f55(nameStart, name, v2) {
11314
11304
  return v2;
11315
11305
  }
11316
- function peg$f53(nameStart, name, value) {
11306
+ function peg$f56(nameStart, name, value) {
11317
11307
  const attribute = {
11318
11308
  name,
11319
11309
  value: value ? value.value : "",
@@ -11327,7 +11317,7 @@ function peg$parse(input, options) {
11327
11317
  }
11328
11318
  return attribute;
11329
11319
  }
11330
- function peg$f54(start, value, end) {
11320
+ function peg$f57(start, value, end) {
11331
11321
  return {
11332
11322
  value,
11333
11323
  start,
@@ -11335,7 +11325,7 @@ function peg$parse(input, options) {
11335
11325
  quote: '"'
11336
11326
  };
11337
11327
  }
11338
- function peg$f55(start, value, end) {
11328
+ function peg$f58(start, value, end) {
11339
11329
  return {
11340
11330
  value,
11341
11331
  start,
@@ -11343,35 +11333,36 @@ function peg$parse(input, options) {
11343
11333
  quote: "'"
11344
11334
  };
11345
11335
  }
11346
- function peg$f56(q2) {
11336
+ function peg$f59(q2) {
11347
11337
  fail("Unclosed attribute value: the " + q2 + " here has no closing " + q2, location(), "unclosed-attribute");
11348
11338
  }
11349
- function peg$f57(start, value, end) {
11339
+ function peg$f60(start, value, end) {
11350
11340
  return {
11351
11341
  value,
11352
11342
  start,
11353
11343
  end
11354
11344
  };
11355
11345
  }
11356
- function peg$f58() {
11346
+ function peg$f61() {
11357
11347
  return !attrCode;
11358
11348
  }
11359
- function peg$f59(bs2) {
11349
+ function peg$f62(bs2) {
11360
11350
  return bs2.length % 2 === 1;
11361
11351
  }
11362
- function peg$f60() {
11352
+ function peg$f63() {
11363
11353
  return !attrCode;
11364
11354
  }
11365
- function peg$f61(start, nameEnd) {
11366
- return skipTo(hooks.closeBrace(input, nameEnd, start));
11355
+ function peg$f64(start, nameEnd) {
11356
+ const end = hooks.closeBrace(input, nameEnd, start);
11357
+ return skipTo(end < 0 && completionMode ? input.length : end);
11367
11358
  }
11368
- function peg$f62(start, nameEnd) {
11359
+ function peg$f65(start, nameEnd) {
11369
11360
  return skipEnd - offset2();
11370
11361
  }
11371
- function peg$f63() {
11362
+ function peg$f66() {
11372
11363
  return !attrCode;
11373
11364
  }
11374
- function peg$f64() {
11365
+ function peg$f67() {
11375
11366
  return attrCode;
11376
11367
  }
11377
11368
  let peg$currPos = options.peg$currPos | 0;
@@ -11710,6 +11701,40 @@ function peg$parse(input, options) {
11710
11701
  peg$currPos = s0;
11711
11702
  s0 = peg$FAILED;
11712
11703
  }
11704
+ if (s0 === peg$FAILED) {
11705
+ s0 = peg$currPos;
11706
+ peg$savedPos = peg$currPos;
11707
+ s1 = peg$f16();
11708
+ if (s1) s1 = void 0;
11709
+ else s1 = peg$FAILED;
11710
+ if (s1 !== peg$FAILED) {
11711
+ s2 = peg$currPos;
11712
+ s3 = [];
11713
+ if (input.length > peg$currPos) {
11714
+ s4 = input.charAt(peg$currPos);
11715
+ peg$currPos++;
11716
+ } else {
11717
+ s4 = peg$FAILED;
11718
+ if (peg$silentFails === 0) peg$fail(peg$e0);
11719
+ }
11720
+ while (s4 !== peg$FAILED) {
11721
+ s3.push(s4);
11722
+ if (input.length > peg$currPos) {
11723
+ s4 = input.charAt(peg$currPos);
11724
+ peg$currPos++;
11725
+ } else {
11726
+ s4 = peg$FAILED;
11727
+ if (peg$silentFails === 0) peg$fail(peg$e0);
11728
+ }
11729
+ }
11730
+ s2 = input.substring(s2, peg$currPos);
11731
+ peg$savedPos = s0;
11732
+ s0 = peg$f17(s2);
11733
+ } else {
11734
+ peg$currPos = s0;
11735
+ s0 = peg$FAILED;
11736
+ }
11737
+ }
11713
11738
  return s0;
11714
11739
  }
11715
11740
  function peg$parseHere() {
@@ -11717,7 +11742,7 @@ function peg$parse(input, options) {
11717
11742
  s0 = peg$currPos;
11718
11743
  s1 = "";
11719
11744
  peg$savedPos = s0;
11720
- s1 = peg$f16();
11745
+ s1 = peg$f18();
11721
11746
  s0 = s1;
11722
11747
  return s0;
11723
11748
  }
@@ -11746,7 +11771,7 @@ function peg$parse(input, options) {
11746
11771
  else s1 = s2;
11747
11772
  if (s1 !== peg$FAILED) {
11748
11773
  peg$savedPos = s0;
11749
- s1 = peg$f17(s1);
11774
+ s1 = peg$f19(s1);
11750
11775
  }
11751
11776
  s0 = s1;
11752
11777
  return s0;
@@ -11862,6 +11887,7 @@ function peg$parse(input, options) {
11862
11887
  s6 = peg$FAILED;
11863
11888
  if (peg$silentFails === 0) peg$fail(peg$e5);
11864
11889
  }
11890
+ if (s6 === peg$FAILED) s6 = peg$parseOpenEnd();
11865
11891
  if (s6 !== peg$FAILED) {
11866
11892
  s5 = [s5, s6];
11867
11893
  s4 = s5;
@@ -11886,7 +11912,7 @@ function peg$parse(input, options) {
11886
11912
  else s1 = s2;
11887
11913
  if (s1 !== peg$FAILED) {
11888
11914
  peg$savedPos = s0;
11889
- s1 = peg$f18(s1);
11915
+ s1 = peg$f20(s1);
11890
11916
  }
11891
11917
  s0 = s1;
11892
11918
  return s0;
@@ -11919,7 +11945,7 @@ function peg$parse(input, options) {
11919
11945
  if (s1 !== peg$FAILED) {
11920
11946
  s2 = peg$currPos;
11921
11947
  peg$savedPos = peg$currPos;
11922
- s3 = peg$f19(s1);
11948
+ s3 = peg$f21(s1);
11923
11949
  if (s3) s3 = void 0;
11924
11950
  else s3 = peg$FAILED;
11925
11951
  if (s3 !== peg$FAILED) {
@@ -11931,7 +11957,7 @@ function peg$parse(input, options) {
11931
11957
  }
11932
11958
  if (s4 !== peg$FAILED) {
11933
11959
  peg$savedPos = s2;
11934
- s2 = peg$f20(s1, s4);
11960
+ s2 = peg$f22(s1, s4);
11935
11961
  } else {
11936
11962
  peg$currPos = s2;
11937
11963
  s2 = peg$FAILED;
@@ -11942,7 +11968,7 @@ function peg$parse(input, options) {
11942
11968
  }
11943
11969
  if (s2 === peg$FAILED) s2 = null;
11944
11970
  peg$savedPos = s0;
11945
- s0 = peg$f21(s1, s2);
11971
+ s0 = peg$f23(s1, s2);
11946
11972
  } else {
11947
11973
  peg$currPos = s0;
11948
11974
  s0 = peg$FAILED;
@@ -11990,7 +12016,7 @@ function peg$parse(input, options) {
11990
12016
  }
11991
12017
  if (s2 !== peg$FAILED) {
11992
12018
  peg$savedPos = s0;
11993
- s0 = peg$f22();
12019
+ s0 = peg$f24();
11994
12020
  } else {
11995
12021
  peg$currPos = s0;
11996
12022
  s0 = peg$FAILED;
@@ -12005,7 +12031,7 @@ function peg$parse(input, options) {
12005
12031
  peg$silentFails++;
12006
12032
  s2 = peg$currPos;
12007
12033
  peg$savedPos = peg$currPos;
12008
- s3 = peg$f23();
12034
+ s3 = peg$f25();
12009
12035
  if (s3) s3 = void 0;
12010
12036
  else s3 = peg$FAILED;
12011
12037
  if (s3 !== peg$FAILED) {
@@ -12037,7 +12063,7 @@ function peg$parse(input, options) {
12037
12063
  }
12038
12064
  if (s2 !== peg$FAILED) {
12039
12065
  peg$savedPos = s0;
12040
- s0 = peg$f24();
12066
+ s0 = peg$f26();
12041
12067
  } else {
12042
12068
  peg$currPos = s0;
12043
12069
  s0 = peg$FAILED;
@@ -12057,7 +12083,7 @@ function peg$parse(input, options) {
12057
12083
  }
12058
12084
  if (s1 !== peg$FAILED) {
12059
12085
  peg$savedPos = s0;
12060
- s1 = peg$f25();
12086
+ s1 = peg$f27();
12061
12087
  }
12062
12088
  s0 = s1;
12063
12089
  }
@@ -12068,7 +12094,7 @@ function peg$parse(input, options) {
12068
12094
  let s0, s1, s2, s3, s4, s5, s6, s7;
12069
12095
  s0 = peg$currPos;
12070
12096
  peg$savedPos = peg$currPos;
12071
- s1 = peg$f26();
12097
+ s1 = peg$f28();
12072
12098
  if (s1) s1 = void 0;
12073
12099
  else s1 = peg$FAILED;
12074
12100
  if (s1 !== peg$FAILED) {
@@ -12094,9 +12120,10 @@ function peg$parse(input, options) {
12094
12120
  s7 = peg$FAILED;
12095
12121
  if (peg$silentFails === 0) peg$fail(peg$e13);
12096
12122
  }
12123
+ if (s7 === peg$FAILED) s7 = peg$parseOpenEnd();
12097
12124
  if (s7 !== peg$FAILED) {
12098
12125
  peg$savedPos = s0;
12099
- s0 = peg$f27(s3, s4, s5, s6);
12126
+ s0 = peg$f29(s3, s4, s5, s6);
12100
12127
  } else {
12101
12128
  peg$currPos = s0;
12102
12129
  s0 = peg$FAILED;
@@ -12116,7 +12143,7 @@ function peg$parse(input, options) {
12116
12143
  if (s0 === peg$FAILED) {
12117
12144
  s0 = peg$currPos;
12118
12145
  peg$savedPos = peg$currPos;
12119
- s1 = peg$f28();
12146
+ s1 = peg$f30();
12120
12147
  if (s1) s1 = void 0;
12121
12148
  else s1 = peg$FAILED;
12122
12149
  if (s1 !== peg$FAILED) {
@@ -12129,7 +12156,7 @@ function peg$parse(input, options) {
12129
12156
  }
12130
12157
  if (s2 !== peg$FAILED) {
12131
12158
  peg$savedPos = s0;
12132
- s0 = peg$f29();
12159
+ s0 = peg$f31();
12133
12160
  } else {
12134
12161
  peg$currPos = s0;
12135
12162
  s0 = peg$FAILED;
@@ -12257,7 +12284,7 @@ function peg$parse(input, options) {
12257
12284
  }
12258
12285
  if (s2 === peg$FAILED) s2 = null;
12259
12286
  peg$savedPos = s0;
12260
- s0 = peg$f30(s1);
12287
+ s0 = peg$f32(s1);
12261
12288
  } else {
12262
12289
  peg$currPos = s0;
12263
12290
  s0 = peg$FAILED;
@@ -12266,7 +12293,7 @@ function peg$parse(input, options) {
12266
12293
  s0 = peg$currPos;
12267
12294
  s1 = "";
12268
12295
  peg$savedPos = s0;
12269
- s1 = peg$f31();
12296
+ s1 = peg$f33();
12270
12297
  s0 = s1;
12271
12298
  }
12272
12299
  return s0;
@@ -12296,7 +12323,7 @@ function peg$parse(input, options) {
12296
12323
  }
12297
12324
  }
12298
12325
  peg$savedPos = s0;
12299
- s0 = peg$f32(s1, s2);
12326
+ s0 = peg$f34(s1, s2);
12300
12327
  } else {
12301
12328
  peg$currPos = s0;
12302
12329
  s0 = peg$FAILED;
@@ -12322,7 +12349,7 @@ function peg$parse(input, options) {
12322
12349
  }
12323
12350
  s2 = input.substring(s2, peg$currPos);
12324
12351
  peg$savedPos = s0;
12325
- s0 = peg$f33(s1, s2);
12352
+ s0 = peg$f35(s1, s2);
12326
12353
  } else {
12327
12354
  peg$currPos = s0;
12328
12355
  s0 = peg$FAILED;
@@ -12440,16 +12467,10 @@ function peg$parse(input, options) {
12440
12467
  }
12441
12468
  }
12442
12469
  s6 = input.substring(s6, peg$currPos);
12443
- if (input.charCodeAt(peg$currPos) === 125) {
12444
- s7 = peg$c11;
12445
- peg$currPos++;
12446
- } else {
12447
- s7 = peg$FAILED;
12448
- if (peg$silentFails === 0) peg$fail(peg$e19);
12449
- }
12470
+ s7 = peg$parseBraceEnd();
12450
12471
  if (s7 !== peg$FAILED) {
12451
12472
  peg$savedPos = s0;
12452
- s0 = peg$f34(s2, s3, s4, s5, s6);
12473
+ s0 = peg$f36(s2, s3, s4, s5, s6);
12453
12474
  } else {
12454
12475
  peg$currPos = s0;
12455
12476
  s0 = peg$FAILED;
@@ -12496,16 +12517,10 @@ function peg$parse(input, options) {
12496
12517
  if (s5 !== peg$FAILED) {
12497
12518
  s6 = peg$parseCodeToBrace();
12498
12519
  if (s6 !== peg$FAILED) {
12499
- if (input.charCodeAt(peg$currPos) === 125) {
12500
- s7 = peg$c11;
12501
- peg$currPos++;
12502
- } else {
12503
- s7 = peg$FAILED;
12504
- if (peg$silentFails === 0) peg$fail(peg$e19);
12505
- }
12520
+ s7 = peg$parseBraceEnd();
12506
12521
  if (s7 !== peg$FAILED) {
12507
12522
  peg$savedPos = s0;
12508
- s0 = peg$f35(s2, s3, s4, s6);
12523
+ s0 = peg$f37(s2, s3, s4, s6);
12509
12524
  } else {
12510
12525
  peg$currPos = s0;
12511
12526
  s0 = peg$FAILED;
@@ -12553,7 +12568,7 @@ function peg$parse(input, options) {
12553
12568
  } else s3 = peg$FAILED;
12554
12569
  if (s3 !== peg$FAILED) {
12555
12570
  peg$savedPos = s0;
12556
- s0 = peg$f36();
12571
+ s0 = peg$f38();
12557
12572
  } else {
12558
12573
  peg$currPos = s0;
12559
12574
  s0 = peg$FAILED;
@@ -12575,12 +12590,12 @@ function peg$parse(input, options) {
12575
12590
  s0 = peg$currPos;
12576
12591
  s1 = peg$parseHere();
12577
12592
  peg$savedPos = peg$currPos;
12578
- s2 = peg$f37(s1);
12593
+ s2 = peg$f39(s1);
12579
12594
  if (s2) s2 = void 0;
12580
12595
  else s2 = peg$FAILED;
12581
12596
  if (s2 !== peg$FAILED) {
12582
12597
  s3 = peg$currPos;
12583
- s4 = peg$f38(s1);
12598
+ s4 = peg$f40(s1);
12584
12599
  s5 = peg$currPos;
12585
12600
  s6 = [];
12586
12601
  if (s6.length >= (s4 | 0)) s7 = peg$FAILED;
@@ -12611,7 +12626,7 @@ function peg$parse(input, options) {
12611
12626
  else s3 = s4;
12612
12627
  if (s3 !== peg$FAILED) {
12613
12628
  peg$savedPos = s0;
12614
- s0 = peg$f39(s1, s3);
12629
+ s0 = peg$f41(s1, s3);
12615
12630
  } else {
12616
12631
  peg$currPos = s0;
12617
12632
  s0 = peg$FAILED;
@@ -12622,6 +12637,54 @@ function peg$parse(input, options) {
12622
12637
  }
12623
12638
  return s0;
12624
12639
  }
12640
+ function peg$parseOpenEnd() {
12641
+ let s0, s1, s2, s3;
12642
+ s0 = peg$currPos;
12643
+ peg$savedPos = peg$currPos;
12644
+ s1 = peg$f42();
12645
+ if (s1) s1 = void 0;
12646
+ else s1 = peg$FAILED;
12647
+ if (s1 !== peg$FAILED) {
12648
+ s2 = peg$currPos;
12649
+ peg$silentFails++;
12650
+ if (input.length > peg$currPos) {
12651
+ s3 = input.charAt(peg$currPos);
12652
+ peg$currPos++;
12653
+ } else {
12654
+ s3 = peg$FAILED;
12655
+ if (peg$silentFails === 0) peg$fail(peg$e0);
12656
+ }
12657
+ peg$silentFails--;
12658
+ if (s3 === peg$FAILED) s2 = void 0;
12659
+ else {
12660
+ peg$currPos = s2;
12661
+ s2 = peg$FAILED;
12662
+ }
12663
+ if (s2 !== peg$FAILED) {
12664
+ s1 = [s1, s2];
12665
+ s0 = s1;
12666
+ } else {
12667
+ peg$currPos = s0;
12668
+ s0 = peg$FAILED;
12669
+ }
12670
+ } else {
12671
+ peg$currPos = s0;
12672
+ s0 = peg$FAILED;
12673
+ }
12674
+ return s0;
12675
+ }
12676
+ function peg$parseBraceEnd() {
12677
+ let s0;
12678
+ if (input.charCodeAt(peg$currPos) === 125) {
12679
+ s0 = peg$c11;
12680
+ peg$currPos++;
12681
+ } else {
12682
+ s0 = peg$FAILED;
12683
+ if (peg$silentFails === 0) peg$fail(peg$e19);
12684
+ }
12685
+ if (s0 === peg$FAILED) s0 = peg$parseOpenEnd();
12686
+ return s0;
12687
+ }
12625
12688
  function peg$parseMacroTag() {
12626
12689
  let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14;
12627
12690
  s0 = peg$currPos;
@@ -12650,12 +12713,12 @@ function peg$parse(input, options) {
12650
12713
  s7 = peg$parseHere();
12651
12714
  s8 = peg$currPos;
12652
12715
  peg$savedPos = peg$currPos;
12653
- s9 = peg$f40(s2, s3, s4, s5, s6, s7);
12716
+ s9 = peg$f43(s2, s3, s4, s5, s6, s7);
12654
12717
  if (s9) s9 = void 0;
12655
12718
  else s9 = peg$FAILED;
12656
12719
  if (s9 !== peg$FAILED) {
12657
12720
  s10 = peg$currPos;
12658
- s11 = peg$f41(s2, s3, s4, s5, s6, s7);
12721
+ s11 = peg$f44(s2, s3, s4, s5, s6, s7);
12659
12722
  s12 = peg$currPos;
12660
12723
  s13 = [];
12661
12724
  if (s13.length >= (s11 | 0)) s14 = peg$FAILED;
@@ -12685,16 +12748,10 @@ function peg$parse(input, options) {
12685
12748
  if (s11 !== peg$FAILED) s10 = input.substring(s10, peg$currPos);
12686
12749
  else s10 = s11;
12687
12750
  if (s10 !== peg$FAILED) {
12688
- if (input.charCodeAt(peg$currPos) === 125) {
12689
- s11 = peg$c11;
12690
- peg$currPos++;
12691
- } else {
12692
- s11 = peg$FAILED;
12693
- if (peg$silentFails === 0) peg$fail(peg$e19);
12694
- }
12751
+ s11 = peg$parseBraceEnd();
12695
12752
  if (s11 !== peg$FAILED) {
12696
12753
  peg$savedPos = s8;
12697
- s8 = peg$f42(s2, s3, s4, s5, s6, s7, s10);
12754
+ s8 = peg$f45(s2, s3, s4, s5, s6, s7, s10);
12698
12755
  } else {
12699
12756
  peg$currPos = s8;
12700
12757
  s8 = peg$FAILED;
@@ -12709,7 +12766,7 @@ function peg$parse(input, options) {
12709
12766
  }
12710
12767
  if (s8 === peg$FAILED) s8 = null;
12711
12768
  peg$savedPos = s0;
12712
- s0 = peg$f43(s2, s3, s4, s5, s6, s7, s8);
12769
+ s0 = peg$f46(s2, s3, s4, s5, s6, s7, s8);
12713
12770
  } else {
12714
12771
  peg$currPos = s0;
12715
12772
  s0 = peg$FAILED;
@@ -12743,7 +12800,7 @@ function peg$parse(input, options) {
12743
12800
  }
12744
12801
  if (s3 !== peg$FAILED) {
12745
12802
  peg$savedPos = s0;
12746
- s0 = peg$f44(s2);
12803
+ s0 = peg$f47(s2);
12747
12804
  } else {
12748
12805
  peg$currPos = s0;
12749
12806
  s0 = peg$FAILED;
@@ -12893,12 +12950,12 @@ function peg$parse(input, options) {
12893
12950
  else s1 = s2;
12894
12951
  if (s1 !== peg$FAILED) {
12895
12952
  peg$savedPos = peg$currPos;
12896
- s2 = peg$f45(s1);
12953
+ s2 = peg$f48(s1);
12897
12954
  if (s2) s2 = void 0;
12898
12955
  else s2 = peg$FAILED;
12899
12956
  if (s2 !== peg$FAILED) {
12900
12957
  peg$savedPos = s0;
12901
- s0 = peg$f46(s1);
12958
+ s0 = peg$f49(s1);
12902
12959
  } else {
12903
12960
  peg$currPos = s0;
12904
12961
  s0 = peg$FAILED;
@@ -13075,7 +13132,7 @@ function peg$parse(input, options) {
13075
13132
  else s1 = s2;
13076
13133
  if (s1 !== peg$FAILED) {
13077
13134
  peg$savedPos = s0;
13078
- s1 = peg$f47(s1);
13135
+ s1 = peg$f50(s1);
13079
13136
  }
13080
13137
  s0 = s1;
13081
13138
  return s0;
@@ -13116,9 +13173,10 @@ function peg$parse(input, options) {
13116
13173
  s7 = peg$FAILED;
13117
13174
  if (peg$silentFails === 0) peg$fail(peg$e3);
13118
13175
  }
13176
+ if (s7 === peg$FAILED) s7 = peg$parseOpenEnd();
13119
13177
  if (s7 !== peg$FAILED) {
13120
13178
  peg$savedPos = s0;
13121
- s0 = peg$f48(s2, s3, s4, s6);
13179
+ s0 = peg$f51(s2, s3, s4, s6);
13122
13180
  } else {
13123
13181
  peg$currPos = s0;
13124
13182
  s0 = peg$FAILED;
@@ -13183,7 +13241,7 @@ function peg$parse(input, options) {
13183
13241
  s5 = peg$parse_();
13184
13242
  s6 = peg$parseHere();
13185
13243
  peg$savedPos = s0;
13186
- s0 = peg$f49(s2, s6);
13244
+ s0 = peg$f52(s2, s6);
13187
13245
  } else {
13188
13246
  peg$currPos = s0;
13189
13247
  s0 = peg$FAILED;
@@ -13223,7 +13281,7 @@ function peg$parse(input, options) {
13223
13281
  }
13224
13282
  if (s5 !== peg$FAILED) {
13225
13283
  peg$savedPos = s0;
13226
- s0 = peg$f50(s2, s3);
13284
+ s0 = peg$f53(s2, s3);
13227
13285
  } else {
13228
13286
  peg$currPos = s0;
13229
13287
  s0 = peg$FAILED;
@@ -13358,7 +13416,7 @@ function peg$parse(input, options) {
13358
13416
  else s3 = s4;
13359
13417
  if (s3 !== peg$FAILED) {
13360
13418
  peg$savedPos = peg$currPos;
13361
- s4 = peg$f51(s2, s3);
13419
+ s4 = peg$f54(s2, s3);
13362
13420
  if (s4) s4 = void 0;
13363
13421
  else s4 = peg$FAILED;
13364
13422
  if (s4 !== peg$FAILED) {
@@ -13376,7 +13434,7 @@ function peg$parse(input, options) {
13376
13434
  s9 = peg$parseAttrValue();
13377
13435
  if (s9 !== peg$FAILED) {
13378
13436
  peg$savedPos = s5;
13379
- s5 = peg$f52(s2, s3, s9);
13437
+ s5 = peg$f55(s2, s3, s9);
13380
13438
  } else {
13381
13439
  peg$currPos = s5;
13382
13440
  s5 = peg$FAILED;
@@ -13387,7 +13445,7 @@ function peg$parse(input, options) {
13387
13445
  }
13388
13446
  if (s5 === peg$FAILED) s5 = null;
13389
13447
  peg$savedPos = s0;
13390
- s0 = peg$f53(s2, s3, s5);
13448
+ s0 = peg$f56(s2, s3, s5);
13391
13449
  } else {
13392
13450
  peg$currPos = s0;
13393
13451
  s0 = peg$FAILED;
@@ -13442,9 +13500,10 @@ function peg$parse(input, options) {
13442
13500
  s5 = peg$FAILED;
13443
13501
  if (peg$silentFails === 0) peg$fail(peg$e35);
13444
13502
  }
13503
+ if (s5 === peg$FAILED) s5 = peg$parseOpenEnd();
13445
13504
  if (s5 !== peg$FAILED) {
13446
13505
  peg$savedPos = s0;
13447
- s0 = peg$f54(s2, s3, s4);
13506
+ s0 = peg$f57(s2, s3, s4);
13448
13507
  } else {
13449
13508
  peg$currPos = s0;
13450
13509
  s0 = peg$FAILED;
@@ -13496,9 +13555,10 @@ function peg$parse(input, options) {
13496
13555
  s5 = peg$FAILED;
13497
13556
  if (peg$silentFails === 0) peg$fail(peg$e37);
13498
13557
  }
13558
+ if (s5 === peg$FAILED) s5 = peg$parseOpenEnd();
13499
13559
  if (s5 !== peg$FAILED) {
13500
13560
  peg$savedPos = s0;
13501
- s0 = peg$f55(s2, s3, s4);
13561
+ s0 = peg$f58(s2, s3, s4);
13502
13562
  } else {
13503
13563
  peg$currPos = s0;
13504
13564
  s0 = peg$FAILED;
@@ -13517,7 +13577,7 @@ function peg$parse(input, options) {
13517
13577
  }
13518
13578
  if (s1 !== peg$FAILED) {
13519
13579
  peg$savedPos = s0;
13520
- s1 = peg$f56(s1);
13580
+ s1 = peg$f59(s1);
13521
13581
  }
13522
13582
  s0 = s1;
13523
13583
  if (s0 === peg$FAILED) {
@@ -13543,7 +13603,7 @@ function peg$parse(input, options) {
13543
13603
  s2 = input.substring(s2, peg$currPos);
13544
13604
  s3 = peg$parseHere();
13545
13605
  peg$savedPos = s0;
13546
- s0 = peg$f57(s1, s2, s3);
13606
+ s0 = peg$f60(s1, s2, s3);
13547
13607
  }
13548
13608
  }
13549
13609
  }
@@ -13553,7 +13613,7 @@ function peg$parse(input, options) {
13553
13613
  let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
13554
13614
  s0 = peg$currPos;
13555
13615
  peg$savedPos = peg$currPos;
13556
- s1 = peg$f58();
13616
+ s1 = peg$f61();
13557
13617
  if (s1) s1 = void 0;
13558
13618
  else s1 = peg$FAILED;
13559
13619
  if (s1 !== peg$FAILED) {
@@ -13582,7 +13642,7 @@ function peg$parse(input, options) {
13582
13642
  if (s2 !== peg$FAILED) {
13583
13643
  s3 = peg$currPos;
13584
13644
  peg$savedPos = peg$currPos;
13585
- s4 = peg$f59(s2);
13645
+ s4 = peg$f62(s2);
13586
13646
  if (s4) s4 = void 0;
13587
13647
  else s4 = peg$FAILED;
13588
13648
  if (s4 !== peg$FAILED) {
@@ -13621,7 +13681,7 @@ function peg$parse(input, options) {
13621
13681
  if (s0 === peg$FAILED) {
13622
13682
  s0 = peg$currPos;
13623
13683
  peg$savedPos = peg$currPos;
13624
- s1 = peg$f60();
13684
+ s1 = peg$f63();
13625
13685
  if (s1) s1 = void 0;
13626
13686
  else s1 = peg$FAILED;
13627
13687
  if (s1 !== peg$FAILED) {
@@ -13648,12 +13708,12 @@ function peg$parse(input, options) {
13648
13708
  if (s6 !== peg$FAILED) {
13649
13709
  s7 = peg$parseHere();
13650
13710
  peg$savedPos = peg$currPos;
13651
- s8 = peg$f61(s4, s7);
13711
+ s8 = peg$f64(s4, s7);
13652
13712
  if (s8) s8 = void 0;
13653
13713
  else s8 = peg$FAILED;
13654
13714
  if (s8 !== peg$FAILED) {
13655
13715
  s9 = peg$currPos;
13656
- s10 = peg$f62(s4, s7);
13716
+ s10 = peg$f65(s4, s7);
13657
13717
  s11 = peg$currPos;
13658
13718
  s12 = [];
13659
13719
  if (s12.length >= (s10 | 0)) s13 = peg$FAILED;
@@ -13683,13 +13743,7 @@ function peg$parse(input, options) {
13683
13743
  if (s10 !== peg$FAILED) s9 = input.substring(s9, peg$currPos);
13684
13744
  else s9 = s10;
13685
13745
  if (s9 !== peg$FAILED) {
13686
- if (input.charCodeAt(peg$currPos) === 125) {
13687
- s10 = peg$c11;
13688
- peg$currPos++;
13689
- } else {
13690
- s10 = peg$FAILED;
13691
- if (peg$silentFails === 0) peg$fail(peg$e19);
13692
- }
13746
+ s10 = peg$parseBraceEnd();
13693
13747
  if (s10 !== peg$FAILED) {
13694
13748
  s1 = [
13695
13749
  s1,
@@ -13735,7 +13789,7 @@ function peg$parse(input, options) {
13735
13789
  if (s0 === peg$FAILED) {
13736
13790
  s0 = peg$currPos;
13737
13791
  peg$savedPos = peg$currPos;
13738
- s1 = peg$f63();
13792
+ s1 = peg$f66();
13739
13793
  if (s1) s1 = void 0;
13740
13794
  else s1 = peg$FAILED;
13741
13795
  if (s1 !== peg$FAILED) {
@@ -13782,13 +13836,7 @@ function peg$parse(input, options) {
13782
13836
  if (s4 !== peg$FAILED) {
13783
13837
  s5 = peg$parseCodeToBrace();
13784
13838
  if (s5 !== peg$FAILED) {
13785
- if (input.charCodeAt(peg$currPos) === 125) {
13786
- s6 = peg$c11;
13787
- peg$currPos++;
13788
- } else {
13789
- s6 = peg$FAILED;
13790
- if (peg$silentFails === 0) peg$fail(peg$e19);
13791
- }
13839
+ s6 = peg$parseBraceEnd();
13792
13840
  if (s6 !== peg$FAILED) {
13793
13841
  s1 = [
13794
13842
  s1,
@@ -13826,7 +13874,7 @@ function peg$parse(input, options) {
13826
13874
  if (s0 === peg$FAILED) {
13827
13875
  s0 = peg$currPos;
13828
13876
  peg$savedPos = peg$currPos;
13829
- s1 = peg$f64();
13877
+ s1 = peg$f67();
13830
13878
  if (s1) s1 = void 0;
13831
13879
  else s1 = peg$FAILED;
13832
13880
  if (s1 !== peg$FAILED) {
@@ -13854,13 +13902,7 @@ function peg$parse(input, options) {
13854
13902
  if (s3 !== peg$FAILED) {
13855
13903
  s4 = peg$parseCodeToBrace();
13856
13904
  if (s4 !== peg$FAILED) {
13857
- if (input.charCodeAt(peg$currPos) === 125) {
13858
- s5 = peg$c11;
13859
- peg$currPos++;
13860
- } else {
13861
- s5 = peg$FAILED;
13862
- if (peg$silentFails === 0) peg$fail(peg$e19);
13863
- }
13905
+ s5 = peg$parseBraceEnd();
13864
13906
  if (s5 !== peg$FAILED) {
13865
13907
  s1 = [
13866
13908
  s1,
@@ -13916,6 +13958,7 @@ function peg$parse(input, options) {
13916
13958
  return s0;
13917
13959
  }
13918
13960
  const textMode = options.text === true;
13961
+ const completionMode = options.completion === true;
13919
13962
  const hooks = options.hooks;
13920
13963
  let skipEnd = -1;
13921
13964
  let attrCode = false;
@@ -13986,8 +14029,8 @@ function peg$parse(input, options) {
13986
14029
  if (peg$success) return peg$result;
13987
14030
  else peg$throw();
13988
14031
  }
13989
- function isRawMacro(name) {
13990
- return RAW_BODY_MACROS.has(name);
14032
+ function isRawMacro(name, widget = WIDGETS.has(name)) {
14033
+ return RAW_BODY_MACROS.has(name.toLowerCase()) && !widget;
13991
14034
  }
13992
14035
  function isBlockMacro(name) {
13993
14036
  return BLOCK_MACROS.has(name) || DEFINED_BLOCK_MACROS.has(name) || BLOCK_WIDGETS.has(name);
@@ -14041,7 +14084,8 @@ function astNode({ token, body }) {
14041
14084
  };
14042
14085
  if (!body) return withSelectors$1(node, token);
14043
14086
  const children = buildAst(body.children);
14044
- if (!BRANCHING.has(name) && body.branches.length === 0) {
14087
+ const branching = BRANCHING.has(name) && !WIDGETS.has(name);
14088
+ if (!branching && body.branches.length === 0) {
14045
14089
  node.children = children;
14046
14090
  return withSelectors$1(node, token);
14047
14091
  }
@@ -14049,12 +14093,36 @@ function astNode({ token, body }) {
14049
14093
  rawArgs: tag.rawArgs,
14050
14094
  children: branchChildren
14051
14095
  }, tag);
14052
- if (!BRANCHING.has(name)) node.children = children;
14053
- node.branches = [...BRANCHING.has(name) ? [branch(token, children)] : [], ...body.branches.map(({ tag, children: c }) => branch(tag, buildAst(c)))];
14054
- return BRANCHING.has(name) ? node : withSelectors$1(node, token);
14096
+ if (!branching) node.children = children;
14097
+ node.branches = [...branching ? [branch(token, children)] : [], ...body.branches.map(({ tag, children: c }) => branch(tag, buildAst(c)))];
14098
+ return branching ? node : withSelectors$1(node, token);
14055
14099
  }
14056
14100
  }
14057
14101
  }
14102
+ function checkParameterTypes(macro, parameters) {
14103
+ const holdsFix = `Give a \`string\` or \`text\` parameter one of ${STRING_HOLDS.join(", ")} to hold (see docs/custom-macros.md#what-a-string-holds).`;
14104
+ for (const param of parameters) {
14105
+ const type = param.type;
14106
+ 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.`);
14107
+ const holds = param.holds;
14108
+ if (holds !== void 0 && type !== "string" && type !== "text") throw parameterError(macro, param, `is of the type ${type}, which holds what it is`, holdsFix);
14109
+ if (holds !== void 0 && !STRING_HOLDS.includes(holds)) throw parameterError(macro, param, `holds the unknown ${JSON.stringify(holds)}`, holdsFix);
14110
+ if (param.parameters) checkParameterTypes(macro, param.parameters);
14111
+ }
14112
+ }
14113
+ function parameterError(macro, param, problem, fix) {
14114
+ return /* @__PURE__ */ new Error(`spindle: The parameter "${param.name}" of the macro {${macro}} ${problem}. ${fix}`);
14115
+ }
14116
+ function branchParents(name, macros) {
14117
+ if (!macros) return [...companionParents.get(name) ?? []].filter((parent) => parent !== "listbox");
14118
+ return macros.filter((macro) => macro.name.toLowerCase() !== "listbox" && macro.subMacros.some((sub) => sub.toLowerCase() === name.toLowerCase())).map((macro) => macro.name.toLowerCase());
14119
+ }
14120
+ function getMacroParameters(name) {
14121
+ return metadataRegistry.get(name)?.parameters;
14122
+ }
14123
+ function getMacroRegistry() {
14124
+ return Array.from(metadataRegistry.values());
14125
+ }
14058
14126
  function lineColumn(text3, offset2) {
14059
14127
  let line = 1;
14060
14128
  let lineStart = 0;
@@ -14068,6 +14136,24 @@ function lineColumn(text3, offset2) {
14068
14136
  column: offset2 - lineStart + 1
14069
14137
  };
14070
14138
  }
14139
+ function sourcePositions(text3) {
14140
+ const starts = [0];
14141
+ for (let i2 = 0; i2 < text3.length; i2++) if (text3[i2] === "\n") starts.push(i2 + 1);
14142
+ return (offset2) => {
14143
+ let low = 0;
14144
+ let high = starts.length;
14145
+ while (low < high) {
14146
+ const mid = low + high >>> 1;
14147
+ if (starts[mid] <= offset2) low = mid + 1;
14148
+ else high = mid;
14149
+ }
14150
+ const line = Math.max(1, low);
14151
+ return {
14152
+ line,
14153
+ column: offset2 - starts[line - 1] + 1
14154
+ };
14155
+ };
14156
+ }
14071
14157
  function tagLabel(tag) {
14072
14158
  return tag.type === "html" ? `${tag.isClose ? "</" : "<"}${tag.tag}>` : `{${tag.isClose ? "/" : ""}${tag.name}}`;
14073
14159
  }
@@ -14081,9 +14167,11 @@ function pairMarkup(tokens2, options = {}) {
14081
14167
  };
14082
14168
  const errors = [];
14083
14169
  const noticedAtEnd = (tokens2[tokens2.length - 1]?.end ?? 0) + 1;
14170
+ let position;
14084
14171
  const where2 = (offset2) => {
14085
14172
  if (options.source === void 0) return `offset ${offset2}`;
14086
- const { line, column } = lineColumn(options.source, offset2);
14173
+ position ?? (position = sourcePositions(options.source));
14174
+ const { line, column } = position(offset2);
14087
14175
  return `line ${line}, column ${column}`;
14088
14176
  };
14089
14177
  const report2 = (code, token, message, noticedAt, data2) => {
@@ -14107,10 +14195,16 @@ function pairMarkup(tokens2, options = {}) {
14107
14195
  };
14108
14196
  const root = [];
14109
14197
  const stack = [];
14198
+ const openPositions = /* @__PURE__ */ new Map();
14199
+ const tagKey = (tag) => `${tag.type}:${lowerName(tag)}`;
14110
14200
  const add = (node) => (last$1(stack)?.into ?? root).push(node);
14111
14201
  const end = (ending, close, owns = ending === "closed") => {
14112
14202
  const { node, body } = stack.pop();
14113
14203
  const open = node.token;
14204
+ const key = tagKey(open);
14205
+ const positions = openPositions.get(key);
14206
+ positions.pop();
14207
+ if (positions.length === 0) openPositions.delete(key);
14114
14208
  const noticedAt = close ? close.end : noticedAtEnd;
14115
14209
  for (const { tag } of body.branches) if (open.type !== "macro" || !parentsOf(tag.name).includes(lowerName(open))) misplaced(tag, open, noticedAt);
14116
14210
  if (ending === "unclosed") report2("unclosed-block", open, `Unclosed ${tagLabel(open)}: no ${closerOf(open)} closes it`, noticedAt, { name: nameOf(open) });
@@ -14129,8 +14223,7 @@ function pairMarkup(tokens2, options = {}) {
14129
14223
  if (!top) report2("stray-closer", token, `${tagLabel(token)} closes nothing: no ${token.type === "html" ? `<${token.tag}>` : `{${token.name}}`} is open here`, token.start, { name: nameOf(token) });
14130
14224
  else if (closes(top.node.token, token)) end("closed", token);
14131
14225
  else {
14132
- let outer = stack.length - 1;
14133
- while (outer >= 0 && !closes(stack[outer].node.token, token)) outer--;
14226
+ const outer = last$1(openPositions.get(tagKey(token)) ?? []) ?? -1;
14134
14227
  end("mismatch", token, outer === -1);
14135
14228
  if (outer !== -1) {
14136
14229
  while (stack.length > outer + 1) end("cut", token);
@@ -14174,6 +14267,10 @@ function pairMarkup(tokens2, options = {}) {
14174
14267
  branches: []
14175
14268
  };
14176
14269
  node.body = body;
14270
+ const key = tagKey(token);
14271
+ const positions = openPositions.get(key) ?? [];
14272
+ positions.push(stack.length);
14273
+ openPositions.set(key, positions);
14177
14274
  stack.push({
14178
14275
  node,
14179
14276
  body,
@@ -15075,14 +15172,42 @@ function jsCodeEnd(onOpenEnd) {
15075
15172
  }
15076
15173
  };
15077
15174
  }
15175
+ function indexCodeEnds(input) {
15176
+ const possible = /* @__PURE__ */ new Set();
15177
+ const pending = [];
15178
+ let remaining = 0;
15179
+ let lastBrace = -1;
15180
+ for (let i2 = 0; i2 < input.length; i2++) if (input[i2] === "}") {
15181
+ remaining++;
15182
+ lastBrace = i2;
15183
+ }
15184
+ const uncertain = () => {
15185
+ for (let i2 = Math.max(0, pending.length - remaining); i2 < pending.length; i2++) possible.add(pending[i2]);
15186
+ pending.length = 0;
15187
+ };
15188
+ for (let i2 = 0; i2 < input.length; i2++) {
15189
+ const c = input[i2];
15190
+ if (c === "{") pending.push(i2);
15191
+ else if (c === "}") {
15192
+ remaining--;
15193
+ const open = pending.pop();
15194
+ if (open !== void 0) possible.add(open);
15195
+ } else if (/['"`/\\]/.test(c)) uncertain();
15196
+ }
15197
+ return (start) => {
15198
+ if (start > lastBrace) return -1;
15199
+ return possible.has(start - 1) ? defaultCodeEnd.closeBrace(input, start, start) : parsedCodeEnd(input, start, "expression");
15200
+ };
15201
+ }
15078
15202
  function isCodeAttribute(name) {
15079
15203
  const lower = name.toLowerCase();
15080
15204
  return lower.startsWith("on") || lower === "pattern" || lower === "srcdoc";
15081
15205
  }
15082
- function splitSigilTemplate(template) {
15206
+ function splitSigilTemplate(template, incomplete = false) {
15083
15207
  const parts = [];
15084
15208
  let text3 = "";
15085
15209
  let i2 = 0;
15210
+ const closeBrace = indexCodeEnds(template);
15086
15211
  while (i2 < template.length) {
15087
15212
  const brace = template.indexOf("{", i2);
15088
15213
  if (brace === -1) {
@@ -15091,7 +15216,8 @@ function splitSigilTemplate(template) {
15091
15216
  }
15092
15217
  text3 += template.slice(i2, brace);
15093
15218
  i2 = brace + 1;
15094
- const end = isSigil(template[i2]) ? defaultCodeEnd.closeBrace(template, i2, i2) : -1;
15219
+ let end = isSigil(template[i2]) ? closeBrace(i2) : -1;
15220
+ if (end === -1 && incomplete && isSigil(template[i2])) end = template.length;
15095
15221
  if (end === -1) {
15096
15222
  text3 += "{";
15097
15223
  continue;
@@ -15099,13 +15225,13 @@ function splitSigilTemplate(template) {
15099
15225
  if (text3) parts.push({ text: text3 });
15100
15226
  text3 = "";
15101
15227
  const block = template.slice(i2, end);
15102
- parts.push(/^.\w/.test(block) ? { expr: block } : { verbatim: `{${block}}` });
15228
+ parts.push(/^.\w/.test(block) || incomplete && block.length === 1 && end === template.length ? { expr: block } : { verbatim: `{${block}}` });
15103
15229
  i2 = end + 1;
15104
15230
  }
15105
15231
  if (text3) parts.push({ text: text3 });
15106
15232
  return parts;
15107
15233
  }
15108
- function run$1(source, startRule, options) {
15234
+ function run$1(source, startRule, options, completion = false) {
15109
15235
  const hooks = options.hooks ? {
15110
15236
  ...defaultHooks,
15111
15237
  ...options.hooks
@@ -15114,7 +15240,8 @@ function run$1(source, startRule, options) {
15114
15240
  return peg$parse(source, {
15115
15241
  startRule,
15116
15242
  text: options.text === true,
15117
- hooks
15243
+ hooks,
15244
+ completion
15118
15245
  });
15119
15246
  } catch (err) {
15120
15247
  const { location, code } = err;
@@ -15145,7 +15272,8 @@ function analyzeMarkup(source, options = {}) {
15145
15272
  };
15146
15273
  const inDamage = (start) => errors.some((e, i2) => start >= damage[i2] && start <= e.offset);
15147
15274
  const noticed = errors.map((e, i2) => [damage[i2] + 0.5, e]);
15148
- for (const error48 of paired.errors) if (!inDamage(error48.start)) noticed.push([error48.noticedAt, pairingError(source, error48)]);
15275
+ const position = paired.errors.length ? sourcePositions(source) : void 0;
15276
+ for (const error48 of paired.errors) if (!inDamage(error48.start)) noticed.push([error48.noticedAt, pairingError(position, error48)]);
15149
15277
  noticed.sort((a, b) => a[0] - b[0]);
15150
15278
  return {
15151
15279
  tokens: tokens2,
@@ -15158,8 +15286,8 @@ function parseMarkup(source, options = {}) {
15158
15286
  if (errors.length > 0) throw errors[0];
15159
15287
  return buildAst(nodes);
15160
15288
  }
15161
- function pairingError(source, error48) {
15162
- const { line, column } = lineColumn(source, error48.start);
15289
+ function pairingError(position, error48) {
15290
+ const { line, column } = position(error48.start);
15163
15291
  return new MarkupError(error48.message, error48.start, line, column, error48.code, error48.end, error48.data);
15164
15292
  }
15165
15293
  function tokenizeMarkup(source, options = {}) {
@@ -15179,10 +15307,11 @@ function tokenizeMarkupTolerant(source, options = {}) {
15179
15307
  errors
15180
15308
  };
15181
15309
  }
15182
- function scanMarkupTolerant(source, options = {}) {
15310
+ function scanMarkupTolerant(source, options = {}, completion = false) {
15183
15311
  const tokens2 = [];
15184
15312
  const errors = [];
15185
15313
  const damage = [];
15314
+ let position;
15186
15315
  const lastBrace = source.lastIndexOf("}");
15187
15316
  const { closeBrace } = {
15188
15317
  ...defaultHooks,
@@ -15194,10 +15323,10 @@ function scanMarkupTolerant(source, options = {}) {
15194
15323
  closeBrace: (input, codeStart, lenientStart) => base2 + Math.min(codeStart, lenientStart) > lastBrace ? -1 : closeBrace(input, codeStart, lenientStart)
15195
15324
  };
15196
15325
  try {
15197
- return tokenizeMarkup(text3, {
15326
+ return run$1(text3, "Tokens", {
15198
15327
  ...options,
15199
15328
  hooks
15200
- }).map((token) => shiftOffsets(token, base2));
15329
+ }, completion).map((token) => shiftOffsets(token, base2));
15201
15330
  } catch (error48) {
15202
15331
  if (!(error48 instanceof MarkupError)) throw error48;
15203
15332
  return error48;
@@ -15212,7 +15341,8 @@ function scanMarkupTolerant(source, options = {}) {
15212
15341
  break;
15213
15342
  }
15214
15343
  const at3 = base + result2.offset;
15215
- const { line, column } = lineColumn(source, at3);
15344
+ position ?? (position = sourcePositions(source));
15345
+ const { line, column } = position(at3);
15216
15346
  errors.push(new MarkupError(result2.reason, at3, line, column, result2.code, base + result2.end));
15217
15347
  let cut = result2.offset;
15218
15348
  let prefix = attempt(rest.slice(0, cut), base);
@@ -15239,6 +15369,9 @@ function scanMarkupTolerant(source, options = {}) {
15239
15369
  damage
15240
15370
  };
15241
15371
  }
15372
+ function completionTokens(source, options = {}) {
15373
+ return scanMarkupTolerant(source, options, true).tokens;
15374
+ }
15242
15375
  function mapOffsets(token, map2) {
15243
15376
  const moved = { ...token };
15244
15377
  for (const [key, value] of Object.entries(moved)) if (typeof value === "number" && OFFSET_FIELD.test(key)) moved[key] = map2(value);
@@ -15588,6 +15721,27 @@ function passageTarget(arg) {
15588
15721
  name
15589
15722
  };
15590
15723
  }
15724
+ function macroArgumentSpans(rawArgs, parameters) {
15725
+ const spans = /* @__PURE__ */ new Map();
15726
+ parseMacroArgs(rawArgs, parameters, void 0, spans);
15727
+ const result2 = [];
15728
+ const add = (params, path2) => {
15729
+ params.forEach((parameter, index) => {
15730
+ const parameterPath = [...path2, index];
15731
+ const span = spans.get(parameter);
15732
+ if (span) result2.push({
15733
+ parameter,
15734
+ parameterIndex: parameterPath[0],
15735
+ parameterPath,
15736
+ start: span[0],
15737
+ end: span[1]
15738
+ });
15739
+ if (parameter.parameters) add(parameter.parameters, parameterPath);
15740
+ });
15741
+ };
15742
+ add(parameters, []);
15743
+ return result2.sort((a, b) => a.start - b.start || b.end - a.end);
15744
+ }
15591
15745
  function subMacroParameters(name) {
15592
15746
  return SUB_MACRO_PARAMETERS.get(name.toLowerCase());
15593
15747
  }
@@ -15640,6 +15794,9 @@ function widgetParameters(params, holds = {}) {
15640
15794
  ...hasOwn(holds, name) ? { holds: holds[name] } : {}
15641
15795
  }));
15642
15796
  }
15797
+ function executesRawBody(name, parametersOf2) {
15798
+ return isRawMacro(name, parametersOf2.widget?.(name) !== void 0) && parametersOf2.source?.(name) !== "user";
15799
+ }
15643
15800
  function withHolds(params, holds) {
15644
15801
  return params.map((param) => {
15645
15802
  if (param.parameters) return {
@@ -15655,12 +15812,14 @@ function withHolds(params, holds) {
15655
15812
  function parameterLookup(macros) {
15656
15813
  const parameters = new NameMap();
15657
15814
  const widgets = new NameMap();
15658
- for (const { name, parameters: params, interpolate, widget } of macros) {
15815
+ const sources = new NameMap();
15816
+ for (const { name, parameters: params, interpolate, widget, source } of macros) {
15817
+ sources.set(name, source ?? "user");
15659
15818
  if (params) parameters.set(name, withHolds(params, holdsByDefault(interpolate)));
15660
- const declared = widget && widgetParameters(widget.params, widget.holds);
15661
- if (declared) widgets.set(name, declared);
15819
+ if (widget) widgets.set(name, widgetParameters(widget.params, widget.holds) ?? []);
15662
15820
  }
15663
15821
  const lookup = (name) => {
15822
+ if (widgets.has(name)) return void 0;
15664
15823
  let params = parameters.get(name);
15665
15824
  if (!params) {
15666
15825
  const sub = subMacroParameters(name);
@@ -15669,6 +15828,7 @@ function parameterLookup(macros) {
15669
15828
  return params;
15670
15829
  };
15671
15830
  if (widgets.size > 0) lookup.widget = (name) => widgets.get(name);
15831
+ lookup.source = (name) => sources.get(name);
15672
15832
  return lookup;
15673
15833
  }
15674
15834
  function codeGoal(param) {
@@ -15722,7 +15882,8 @@ function* codeAndText(src, tokens2, parametersOf2) {
15722
15882
  }
15723
15883
  else if (token.type === "macro" && !token.isClose) {
15724
15884
  const name = token.name.toLowerCase();
15725
- if (name === "do") {
15885
+ const widget = parametersOf2.widget?.(name);
15886
+ if (executesRawBody(name, parametersOf2)) {
15726
15887
  const close = closeOfTextBody(t, name);
15727
15888
  if (close < 0) continue;
15728
15889
  yield {
@@ -15736,7 +15897,7 @@ function* codeAndText(src, tokens2, parametersOf2) {
15736
15897
  continue;
15737
15898
  }
15738
15899
  const args2 = token.rawArgs;
15739
- if (PASSAGE_BODIES.has(name)) {
15900
+ if (PASSAGE_BODIES.has(name) && !widget) {
15740
15901
  const close = closeOfTextBody(t, name);
15741
15902
  if (close > t + 1) {
15742
15903
  const text3 = src.slice(token.end, tokens2[close].start);
@@ -15754,7 +15915,7 @@ function* codeAndText(src, tokens2, parametersOf2) {
15754
15915
  if (!args2) continue;
15755
15916
  const label2 = `{${token.name} ${args2}}`;
15756
15917
  const argsAt = token.argsStart;
15757
- if (CONDITION_MACROS.has(name)) {
15918
+ if (CONDITION_MACROS.has(name) && !widget) {
15758
15919
  yield {
15759
15920
  kind: "code",
15760
15921
  code: args2,
@@ -15766,15 +15927,16 @@ function* codeAndText(src, tokens2, parametersOf2) {
15766
15927
  }
15767
15928
  const params = parametersOf2(name);
15768
15929
  if (params) yield* argPieces(args2, argsAt, params, token.name, label2);
15769
- const widget = parametersOf2.widget?.(name);
15770
15930
  if (widget) yield* widgetPieces(args2, argsAt, widget, token.name, label2);
15771
15931
  }
15772
15932
  }
15773
15933
  }
15774
15934
  function* passagePiecesOf(src, tokens2, parametersOf2, options = {}) {
15775
- yield* expand(src, src, tokens2, parametersOf2, options, (i2) => i2, void 0);
15935
+ let position;
15936
+ const locate2 = (offset2) => (position ?? (position = sourcePositions(src)))(offset2);
15937
+ yield* expand(src, src, tokens2, parametersOf2, options, (i2) => i2, void 0, locate2);
15776
15938
  }
15777
- function* expand(root, src, tokens2, parametersOf2, options, toRoot, where2) {
15939
+ function* expand(root, src, tokens2, parametersOf2, options, toRoot, where2, locate2) {
15778
15940
  for (const flat of codeAndText(src, tokens2, parametersOf2)) {
15779
15941
  const piece = where2 ? nestedPiece(flat, toRoot, where2) : flat;
15780
15942
  if (flat.kind !== "text" || !flat.text.includes("{")) {
@@ -15784,17 +15946,20 @@ function* expand(root, src, tokens2, parametersOf2, options, toRoot, where2) {
15784
15946
  const toText = (i2) => toRoot(pieceOffset(flat, i2));
15785
15947
  const inner = analyzeMarkup(flat.text, {
15786
15948
  text: true,
15787
- hooks: options.isBlock && { isBlock: options.isBlock }
15949
+ hooks: {
15950
+ ...options.isBlock && { isBlock: options.isBlock },
15951
+ isRaw: (name) => isRawMacro(name, parametersOf2.widget?.(name) !== void 0)
15952
+ }
15788
15953
  });
15789
15954
  yield {
15790
15955
  ...piece,
15791
15956
  tokens: inner.tokens.map((t) => mapOffsets(t, toText)),
15792
15957
  errors: inner.errors.map((e) => {
15793
- const { line, column } = lineColumn(root, toText(e.offset));
15958
+ const { line, column } = locate2(toText(e.offset));
15794
15959
  return new MarkupError(e.reason, toText(e.offset), line, column, e.code, toText(e.end), e.data);
15795
15960
  })
15796
15961
  };
15797
- yield* expand(root, flat.text, inner.tokens, parametersOf2, options, toText, flat.where);
15962
+ yield* expand(root, flat.text, inner.tokens, parametersOf2, options, toText, flat.where, locate2);
15798
15963
  }
15799
15964
  }
15800
15965
  function nestedPiece(piece, toRoot, where2) {
@@ -16011,50 +16176,153 @@ function locate(text3, part, from) {
16011
16176
  const anywhere = text3.indexOf(part);
16012
16177
  return anywhere >= 0 ? anywhere : from;
16013
16178
  }
16014
- function textContainsChildren(text3, parametersOf2) {
16179
+ function completionContext(source, cursor, macros = []) {
16180
+ if (!Number.isInteger(cursor) || cursor < 0 || cursor > source.length) throw new RangeError("cursor must be a UTF-16 offset within source");
16181
+ const parametersOf2 = parameterLookup(macros);
16182
+ const codeContext = (code, goal, map2) => {
16183
+ const probe = code.length + 1;
16184
+ let executable = false;
16185
+ lexJs(code + " __spindle_cursor__", {
16186
+ code(_ch, at3) {
16187
+ if (at3 >= probe) executable = true;
16188
+ },
16189
+ variable(_sigil, _name, at3) {
16190
+ if (at3 >= probe) executable = true;
16191
+ }
16192
+ }, goal);
16193
+ if (!executable) return LITERAL;
16194
+ const offset2 = map2(0);
16195
+ const offsets = Array.from({ length: code.length + 1 }, (_2, i2) => map2(i2));
16196
+ return {
16197
+ kind: "code",
16198
+ code,
16199
+ goal,
16200
+ offset: offset2,
16201
+ cursor: code.length,
16202
+ ...offsets.some((at3, i2) => at3 !== offset2 + i2) ? { sourceOffsets: offsets } : {}
16203
+ };
16204
+ };
16205
+ const read = (src, map2, text3 = false, depth = 0, sigilsOnly = false) => {
16206
+ if (depth >= 64) return LITERAL;
16207
+ if (sigilsOnly) {
16208
+ let at3 = 0;
16209
+ for (const part of splitSigilTemplate(src, true)) if ("expr" in part) {
16210
+ if (at3 + 1 + part.expr.length === src.length) return codeContext(part.expr, "expression", (i2) => map2(at3 + 1 + i2));
16211
+ at3 += part.expr.length + 2;
16212
+ } else at3 += ("text" in part ? part.text : part.verbatim).length;
16213
+ return LITERAL;
16214
+ }
16215
+ const tokens2 = completionTokens(src, {
16216
+ text: text3,
16217
+ hooks: { isRaw: (name) => isRawMacro(name, parametersOf2.widget?.(name) !== void 0) }
16218
+ });
16219
+ const token = tokens2[tokens2.length - 1];
16220
+ if (!token || token.end !== src.length) return LITERAL;
16221
+ const codeAt = (code, at3, goal = "expression") => codeContext(code, goal, (i2) => map2(at3 + i2));
16222
+ const nested = (value, at3, offsets, onlySigils = false) => read(value, (i2) => map2(offsets?.[i2] ?? at3 + i2), true, depth + 1, onlySigils);
16223
+ if (token.type === "variable" && token.nameEnd === src.length) return codeAt(src.slice(token.nameStart - 1), token.nameStart - 1);
16224
+ if (token.type === "expression" && token.expressionEnd === src.length) return codeAt(token.expression, token.expressionStart);
16225
+ if (token.type === "html") {
16226
+ for (const { name, value, at: at3 } of attributeValues(src, token)) if (at3 + value.length === src.length) return nested(value, at3, void 0, isCodeAttribute(name));
16227
+ } else if (token.type === "link" && token.displayEnd === src.length) return nested(token.display, token.displayStart);
16228
+ else if (token.type === "text" && !token.comment) {
16229
+ const opener = tokens2[tokens2.length - 2];
16230
+ if (opener?.type === "macro" && !opener.isClose && executesRawBody(opener.name, parametersOf2)) return codeAt(token.value, token.start, "statements");
16231
+ } else if (token.type === "macro" && !token.isClose && token.argsEnd === src.length) {
16232
+ const name = token.name.toLowerCase();
16233
+ const args2 = token.rawArgs;
16234
+ const widget = parametersOf2.widget?.(name);
16235
+ if (!widget && [
16236
+ "if",
16237
+ "elseif",
16238
+ "case"
16239
+ ].includes(name)) return codeAt(args2, token.argsStart);
16240
+ const stringContext = (raw2, at4, holds, escapes = "quotes") => {
16241
+ const quoted = readQuoted(raw2, 0, true);
16242
+ if (!quoted) {
16243
+ if (holds === "expression" || holds === "statements") return codeAt(raw2, at4, holds);
16244
+ return holds === "markup" ? nested(raw2, at4) : LITERAL;
16245
+ }
16246
+ if (scanStringLiteral(raw2, 0).closed) return LITERAL;
16247
+ const value = escapes === "javascript" ? stringLiteralValue(raw2 + raw2[0]) : quoted.value;
16248
+ if (value == null) return LITERAL;
16249
+ const offsets = escapeOffsets(raw2.slice(1), at4 + 1, value, escapes);
16250
+ if (holds === "expression" || holds === "statements") return codeContext(value, holds, (i2) => map2(offsets?.[i2] ?? at4 + 1 + i2));
16251
+ return holds === "markup" ? nested(value, at4 + 1, offsets) : LITERAL;
16252
+ };
16253
+ if (widget) {
16254
+ const spans2 = splitArgSpans(args2);
16255
+ const span2 = spans2[spans2.length - 1];
16256
+ if (!span2) return LITERAL;
16257
+ const raw2 = args2.slice(span2[0]);
16258
+ const holds = widget[spans2.length - 1]?.holds;
16259
+ return /^["']/.test(raw2) && holds ? stringContext(raw2, token.argsStart + span2[0], holds, "javascript") : codeAt(raw2, token.argsStart + span2[0]);
16260
+ }
16261
+ const params = parametersOf2(name);
16262
+ if (!params) return LITERAL;
16263
+ const spans = macroArgumentSpans(args2, params).filter((span2) => span2.parameter.type !== "options" && span2.end === args2.trimEnd().length);
16264
+ const span = spans[spans.length - 1];
16265
+ if (!span) return LITERAL;
16266
+ const param = span.parameter;
16267
+ let at3 = span.start;
16268
+ if (span.parameterPath.length > 1) {
16269
+ at3 += param.name.length;
16270
+ while (/\s/.test(args2.charAt(at3))) at3++;
16271
+ }
16272
+ const raw = args2.slice(at3);
16273
+ const offset2 = token.argsStart + at3;
16274
+ if (param.type === "expression" || param.type === "passage") return codeAt(raw, offset2);
16275
+ if (param.type === "statements") return codeAt(raw, offset2, "statements");
16276
+ if (param.type === "string" || param.type === "text") return stringContext(raw, offset2, param.holds);
16277
+ }
16278
+ return LITERAL;
16279
+ };
16280
+ return read(source.slice(0, cursor), (i2) => i2);
16281
+ }
16282
+ function textContainsChildren(text3, parametersOf2, hooks) {
16015
16283
  if (!text3.includes("{")) return false;
16016
16284
  let nodes;
16017
16285
  try {
16018
- nodes = parseMarkup(text3, { text: true });
16286
+ nodes = parseMarkup(text3, {
16287
+ text: true,
16288
+ hooks
16289
+ });
16019
16290
  } catch {
16020
16291
  return false;
16021
16292
  }
16022
- return astContainsChildren(nodes, parametersOf2);
16293
+ return astContainsChildren(nodes, parametersOf2, hooks);
16023
16294
  }
16024
- function tokenTextContainsChildren(token, parametersOf2 = registeredParameters, src = "") {
16025
- for (const piece of codeAndText(src, [token], parametersOf2)) if (piece.kind === "text" && textContainsChildren(piece.text, parametersOf2)) return true;
16295
+ function tokenTextContainsChildren(token, parametersOf2 = registeredParameters, src = "", hooks) {
16296
+ for (const piece of codeAndText(src, [token], parametersOf2)) if (piece.kind === "text" && textContainsChildren(piece.text, parametersOf2, hooks)) return true;
16026
16297
  return false;
16027
16298
  }
16028
- function astContainsChildren(nodes, parametersOf2 = registeredParameters) {
16299
+ function astContainsChildren(nodes, parametersOf2 = registeredParameters, hooks) {
16029
16300
  for (const node of nodes) {
16030
16301
  if (node.type === "variable" && node.scope === "local" && node.name === "children") return true;
16031
- if (node.type === "html") {
16032
- if (tokenTextContainsChildren({
16033
- isClose: false,
16034
- isSelfClose: false,
16035
- tagNameStart: 0,
16036
- tagNameEnd: 0,
16037
- attributeSpans: [],
16038
- ...node,
16039
- start: 0,
16040
- end: 0
16041
- }, parametersOf2) || astContainsChildren(node.children, parametersOf2)) return true;
16042
- }
16043
- if (node.type === "macro") {
16044
- if (node.name.toLowerCase() === "widget") continue;
16045
- if (tokenTextContainsChildren({
16046
- isClose: false,
16047
- nameStart: 0,
16048
- nameEnd: 0,
16049
- argsStart: 0,
16050
- argsEnd: 0,
16051
- ...node,
16052
- start: 0,
16053
- end: 0
16054
- }, parametersOf2) || astContainsChildren(node.children, parametersOf2)) return true;
16055
- if (node.branches) {
16056
- for (const branch of node.branches) if (astContainsChildren(branch.children, parametersOf2)) return true;
16057
- }
16302
+ if (node.type !== "html" && node.type !== "macro") continue;
16303
+ if (node.type === "macro" && node.name.toLowerCase() === "widget") continue;
16304
+ const span = {
16305
+ isClose: false,
16306
+ start: 0,
16307
+ end: 0
16308
+ };
16309
+ if (tokenTextContainsChildren(node.type === "html" ? {
16310
+ ...node,
16311
+ ...span,
16312
+ isSelfClose: false,
16313
+ tagNameStart: 0,
16314
+ tagNameEnd: 0,
16315
+ attributeSpans: []
16316
+ } : {
16317
+ ...node,
16318
+ ...span,
16319
+ nameStart: 0,
16320
+ nameEnd: 0,
16321
+ argsStart: 0,
16322
+ argsEnd: 0
16323
+ }, parametersOf2, "", hooks) || astContainsChildren(node.children, parametersOf2, hooks)) return true;
16324
+ if (node.type === "macro" && node.branches) {
16325
+ for (const branch of node.branches) if (astContainsChildren(branch.children, parametersOf2, hooks)) return true;
16058
16326
  }
16059
16327
  }
16060
16328
  return false;
@@ -16092,43 +16360,49 @@ function nameSpan(rawArgs) {
16092
16360
  return (quote === '"' || quote === "'") && end - start >= 2 ? [start + 1, rawArgs[end - 1] === quote ? end - 1 : end] : [start, end];
16093
16361
  }
16094
16362
  function widgetDefinitions$1(passages, parametersOf2 = registeredParameters) {
16095
- const found = [];
16096
- for (const passage of passages) {
16097
- if (passage.name !== "StoryInit" && !passage.tags?.includes("widget")) continue;
16098
- const open = [];
16099
- const done = [];
16100
- for (const token of tokenizeMarkupTolerant(passage.content).tokens) if (token.type === "macro" && token.name.toLowerCase() === "widget") {
16101
- if (!token.isClose) open.push({
16102
- token,
16103
- isBlock: false
16104
- });
16105
- else {
16106
- const def = open.pop();
16107
- if (def) done.push({
16108
- ...def,
16109
- closeStart: token.start
16363
+ const list3 = [...passages];
16364
+ const scan = (replacesDo) => {
16365
+ const found = [];
16366
+ const hooks = { isRaw: (name) => isRawMacro(name, replacesDo || parametersOf2.widget?.(name) !== void 0) };
16367
+ for (const passage of list3) {
16368
+ if (passage.name !== "StoryInit" && !passage.tags?.includes("widget")) continue;
16369
+ const open = [];
16370
+ const done = [];
16371
+ for (const token of tokenizeMarkupTolerant(passage.content, { hooks }).tokens) if (token.type === "macro" && token.name.toLowerCase() === "widget") {
16372
+ if (!token.isClose) open.push({
16373
+ token,
16374
+ isBlock: false
16375
+ });
16376
+ else {
16377
+ const def = open.pop();
16378
+ if (def) done.push({
16379
+ ...def,
16380
+ closeStart: token.start
16381
+ });
16382
+ }
16383
+ } else if (open.length > 0 && (token.type === "variable" && token.scope === "local" && token.name === "children" || tokenTextContainsChildren(token, parametersOf2, passage.content, hooks))) open[open.length - 1].isBlock = true;
16384
+ done.push(...open);
16385
+ const defs = [];
16386
+ for (const { token, isBlock, closeStart } of done) try {
16387
+ const [from, to] = nameSpan(token.rawArgs);
16388
+ defs.push({
16389
+ ...parseWidgetDef(token.rawArgs),
16390
+ block: isBlock,
16391
+ passage: passage.name,
16392
+ start: token.start,
16393
+ end: token.end,
16394
+ nameStart: token.argsStart + from,
16395
+ nameEnd: token.argsStart + to,
16396
+ ...closeStart === void 0 ? {} : { closeStart }
16110
16397
  });
16398
+ } catch {
16111
16399
  }
16112
- } else if (open.length > 0 && (token.type === "variable" && token.scope === "local" && token.name === "children" || tokenTextContainsChildren(token, parametersOf2, passage.content))) open[open.length - 1].isBlock = true;
16113
- done.push(...open);
16114
- const defs = [];
16115
- for (const { token, isBlock, closeStart } of done) try {
16116
- const [from, to] = nameSpan(token.rawArgs);
16117
- defs.push({
16118
- ...parseWidgetDef(token.rawArgs),
16119
- block: isBlock,
16120
- passage: passage.name,
16121
- start: token.start,
16122
- end: token.end,
16123
- nameStart: token.argsStart + from,
16124
- nameEnd: token.argsStart + to,
16125
- ...closeStart === void 0 ? {} : { closeStart }
16126
- });
16127
- } catch {
16400
+ found.push(...defs.sort((a, b) => a.start - b.start));
16128
16401
  }
16129
- found.push(...defs.sort((a, b) => a.start - b.start));
16130
- }
16131
- return found;
16402
+ return found;
16403
+ };
16404
+ const initial = scan(false);
16405
+ return effectiveWidgetDefinitions(initial).some((def) => def.name.toLowerCase() === "do") ? scan(true) : initial;
16132
16406
  }
16133
16407
  function effectiveWidgetDefinitions(definitions) {
16134
16408
  const inInit = (def) => def.passage === "StoryInit";
@@ -16146,14 +16420,23 @@ function distance(a, b) {
16146
16420
  return prev[b.length];
16147
16421
  }
16148
16422
  function closestName(name, names) {
16423
+ if (name.length > 256) return void 0;
16149
16424
  const lower = name.toLowerCase();
16150
16425
  let best;
16151
16426
  let bestDistance = Math.max(2, Math.floor(lower.length / 3)) + 1;
16427
+ let budget = 1e5;
16428
+ let candidates = 1e3;
16152
16429
  for (const candidate of names) {
16430
+ if (--candidates < 0) break;
16431
+ if (candidate.length > 256) continue;
16432
+ if (Math.abs(candidate.length - lower.length) >= bestDistance) continue;
16433
+ budget -= lower.length * candidate.length;
16434
+ if (budget < 0) break;
16153
16435
  const d = distance(lower, candidate.toLowerCase());
16154
16436
  if (d < bestDistance) {
16155
16437
  best = candidate;
16156
16438
  bestDistance = d;
16439
+ if (d === 0) break;
16157
16440
  }
16158
16441
  }
16159
16442
  return best;
@@ -16167,12 +16450,19 @@ function widgetName(rawArgs) {
16167
16450
  }
16168
16451
  function validateMarkup(passages, options) {
16169
16452
  const diagnostics = [];
16453
+ const positions = /* @__PURE__ */ new Map();
16170
16454
  const hooks = {
16171
16455
  ...options.isBlockMacro && { isBlock: options.isBlockMacro },
16172
- ...options.branchParents && { branchParents: options.branchParents }
16456
+ ...options.branchParents && { branchParents: options.branchParents },
16457
+ isRaw: (name) => isRawMacro(name, options.parametersOf ? options.parametersOf.widget?.(name) !== void 0 : void 0)
16173
16458
  };
16174
16459
  const report2 = (passage, start, end, code, message, data2) => {
16175
- const { line, column } = lineColumn(passage.content, start);
16460
+ let position = positions.get(passage);
16461
+ if (!position) {
16462
+ position = sourcePositions(passage.content);
16463
+ positions.set(passage, position);
16464
+ }
16465
+ const { line, column } = position(start);
16176
16466
  const d = {
16177
16467
  passage: passage.name,
16178
16468
  line,
@@ -16217,7 +16507,7 @@ function validateMarkup(passages, options) {
16217
16507
  continue;
16218
16508
  }
16219
16509
  try {
16220
- const tokens2 = tokenizeMarkup(passage.content);
16510
+ const tokens2 = tokenizeMarkup(passage.content, { hooks });
16221
16511
  addWidgets(tokens2);
16222
16512
  if (!reported) continue;
16223
16513
  parseMarkup(passage.content, { hooks });
@@ -16319,10 +16609,14 @@ function bigintValue(digits) {
16319
16609
  return;
16320
16610
  }
16321
16611
  }
16322
- function tokensOf(text3, textMode, { tolerant } = {}) {
16323
- if (tolerant) return tokenizeMarkupTolerant(text3, { text: textMode }).tokens;
16612
+ function tokensOf(text3, textMode, { tolerant, parametersOf: parametersOf2 } = {}) {
16613
+ const options = {
16614
+ text: textMode,
16615
+ hooks: parametersOf2 && { isRaw: (name) => isRawMacro(name, parametersOf2.widget?.(name) !== void 0) }
16616
+ };
16617
+ if (tolerant) return tokenizeMarkupTolerant(text3, options).tokens;
16324
16618
  try {
16325
- return tokenizeMarkup(text3, { text: textMode });
16619
+ return tokenizeMarkup(text3, options);
16326
16620
  } catch (err) {
16327
16621
  if (err instanceof MarkupError) return [];
16328
16622
  throw err;
@@ -16344,7 +16638,7 @@ function shapeSchema(shape) {
16344
16638
  function initializerSyntaxError(expr) {
16345
16639
  const prefix = "return (";
16346
16640
  try {
16347
- parse$1(prefix + expr + ")", {
16641
+ parse$1(prefix + expr + "\n)", {
16348
16642
  ecmaVersion: "latest",
16349
16643
  allowReturnOutsideFunction: true
16350
16644
  });
@@ -16581,7 +16875,7 @@ function collectTokenRefs(content2, tokens2, at3, emit, options) {
16581
16875
  else if (token.type === "macro" && !token.isClose) {
16582
16876
  scanArgs(token, parametersOf2, at3, emit, options);
16583
16877
  scanSelectors(content2, token, at3, emit, options);
16584
- if (storeVarMacros.has(token.name.toLowerCase())) {
16878
+ if (storeVarMacros.has(token.name.toLowerCase()) && !parametersOf2.widget?.(token.name)) {
16585
16879
  const lead = token.rawArgs.length - token.rawArgs.trimStart().length;
16586
16880
  const first = token.rawArgs.trim().split(/\s+/)[0] ?? "";
16587
16881
  const quoted = QUOTED_VAR_ARG_RE.exec(first);
@@ -16597,7 +16891,7 @@ function collectTokenRefs(content2, tokens2, at3, emit, options) {
16597
16891
  });
16598
16892
  } else if (!/^["'`]/.test(first)) scanCode(first, (i2) => at3(token.argsStart + lead + i2), emit);
16599
16893
  }
16600
- if (token.name.toLowerCase() === "do") {
16894
+ if (executesRawBody(token.name, parametersOf2)) {
16601
16895
  let close = t + 1;
16602
16896
  while (close < tokens2.length) {
16603
16897
  const c = tokens2[close];
@@ -16710,6 +17004,7 @@ function macroOf(config2) {
16710
17004
  function discoverMacros(source) {
16711
17005
  const { code, regexes } = codeOnly(source);
16712
17006
  const found = [];
17007
+ const configs = /* @__PURE__ */ new Map();
16713
17008
  let bindings;
16714
17009
  for (const call of code.matchAll(CALL_RE)) {
16715
17010
  let at3 = call.index + call[0].length;
@@ -16721,12 +17016,15 @@ function discoverMacros(source) {
16721
17016
  if (object3 === void 0) continue;
16722
17017
  at3 = object3;
16723
17018
  }
16724
- const reader = new ConfigReader(source, {
16725
- goal: "statements",
16726
- regexes
16727
- });
16728
- reader.i = at3;
16729
- const config2 = reader.datum();
17019
+ if (!configs.has(at3)) {
17020
+ const reader = new ConfigReader(source, {
17021
+ goal: "statements",
17022
+ regexes
17023
+ });
17024
+ reader.i = at3;
17025
+ configs.set(at3, reader.datum());
17026
+ }
17027
+ const config2 = configs.get(at3);
16730
17028
  const macro = config2 && macroOf(config2);
16731
17029
  if (macro) found.push(macro);
16732
17030
  }
@@ -20596,18 +20894,8 @@ function markdownOptions(options) {
20596
20894
  };
20597
20895
  }
20598
20896
  function sourceLines(source) {
20599
- const starts = [0];
20600
- for (let i2 = 0; i2 < source.length; i2++) if (source[i2] === "\n") starts.push(i2 + 1);
20601
- return (offset2) => {
20602
- let low = 0;
20603
- let high = starts.length;
20604
- while (low < high) {
20605
- const middle = low + high >>> 1;
20606
- if (starts[middle] <= offset2) low = middle + 1;
20607
- else high = middle;
20608
- }
20609
- return Math.max(0, low - 1);
20610
- };
20897
+ const position = sourcePositions(source);
20898
+ return (offset2) => position(offset2).line - 1;
20611
20899
  }
20612
20900
  function markdownLines(source, isBlock, types3) {
20613
20901
  const found = /* @__PURE__ */ new Set();
@@ -20996,7 +21284,11 @@ function whitespaceSensitiveLines(source, isBlock) {
20996
21284
  function visit(nodes2) {
20997
21285
  for (const node of nodes2) {
20998
21286
  const { token, body } = node;
20999
- if (token.type === "html" && (["pre", "textarea"].includes(token.tag.toLowerCase()) || Object.entries(token.attributes).some(([name, value]) => name.toLowerCase() === "style" && /white-space\s*:/i.test(value)))) {
21287
+ if (token.type === "html" && ([
21288
+ "pre",
21289
+ "textarea",
21290
+ "svg"
21291
+ ].includes(token.tag.toLowerCase()) || Object.entries(token.attributes).some(([name, value]) => name.toLowerCase() === "style" && /white-space\s*:/i.test(value)))) {
21000
21292
  const first = lineAt(node.start);
21001
21293
  const last2 = lineAt(node.end - 1);
21002
21294
  for (let line = first; line <= last2; line++) lines.add(line);
@@ -21233,7 +21525,9 @@ function blockLookup(macros) {
21233
21525
  return (name) => blocks.has(name.toLowerCase()) || isBlockMacro(name);
21234
21526
  }
21235
21527
  function passagePieces(source, macros = []) {
21236
- return passagePiecesOfTokens(source, macros, tokenizeMarkupTolerant(source).tokens);
21528
+ const all = [...macros];
21529
+ const parametersOf2 = parameterLookup(all);
21530
+ return passagePiecesOfTokens(source, all, tokenizeMarkupTolerant(source, { hooks: { isRaw: (name) => isRawMacro(name, parametersOf2.widget?.(name) !== void 0) } }).tokens);
21237
21531
  }
21238
21532
  function passagePiecesOfTokens(source, macros, tokens2) {
21239
21533
  const all = [...macros];
@@ -21259,7 +21553,7 @@ function validateVariableReferences(passages, declarations, macros = []) {
21259
21553
  const list3 = [...passages];
21260
21554
  return checkVariableReferences(list3.filter((p) => !p.tags?.some((t) => NOT_MARKUP_TAGS.includes(t))), declarations, referenceOptions(macros, false, list3));
21261
21555
  }
21262
- var __defProp2, __exportAll, NameMap, NameSet, PARAMETER_TYPES, STRING_HOLDS, metadataRegistry, companionParents, 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, BLOCK_MACROS, BLOCK_WIDGETS, DEFINED_BLOCK_MACROS, RAW_BODY_MACROS, BRANCHING, quoteArg, last$1, BRANCH_PARENT, 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$1, 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$2, 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, 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, VOID_ELEMENTS, characterEntities, own, hasOwnProperty, asciiAlpha, asciiAlphanumeric, asciiAtext, asciiDigit, asciiHexDigit, asciiPunctuation, unicodePunctuation, unicodeWhitespace, content$1, EditMap$1, document$1, containerConstruct, attention, autolink, blankLine, blockQuote, characterEscape, characterReference, nonLazyContinuation, codeFenced, codeIndented, furtherStart, codeText, SpliceBuffer, content, continuationConstruct, definition, titleBefore, hardBreakEscape, headingAtx, htmlBlockNames, htmlRawNames, htmlFlow, blankLineBefore, nonLazyContinuationStart, htmlText, labelEnd, resourceConstruct, referenceFullConstruct, referenceCollapsedConstruct, labelStartImage, labelStartLink, lineEnding, thematicBreak, list, listItemPrefixWhitespaceConstruct, indentConstruct, setextUnderline, flow$1, resolver, string$1, text$1, constructs_exports, document, contentInitial, flowInitial, flow, string, text, insideSpan, attentionMarkers, disable, search, alignment, EditMap, DEFAULT_PRETTIER_OPTIONS, SVG_PLACEHOLDERS, SPINDLE_PLACEHOLDERS, PLACEHOLDER_PATTERN, DEFAULT_DEDENTING, WHITESPACE_AS_WRITTEN, LIST_ITEM_REGEX, CODE_FENCE_REGEX, NO_WRAP, NOT_MARKUP_TAGS;
21556
+ var __defProp2, __exportAll, 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, WIDGETS, DEFINED_BLOCK_MACROS, RAW_BODY_MACROS, BRANCHING, quoteArg, PARAMETER_TYPES, STRING_HOLDS, metadataRegistry, companionParents, last$1, BRANCH_PARENT, 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$1, 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$2, 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, SUB_MACRO_PARAMETERS, RESERVED_NAME, isArrayIndex, hasOwn, createNamespace, parses, CONDITION_MACROS, PASSAGE_BODIES, holdsByDefault, JS_ESCAPE_RE, textPiece, LITERAL, 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, VOID_ELEMENTS, characterEntities, own, hasOwnProperty, asciiAlpha, asciiAlphanumeric, asciiAtext, asciiDigit, asciiHexDigit, asciiPunctuation, unicodePunctuation, unicodeWhitespace, content$1, EditMap$1, document$1, containerConstruct, attention, autolink, blankLine, blockQuote, characterEscape, characterReference, nonLazyContinuation, codeFenced, codeIndented, furtherStart, codeText, SpliceBuffer, content, continuationConstruct, definition, titleBefore, hardBreakEscape, headingAtx, htmlBlockNames, htmlRawNames, htmlFlow, blankLineBefore, nonLazyContinuationStart, htmlText, labelEnd, resourceConstruct, referenceFullConstruct, referenceCollapsedConstruct, labelStartImage, labelStartLink, lineEnding, thematicBreak, list, listItemPrefixWhitespaceConstruct, indentConstruct, setextUnderline, flow$1, resolver, string$1, text$1, constructs_exports, document, contentInitial, flowInitial, flow, string, text, insideSpan, attentionMarkers, disable, search, alignment, EditMap, DEFAULT_PRETTIER_OPTIONS, SVG_PLACEHOLDERS, SPINDLE_PLACEHOLDERS, PLACEHOLDER_PATTERN, DEFAULT_DEDENTING, WHITESPACE_AS_WRITTEN, LIST_ITEM_REGEX, CODE_FENCE_REGEX, NO_WRAP, NOT_MARKUP_TAGS;
21263
21557
  var init_story_variables = __esm({
21264
21558
  "node_modules/@rohal12/spindle/dist/pkg/story-variables.js"() {
21265
21559
  __defProp2 = Object.defineProperty;
@@ -21272,64 +21566,6 @@ var init_story_variables = __esm({
21272
21566
  if (!no_symbols) __defProp2(target, Symbol.toStringTag, { value: "Module" });
21273
21567
  return target;
21274
21568
  };
21275
- NameMap = class extends Map {
21276
- get(name) {
21277
- return super.get(name.toLowerCase());
21278
- }
21279
- set(name, value) {
21280
- return super.set(name.toLowerCase(), value);
21281
- }
21282
- has(name) {
21283
- return super.has(name.toLowerCase());
21284
- }
21285
- delete(name) {
21286
- return super.delete(name.toLowerCase());
21287
- }
21288
- };
21289
- NameSet = class {
21290
- constructor(names = []) {
21291
- this.names = new NameMap();
21292
- for (const name of names) this.add(name);
21293
- }
21294
- add(name) {
21295
- this.names.set(name, true);
21296
- }
21297
- has(name) {
21298
- return this.names.has(name);
21299
- }
21300
- delete(name) {
21301
- this.names.delete(name);
21302
- }
21303
- clear() {
21304
- this.names.clear();
21305
- }
21306
- };
21307
- new NameMap();
21308
- new NameMap();
21309
- new NameSet();
21310
- PARAMETER_TYPES = [
21311
- "expression",
21312
- "statements",
21313
- "passage",
21314
- "variable",
21315
- "string",
21316
- "text",
21317
- "names",
21318
- "delay",
21319
- "number",
21320
- "flag",
21321
- "separator",
21322
- "options"
21323
- ];
21324
- STRING_HOLDS = [
21325
- "markup",
21326
- "text",
21327
- "passage",
21328
- "expression",
21329
- "statements"
21330
- ];
21331
- metadataRegistry = new NameMap();
21332
- companionParents = new NameMap();
21333
21569
  SIGIL_SCOPES = {
21334
21570
  $: "variable",
21335
21571
  _: "temporary",
@@ -21464,6 +21700,38 @@ var init_story_variables = __esm({
21464
21700
  return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
21465
21701
  }
21466
21702
  };
21703
+ NameMap = class extends Map {
21704
+ get(name) {
21705
+ return super.get(name.toLowerCase());
21706
+ }
21707
+ set(name, value) {
21708
+ return super.set(name.toLowerCase(), value);
21709
+ }
21710
+ has(name) {
21711
+ return super.has(name.toLowerCase());
21712
+ }
21713
+ delete(name) {
21714
+ return super.delete(name.toLowerCase());
21715
+ }
21716
+ };
21717
+ NameSet = class {
21718
+ constructor(names = []) {
21719
+ this.names = new NameMap();
21720
+ for (const name of names) this.add(name);
21721
+ }
21722
+ add(name) {
21723
+ this.names.set(name, true);
21724
+ }
21725
+ has(name) {
21726
+ return this.names.has(name);
21727
+ }
21728
+ delete(name) {
21729
+ this.names.delete(name);
21730
+ }
21731
+ clear() {
21732
+ this.names.clear();
21733
+ }
21734
+ };
21467
21735
  BLOCK_MACROS = new NameSet([
21468
21736
  "if",
21469
21737
  "for",
@@ -21481,6 +21749,7 @@ var init_story_variables = __esm({
21481
21749
  "nobr"
21482
21750
  ]);
21483
21751
  BLOCK_WIDGETS = new NameSet();
21752
+ WIDGETS = new NameSet();
21484
21753
  DEFINED_BLOCK_MACROS = new NameSet();
21485
21754
  RAW_BODY_MACROS = /* @__PURE__ */ new Set(["do"]);
21486
21755
  BRANCHING = /* @__PURE__ */ new Set([
@@ -21489,6 +21758,33 @@ var init_story_variables = __esm({
21489
21758
  "timed"
21490
21759
  ]);
21491
21760
  quoteArg = (value) => `"${value.replace(/[\\"]/g, "\\$&")}"`;
21761
+ new NameMap();
21762
+ new NameMap();
21763
+ new NameSet();
21764
+ PARAMETER_TYPES = [
21765
+ "expression",
21766
+ "statements",
21767
+ "passage",
21768
+ "variable",
21769
+ "string",
21770
+ "text",
21771
+ "names",
21772
+ "delay",
21773
+ "number",
21774
+ "flag",
21775
+ "separator",
21776
+ "options"
21777
+ ];
21778
+ STRING_HOLDS = [
21779
+ "markup",
21780
+ "text",
21781
+ "passage",
21782
+ "expression",
21783
+ "statements"
21784
+ ];
21785
+ metadataRegistry = new NameMap();
21786
+ companionParents = new NameMap();
21787
+ getMacroParameters.source = (name) => metadataRegistry.get(name)?.source;
21492
21788
  last$1 = (items) => items[items.length - 1];
21493
21789
  BRANCH_PARENT = {
21494
21790
  elseif: "if",
@@ -26744,7 +27040,9 @@ var init_story_variables = __esm({
26744
27040
  tokens: [],
26745
27041
  errors: []
26746
27042
  });
27043
+ LITERAL = { kind: "literal" };
26747
27044
  registeredParameters = (name) => parameterLookup(getMacroRegistry())(name);
27045
+ registeredParameters.source = getMacroParameters.source;
26748
27046
  WIDGET_PARAMETERS = [{
26749
27047
  name: "name",
26750
27048
  type: "text",
@@ -27073,22 +27371,40 @@ var init_story_variables = __esm({
27073
27371
  }
27074
27372
  /** Skip an array literal (or any bracketed group) at the cursor. */
27075
27373
  skipBalanced() {
27076
- const { src } = this;
27077
27374
  const close = {
27078
27375
  "[": "]",
27079
27376
  "{": "}",
27080
27377
  "(": ")"
27081
- }[src.charAt(this.i)];
27082
- if (!close) return false;
27378
+ }[this.src.charAt(this.i)];
27379
+ return close ? this.skipNested(close) : false;
27380
+ }
27381
+ /** Skip groups and template interpolations with an explicit stack. */
27382
+ skipNested(close) {
27383
+ const { src } = this;
27384
+ const stack = [close];
27083
27385
  this.i++;
27084
- while (this.i < src.length) {
27085
- if (src.charAt(this.i) === close) {
27386
+ while (this.i < src.length && stack.length) {
27387
+ const c = src.charAt(this.i);
27388
+ const top = stack[stack.length - 1];
27389
+ if (c === top) {
27390
+ stack.pop();
27086
27391
  this.i++;
27087
- return true;
27088
- }
27089
- if (!this.skipOne()) return false;
27392
+ } else if (top === "`") {
27393
+ if (c === "\\") this.i += 2;
27394
+ else if (c === "$" && src.charAt(this.i + 1) === "{") {
27395
+ stack.push("}");
27396
+ this.i += 2;
27397
+ } else this.i++;
27398
+ } else if (c === "`" || c === "[" || c === "{" || c === "(") {
27399
+ stack.push(c === "`" ? "`" : {
27400
+ "[": "]",
27401
+ "{": "}",
27402
+ "(": ")"
27403
+ }[c]);
27404
+ this.i++;
27405
+ } else if (!this.skipOne()) return false;
27090
27406
  }
27091
- return false;
27407
+ return stack.length === 0;
27092
27408
  }
27093
27409
  /** Skip one character, or one string, template, comment or group. */
27094
27410
  skipOne() {
@@ -27115,20 +27431,7 @@ var init_story_variables = __esm({
27115
27431
  }
27116
27432
  /** Skip a template literal, with the expressions in it. */
27117
27433
  skipTemplate() {
27118
- const { src } = this;
27119
- this.i++;
27120
- while (this.i < src.length) {
27121
- const c = src.charAt(this.i);
27122
- if (c === "\\") this.i += 2;
27123
- else if (c === "`") {
27124
- this.i++;
27125
- return true;
27126
- } else if (c === "$" && src.charAt(this.i + 1) === "{") {
27127
- this.i++;
27128
- if (!this.skipBalanced()) return false;
27129
- } else this.i++;
27130
- }
27131
- return false;
27434
+ return this.skipNested("`");
27132
27435
  }
27133
27436
  };
27134
27437
  VAR_PATH_RE = /([$%])(\w+(?:\.\w+)*)/y;
@@ -27197,6 +27500,8 @@ var init_story_variables = __esm({
27197
27500
  this.byOpen = /* @__PURE__ */ new Map();
27198
27501
  this.byClose = /* @__PURE__ */ new Map();
27199
27502
  this.functions = /* @__PURE__ */ new Map();
27503
+ this.functionStarts = /* @__PURE__ */ new Map();
27504
+ this.facts = /* @__PURE__ */ new Map();
27200
27505
  this.top = {
27201
27506
  start: -1,
27202
27507
  end: Infinity
@@ -27212,6 +27517,18 @@ var init_story_variables = __esm({
27212
27517
  * (given another, or one of its `properties` set) where the use can follow.
27213
27518
  */
27214
27519
  objectOf(name, at3, properties) {
27520
+ let names = this.facts.get(properties);
27521
+ if (!names) this.facts.set(properties, names = /* @__PURE__ */ new Map());
27522
+ let bindings = names.get(name);
27523
+ if (!bindings) {
27524
+ bindings = this.readBindings(name, properties);
27525
+ names.set(name, bindings);
27526
+ }
27527
+ const found = this.innermostVisible(bindings, at3);
27528
+ return found.length === 1 ? found[0].object : void 0;
27529
+ }
27530
+ /** Declaration and write facts are immutable for this source and property set. */
27531
+ readBindings(name, properties) {
27215
27532
  const { code } = this;
27216
27533
  const escaped = name.replace(/\$/g, "\\$");
27217
27534
  const bindings = [];
@@ -27221,8 +27538,7 @@ var init_story_variables = __esm({
27221
27538
  }
27222
27539
  const declarations = bindings.filter((b) => !b.write);
27223
27540
  for (const b of bindings) if (b.write) b.scope = this.innermostVisible(declarations, b.at)[0]?.scope ?? this.top;
27224
- const found = this.innermostVisible(bindings, at3);
27225
- return found.length === 1 ? found[0].object : void 0;
27541
+ return bindings;
27226
27542
  }
27227
27543
  /**
27228
27544
  * Of the `bindings` the use at `at` can see, those in the innermost scope:
@@ -27238,6 +27554,11 @@ var init_story_variables = __esm({
27238
27554
  readGroups() {
27239
27555
  const { code, groups } = this;
27240
27556
  const stack = [];
27557
+ const active = {
27558
+ "{": [],
27559
+ "(": [],
27560
+ "[": []
27561
+ };
27241
27562
  for (let i2 = 0; i2 < code.length; i2++) {
27242
27563
  const c = code.charAt(i2);
27243
27564
  if (c === "{" || c === "(" || c === "[") {
@@ -27248,15 +27569,16 @@ var init_story_variables = __esm({
27248
27569
  parent: stack[stack.length - 1]
27249
27570
  };
27250
27571
  groups.push(group);
27572
+ active[c].push(stack.length);
27251
27573
  stack.push(group);
27252
27574
  this.byOpen.set(i2, group);
27253
27575
  } else if (CLOSERS[c]) {
27254
- let k2 = stack.length - 1;
27255
- while (k2 >= 0 && stack[k2].kind !== CLOSERS[c]) k2--;
27256
- if (k2 >= 0) {
27576
+ const positions = active[CLOSERS[c]];
27577
+ const k2 = positions[positions.length - 1];
27578
+ if (k2 !== void 0) {
27257
27579
  stack[k2].close = i2;
27258
27580
  this.byClose.set(i2, stack[k2]);
27259
- stack.length = k2;
27581
+ while (stack.length > k2) active[stack.pop().kind].pop();
27260
27582
  }
27261
27583
  }
27262
27584
  }
@@ -27299,7 +27621,13 @@ var init_story_variables = __esm({
27299
27621
  }
27300
27622
  /** The start of the function that has `index` in its body, or -1. */
27301
27623
  functionStart(index) {
27302
- for (let g = this.innermost(index, "{"); g; g = g.parent) if (g.kind === "{" && this.isFunctionBody(g)) return g.open;
27624
+ const known = this.functionStarts.get(index);
27625
+ if (known !== void 0) return known;
27626
+ for (let g = this.innermost(index, "{"); g; g = g.parent) if (g.kind === "{" && this.isFunctionBody(g)) {
27627
+ this.functionStarts.set(index, g.open);
27628
+ return g.open;
27629
+ }
27630
+ this.functionStarts.set(index, -1);
27303
27631
  return -1;
27304
27632
  }
27305
27633
  scopeOf(group) {
@@ -27613,6 +27941,7 @@ var init_story_variables = __esm({
27613
27941
  constructor(..._args) {
27614
27942
  super(..._args);
27615
27943
  this.failed = false;
27944
+ this.depth = 0;
27616
27945
  }
27617
27946
  /** The static value of a member or element, whatever follows is skipped. */
27618
27947
  member() {
@@ -27680,22 +28009,31 @@ var init_story_variables = __esm({
27680
28009
  * after what it read. False if the text ends first, or cannot be read.
27681
28010
  */
27682
28011
  elements(close, read) {
27683
- this.i++;
27684
- for (; ; ) {
27685
- this.space();
27686
- const c = this.src.charAt(this.i);
27687
- if (c === close) {
27688
- this.i++;
27689
- return true;
27690
- }
27691
- if (c === ",") {
27692
- this.i++;
27693
- continue;
28012
+ if (this.depth >= 64) {
28013
+ if (!this.skipBalanced()) this.fail();
28014
+ return false;
28015
+ }
28016
+ this.depth++;
28017
+ try {
28018
+ this.i++;
28019
+ for (; ; ) {
28020
+ this.space();
28021
+ const c = this.src.charAt(this.i);
28022
+ if (c === close) {
28023
+ this.i++;
28024
+ return true;
28025
+ }
28026
+ if (c === ",") {
28027
+ this.i++;
28028
+ continue;
28029
+ }
28030
+ if (this.failed || this.i >= this.src.length) return false;
28031
+ const before = this.i;
28032
+ read();
28033
+ if (this.i === before) this.fail();
27694
28034
  }
27695
- if (this.failed || this.i >= this.src.length) return false;
27696
- const before = this.i;
27697
- read();
27698
- if (this.i === before) this.fail();
28035
+ } finally {
28036
+ this.depth--;
27699
28037
  }
27700
28038
  }
27701
28039
  array() {
@@ -30509,7 +30847,7 @@ var init_story_variables = __esm({
30509
30847
  "case",
30510
30848
  "default"
30511
30849
  ]);
30512
- WHITESPACE_AS_WRITTEN = /<(?:pre|textarea)[\s>]|white-space\s*:/i;
30850
+ WHITESPACE_AS_WRITTEN = /<(?:pre|textarea|svg)[\s>]|white-space\s*:/i;
30513
30851
  LIST_ITEM_REGEX = /^(?:[-+*]|\d{1,9}[.)])(?:[ \t]|$)/;
30514
30852
  CODE_FENCE_REGEX = /^(?:`{3,}|~{3,})/;
30515
30853
  NO_WRAP = /* @__PURE__ */ new Set([
@@ -32338,6 +32676,7 @@ var init_macro_registry2 = __esm({
32338
32676
  toolingMacros() {
32339
32677
  return this.getAllMacros().map((m) => ({
32340
32678
  name: m.name,
32679
+ source: m.source,
32341
32680
  block: m.block,
32342
32681
  subMacros: m.subMacros,
32343
32682
  storeVar: m.storeVar,
@@ -32547,6 +32886,10 @@ var init_passage_markup = __esm({
32547
32886
  get pieces() {
32548
32887
  return this.cachedPieces ??= this.isMarkup ? passagePieces(this.content, this.doc.context.macros) : [];
32549
32888
  }
32889
+ /** Whether the cursor is executable, with decoded code mapped back to `content`. */
32890
+ completionAt(offset2) {
32891
+ return this.isMarkup ? completionContext(this.content, offset2, this.doc.context.macros) : { kind: "literal" };
32892
+ }
32550
32893
  /** The declarations of a StoryVariables / StoryTransients passage. */
32551
32894
  get declarations() {
32552
32895
  return this.cachedDeclarations ??= parseDeclarations(this.content, this.passage.name === "StoryTransients" ? "%" : "$");
@@ -32964,6 +33307,10 @@ var init_widget_registry = __esm({
32964
33307
  const key = name.toLowerCase();
32965
33308
  return this.widgets.get(key) ?? this.incomplete.get(key);
32966
33309
  }
33310
+ /** Only a registered, closed widget can override a runtime macro. */
33311
+ getEffectiveWidget(name) {
33312
+ return this.widgets.get(name.toLowerCase());
33313
+ }
32967
33314
  /** Get the effective registered definitions, excluding incomplete editor declarations. */
32968
33315
  getAllWidgets() {
32969
33316
  return Array.from(this.widgets.values());
@@ -33659,8 +34006,8 @@ var init_macro_supplements = __esm({
33659
34006
 
33660
34007
  // src/core/workspace/workspace-model.ts
33661
34008
  import { EventEmitter as EventEmitter2 } from "node:events";
33662
- function storyInitMacros(content2) {
33663
- return passagePieces(content2, builtinMacros).flatMap((piece) => piece.kind === "code" && piece.goal === "statements" ? discoverMacros(piece.code) : []);
34009
+ function storyInitMacros(passage) {
34010
+ return passage.pieces.flatMap((piece) => piece.kind === "code" && piece.goal === "statements" ? discoverMacros(piece.code) : []);
33664
34011
  }
33665
34012
  var WorkspaceModel;
33666
34013
  var init_workspace_model = __esm({
@@ -33787,11 +34134,15 @@ var init_workspace_model = __esm({
33787
34134
  /**
33788
34135
  * Whether `{name}` opens a container that needs a `{/name}` closing tag:
33789
34136
  * a block macro, or a block widget (one whose body renders `{@children}`).
33790
- * Like Spindle's set of block macros, either source makes a name a block.
34137
+ * A same-name block macro retains its syntax when a widget owns the call.
33791
34138
  */
33792
34139
  isContainer(name) {
33793
34140
  return this.macros.isBlock(name) || (this.widgets.getWidget(name)?.block ?? false);
33794
34141
  }
34142
+ /** Runtime macro metadata, after effective widgets have claimed their names. */
34143
+ getEffectiveMacro(name) {
34144
+ return this.widgets.getEffectiveWidget(name) ? void 0 : this.macros.getMacro(name);
34145
+ }
33795
34146
  /**
33796
34147
  * Whether document `uri` declares any passage. A Twee document with no
33797
34148
  * `::` header holds no passage (the compiler ignores text outside one), so
@@ -33915,10 +34266,17 @@ var init_workspace_model = __esm({
33915
34266
  if (!isStoryInit && !isScript) continue;
33916
34267
  const content2 = this.markup.get(passage.uri)?.passages.find((p) => p.passage === passage)?.content ?? "";
33917
34268
  if (isScript) found.push(...discoverMacros(content2));
33918
- else init.push(...storyInitMacros(content2));
34269
+ else init.push(passage);
33919
34270
  }
33920
34271
  }
33921
- this.macros.setDiscoveredMacros([...found, ...init]);
34272
+ this.macros.setDiscoveredMacros(found);
34273
+ if (init.length === 0) return;
34274
+ this.rescanWidgets();
34275
+ for (const passage of init) {
34276
+ const markup = this.markup.get(passage.uri)?.passages.find((p) => p.passage === passage);
34277
+ if (markup) found.push(...storyInitMacros(markup));
34278
+ }
34279
+ this.macros.setDiscoveredMacros(found);
33922
34280
  }
33923
34281
  /**
33924
34282
  * Cascade: rescan variables and widgets based on current passages.
@@ -33943,12 +34301,26 @@ var init_workspace_model = __esm({
33943
34301
  const transients = declarations("StoryTransients");
33944
34302
  if (transients) this.variables.parseStoryTransients(transients.content, transients.passage.headerEnd.end.line + 1, transients.doc.uri);
33945
34303
  else this.variables.clearStoryTransients();
33946
- const readDocuments = () => this.documents.getUris().flatMap((uri) => {
34304
+ this.rescanWidgets();
34305
+ const effective = new Set(this.passages.getEffectivePassages());
34306
+ this.widgets.clearInvocations();
34307
+ for (const doc of this.readDocuments()) {
34308
+ const passages = doc.passages.filter((p) => effective.has(p.passage));
34309
+ this.variables.scanDocument(doc.uri, doc.text, { passages });
34310
+ this.widgets.recordInvocations(doc.uri, passages);
34311
+ }
34312
+ }
34313
+ /** The story documents' markup; JavaScript/TypeScript sources hold no passages. */
34314
+ readDocuments() {
34315
+ return this.documents.getUris().flatMap((uri) => {
33947
34316
  const markup = isMacroSource(uri) ? void 0 : this.markup.get(uri);
33948
34317
  return markup ? [markup] : [];
33949
34318
  });
34319
+ }
34320
+ /** Refresh widget registration before executable analysis reads its metadata. */
34321
+ rescanWidgets() {
33950
34322
  const markups = /* @__PURE__ */ new Map();
33951
- for (const doc of readDocuments()) for (const passage of doc.passages) markups.set(passage.passage, passage);
34323
+ for (const doc of this.readDocuments()) for (const passage of doc.passages) markups.set(passage.passage, passage);
33952
34324
  const effective = new Set(this.passages.getEffectivePassages());
33953
34325
  this.widgets.scan(
33954
34326
  [...effective].flatMap((passage) => markups.get(passage) ?? []),
@@ -33956,17 +34328,11 @@ var init_workspace_model = __esm({
33956
34328
  [...markups].filter(([passage]) => !effective.has(passage)).map(([, markup]) => markup)
33957
34329
  );
33958
34330
  this.markup.invalidate();
33959
- this.widgets.clearInvocations();
33960
- for (const doc of readDocuments()) {
33961
- const passages = doc.passages.filter((p) => effective.has(p.passage));
33962
- this.variables.scanDocument(doc.uri, doc.text, { passages });
33963
- this.widgets.recordInvocations(doc.uri, passages);
33964
- }
33965
34331
  }
33966
- /** All runtime metadata, with real macros taking precedence over widgets. */
34332
+ /** All runtime metadata, with effective widgets taking precedence over macros. */
33967
34333
  toolingMacros() {
33968
- const macros = this.macros.toolingMacros();
33969
- return [...macros, ...this.widgets.getAllWidgets().filter((widget) => !this.macros.getMacro(widget.name)).map((widget) => ({ name: widget.name, block: widget.block, subMacros: [], widget: widget.tooling }))];
34334
+ const macros = this.macros.toolingMacros().filter((macro) => !this.widgets.getEffectiveWidget(macro.name));
34335
+ return [...macros, ...this.widgets.getAllWidgets().map((widget) => ({ name: widget.name, block: widget.block || this.macros.isBlock(widget.name), subMacros: [], widget: widget.tooling }))];
33970
34336
  }
33971
34337
  /** Coalesce analysis and diagnostics across a burst of document changes. */
33972
34338
  scheduleModelReady() {
@@ -34377,7 +34743,7 @@ function storyReport(workspace) {
34377
34743
  function buildReport(documents, workspace) {
34378
34744
  const toolingMacros = documents[0]?.context.macros ?? workspace.macros.toolingMacros();
34379
34745
  const macros = toolingMacros.map((macro) => {
34380
- const storeVar = workspace.macros.getMacro(macro.name)?.storeVar;
34746
+ const storeVar = workspace.getEffectiveMacro(macro.name)?.storeVar;
34381
34747
  return storeVar ? { ...macro, storeVar } : macro;
34382
34748
  });
34383
34749
  const markups = /* @__PURE__ */ new Map();
@@ -34388,7 +34754,7 @@ function buildReport(documents, workspace) {
34388
34754
  }
34389
34755
  const passages = workspace.passages.getEffectivePassages().filter(isRuntimePassage).flatMap((passage) => markups.get(passage) ?? []);
34390
34756
  const markup = /* @__PURE__ */ new Map();
34391
- const validationMacros = toolingMacros.map((macro) => workspace.macros.hasConfiguredParameters(macro.name) ? { ...macro, widget: void 0 } : macro);
34757
+ const validationMacros = toolingMacros.map((macro) => workspace.macros.hasConfiguredParameters(macro.name) && !workspace.widgets.getEffectiveWidget(macro.name) ? { ...macro, widget: void 0 } : macro);
34392
34758
  const found = validateStoryMarkup(
34393
34759
  passages.map((passage, index) => ({
34394
34760
  name: passage.passage.name,
@@ -34589,7 +34955,7 @@ function structureDiagnostics(passage, workspace) {
34589
34955
  }
34590
34956
  function parentDiagnostics(passage, token, ancestors, workspace) {
34591
34957
  const name = token.name.toLowerCase();
34592
- const parents = workspace.macros.getMacro(name)?.parents;
34958
+ const parents = workspace.getEffectiveMacro(name)?.parents;
34593
34959
  if (!parents || parents.length === 0 || BRANCH_MACROS2.has(name)) return [];
34594
34960
  if (workspace.macros.getAllMacros().some((macro) => macro.source === "user" && macro.name.toLowerCase() !== "listbox" && macro.subMacros.some((sub) => sub.toLowerCase() === name))) return [];
34595
34961
  const allowed = new Set(parents.map((parent) => parent.toLowerCase()));
@@ -34605,7 +34971,7 @@ function parentDiagnostics(passage, token, ancestors, workspace) {
34605
34971
  return [makeDiag(range2, DiagnosticCode.InvalidChildren, `Invalid: {${token.name}} can only be inside {${parentList}}`)];
34606
34972
  }
34607
34973
  function childDiagnostics(passage, node, token, workspace) {
34608
- const constraints = workspace.macros.getMacro(token.name)?.children;
34974
+ const constraints = workspace.getEffectiveMacro(token.name)?.children;
34609
34975
  if (!constraints || constraints.length === 0 || !node.body?.close) return [];
34610
34976
  const direct = [...node.body.children];
34611
34977
  for (const branch of node.body.branches) direct.push({ token: branch.tag, start: branch.tag.start, end: branch.tag.end }, ...branch.children);
@@ -34613,7 +34979,7 @@ function childDiagnostics(passage, node, token, workspace) {
34613
34979
  for (const child of direct) {
34614
34980
  if (child.token.type !== "macro" || child.token.isClose) continue;
34615
34981
  const key = child.token.name.toLowerCase();
34616
- if (workspace.macros.getMacro(key)) counts[key] = (counts[key] ?? 0) + 1;
34982
+ if (workspace.getEffectiveMacro(key)) counts[key] = (counts[key] ?? 0) + 1;
34617
34983
  }
34618
34984
  const diagnostics = [];
34619
34985
  const range2 = passage.range(token.start, token.end);
@@ -34696,11 +35062,11 @@ function statementEnd(code, from) {
34696
35062
  }
34697
35063
  function loopDiagnostics(passage, token, ancestors, workspace) {
34698
35064
  const macro = token.name.toLowerCase();
34699
- if (macro !== "computed" && macro !== "set" || !token.rawArgs || workspace.macros.getMacro(macro)?.source !== "builtin") return [];
35065
+ if (macro !== "computed" && macro !== "set" || !token.rawArgs || workspace.getEffectiveMacro(macro)?.source !== "builtin") return [];
34700
35066
  let loop;
34701
35067
  for (let i2 = ancestors.length - 1; i2 >= 0; i2--) {
34702
35068
  const ancestor = ancestors[i2];
34703
- if (ancestor.element || workspace.macros.getMacro(ancestor.name)?.source !== "builtin") continue;
35069
+ if (ancestor.element || workspace.getEffectiveMacro(ancestor.name)?.source !== "builtin") continue;
34704
35070
  if (DEFERRED_CONTAINERS.has(ancestor.name)) return [];
34705
35071
  if (ancestor.name === "for") {
34706
35072
  loop = ancestor;
@@ -34744,7 +35110,7 @@ function argumentDiagnostics(passage, workspace) {
34744
35110
  const passageNames = workspace.passages.getEffectivePassages().filter(isRuntimePassage).map((candidate) => candidate.name);
34745
35111
  for (const token of passage.isMarkup ? macroTokens(passage) : []) {
34746
35112
  if (token.type !== "macro" || token.isClose) continue;
34747
- const info = workspace.macros.getMacro(token.name);
35113
+ const info = workspace.getEffectiveMacro(token.name);
34748
35114
  if (!info?.parameters || info.skipArgs || info.parameterDefs?.length) continue;
34749
35115
  if (SPINDLE_MACROS.has(token.name.toLowerCase()) && !workspace.macros.hasDiscoveredMacro(token.name)) continue;
34750
35116
  let params;
@@ -34779,7 +35145,7 @@ function widgetDiagnostics(passage, story, workspace) {
34779
35145
  const macros = [...macroTokens(passage)];
34780
35146
  for (const token of macros) {
34781
35147
  if (token.isClose) continue;
34782
- if (workspace.macros.getMacro(token.name)) continue;
35148
+ if (workspace.getEffectiveMacro(token.name)) continue;
34783
35149
  const widget = story.widgets.get(token.name.toLowerCase());
34784
35150
  if (!widget) continue;
34785
35151
  const got = token.rawArgs ? splitArgs(token.rawArgs).length : 0;
@@ -79851,7 +80217,6 @@ function getCompletions(uri, position, triggerChar, workspace) {
79851
80217
  const cursor = markupAt(workspace, uri, position);
79852
80218
  if (!cursor?.passage.isMarkup) return [];
79853
80219
  const { passage, content: content2, at: at3 } = cursor;
79854
- const before = cursor.lineBefore;
79855
80220
  const editing = (items, start, end) => {
79856
80221
  const range2 = passage.range(start, end);
79857
80222
  return items.map((item) => ({ ...item, textEdit: { range: range2, newText: item.insertText ?? item.label } }));
@@ -79865,6 +80230,11 @@ function getCompletions(uri, position, triggerChar, workspace) {
79865
80230
  const target = cursor.linkTarget();
79866
80231
  if (target) return editing(getPassageNameCompletions(workspace), target.start, target.end);
79867
80232
  if (head && !head.typed.startsWith("_")) return typedEdit(getMacroCompletions(workspace), head.typed.length, /^[\w-]*/);
80233
+ const context = passage.completionAt(at3);
80234
+ if (context.kind !== "code") return [];
80235
+ const before = context.code.slice(0, context.cursor);
80236
+ const codeOffset = (index) => pieceOffset(context, index);
80237
+ const variableEdit = (items, typed) => editing(items, codeOffset(context.cursor - typed), at3 + /^[\w$]*/.exec(content2.slice(at3))[0].length);
79868
80238
  const dotPath = /([$%])([\w$]+)\.([A-Za-z_$][\w$]*)?$/.exec(before);
79869
80239
  if (dotPath) {
79870
80240
  const sigil = dotPath[1];
@@ -79872,7 +80242,7 @@ function getCompletions(uri, position, triggerChar, workspace) {
79872
80242
  const end = at3 + /^[\w$]*/.exec(content2.slice(at3))[0].length;
79873
80243
  return getDotPathCompletions(sigil, dotPath[2], workspace).map((item) => {
79874
80244
  const identifier = /^[A-Za-z_$][\w$]*$/.test(item.label);
79875
- const start = at3 - typed - (identifier ? 0 : 1);
80245
+ const start = codeOffset(context.cursor - typed - (identifier ? 0 : 1));
79876
80246
  return {
79877
80247
  ...item,
79878
80248
  textEdit: {
@@ -79884,19 +80254,19 @@ function getCompletions(uri, position, triggerChar, workspace) {
79884
80254
  }
79885
80255
  const storyVar = /\$([A-Za-z_$]?[\w$]*)$/.exec(before);
79886
80256
  if (storyVar) {
79887
- return typedEdit(getVariableCompletions("$", workspace), storyVar[1].length);
80257
+ return variableEdit(getVariableCompletions("$", workspace), storyVar[1].length);
79888
80258
  }
79889
80259
  const tempVar = /_([A-Za-z_$]?[\w$]*)$/.exec(before);
79890
80260
  if (tempVar) {
79891
- return typedEdit(getDocumentVariableCompletions("_", cursor, workspace), tempVar[1].length);
80261
+ return variableEdit(getDocumentVariableCompletions("_", cursor, workspace), tempVar[1].length);
79892
80262
  }
79893
80263
  const localVar = /@([A-Za-z_$]?[\w$]*)$/.exec(before);
79894
80264
  if (localVar) {
79895
- return typedEdit(getDocumentVariableCompletions("@", cursor, workspace), localVar[1].length);
80265
+ return variableEdit(getDocumentVariableCompletions("@", cursor, workspace), localVar[1].length);
79896
80266
  }
79897
80267
  const transientVar = /%([A-Za-z_$]?[\w$]*)$/.exec(before);
79898
80268
  if (transientVar) {
79899
- return typedEdit(getVariableCompletions("%", workspace), transientVar[1].length);
80269
+ return variableEdit(getVariableCompletions("%", workspace), transientVar[1].length);
79900
80270
  }
79901
80271
  return [];
79902
80272
  }
@@ -80035,7 +80405,7 @@ function getMacroHover(passage, at3, workspace) {
80035
80405
  for (const macro of macroTokens(passage)) {
80036
80406
  if (at3 < macro.nameStart || at3 > macro.nameEnd) continue;
80037
80407
  const range2 = passage.range(macro.nameStart, macro.nameEnd);
80038
- const info = workspace.macros.getMacro(macro.name);
80408
+ const info = workspace.getEffectiveMacro(macro.name);
80039
80409
  if (info) {
80040
80410
  const parts = [];
80041
80411
  parts.push(`**${info.name}** _(${info.block ? "container" : "inline"} macro)_`);
@@ -80182,7 +80552,7 @@ function getSignatureHelp(uri, position, workspace) {
80182
80552
  const { name: macroName, argsBefore } = enclosing;
80183
80553
  const typing = argumentsBeingTyped(argsBefore);
80184
80554
  const argument = typing.length - 1;
80185
- const macroInfo = workspace.macros.getMacro(macroName);
80555
+ const macroInfo = workspace.getEffectiveMacro(macroName);
80186
80556
  const schema = macroInfo?.parameterDefs?.length && !workspace.macros.hasConfiguredParameters(macroName) ? void 0 : macroInfo?.parameters;
80187
80557
  if (macroInfo && schema && schema.length > 0) {
80188
80558
  const described = describeSignatures(
@@ -80372,7 +80742,7 @@ function getWidgetDefinition(uri, position, workspace) {
80372
80742
  const doc = workspace.markup.get(uri);
80373
80743
  const head = doc && macroHeadAt(doc, position);
80374
80744
  if (!head) return null;
80375
- if (workspace.macros.getMacro(head.name)) return null;
80745
+ if (workspace.getEffectiveMacro(head.name)) return null;
80376
80746
  const widget = workspace.widgets.getWidget(head.name);
80377
80747
  return widget ? { uri: widget.uri, range: widget.range } : null;
80378
80748
  }
@@ -80435,7 +80805,7 @@ function findReferences(uri, position, workspace, includeDeclaration) {
80435
80805
  const head = doc && macroHeadAt(doc, position);
80436
80806
  if (head) {
80437
80807
  const widget = workspace.widgets.getWidget(head.name);
80438
- if (!workspace.macros.getMacro(head.name) && widget && (!head.closing || widget.block)) {
80808
+ if (!workspace.getEffectiveMacro(head.name) && widget && (!head.closing || workspace.isContainer(head.name))) {
80439
80809
  return findWidgetReferences(head.name, workspace, includeDeclaration);
80440
80810
  }
80441
80811
  }
@@ -80500,9 +80870,9 @@ function findWidgetReferences(widgetName2, workspace, includeDeclaration, includ
80500
80870
  if (includeDeclaration && widget) {
80501
80871
  locations.push({ uri: widget.uri, range: widget.range });
80502
80872
  }
80503
- if (workspace.macros.getMacro(widgetName2)) return locations;
80873
+ if (workspace.getEffectiveMacro(widgetName2)) return locations;
80504
80874
  const lowerName2 = widgetName2.toLowerCase();
80505
- const isBlock = widget?.block ?? false;
80875
+ const isBlock = workspace.isContainer(widgetName2);
80506
80876
  for (const docUri of workspace.documents.getUris()) {
80507
80877
  const doc = workspace.markup.get(docUri);
80508
80878
  if (!doc) continue;
@@ -80596,7 +80966,7 @@ function computeRename(uri, position, newName, workspace) {
80596
80966
  const doc = workspace.markup.get(documentUri);
80597
80967
  if (!doc) continue;
80598
80968
  for (const head of documentMacroHeads(doc)) {
80599
- if (!head.closing && workspace.macros.hasUnsafeConfiguredPassages(head.name)) {
80969
+ if (!head.closing && workspace.getEffectiveMacro(head.name) && workspace.macros.hasUnsafeConfiguredPassages(head.name)) {
80600
80970
  throw new RenameError(`Cannot rename: {${head.name}} has configured passage parameters whose positions are ambiguous, repeated or conflict with its runtime signature.`).at(documentUri, head.range);
80601
80971
  }
80602
80972
  }
@@ -80677,9 +81047,13 @@ function computeRename(uri, position, newName, workspace) {
80677
81047
  `Cannot rename to ${JSON.stringify(newName)}: a widget name must start with a letter and contain only letters, digits, _ and - to be called as {name}.`
80678
81048
  ).at(uri, symbol2.range);
80679
81049
  }
81050
+ const widget = workspace.widgets.getWidget(symbol2.name);
81051
+ if (widget && !widget.block && workspace.macros.isBlock(symbol2.name)) {
81052
+ throw new RenameError("Cannot rename this inline widget: its current name inherits a macro container body that the new name would lose.").at(uri, symbol2.range);
81053
+ }
80680
81054
  if (workspace.macros.getMacro(newName)) {
80681
81055
  throw new RenameError(
80682
- `Cannot rename to ${JSON.stringify(newName)}: a macro of that name exists and takes precedence over the widget.`
81056
+ `Cannot rename to ${JSON.stringify(newName)}: a macro of that name already exists.`
80683
81057
  ).at(uri, symbol2.range);
80684
81058
  }
80685
81059
  const destination = workspace.widgets.getWidget(newName);
@@ -80690,7 +81064,6 @@ function computeRename(uri, position, newName, workspace) {
80690
81064
  for (const ref2 of invocationRefs) {
80691
81065
  addEdit(ref2.uri, ref2.range, newName);
80692
81066
  }
80693
- const widget = workspace.widgets.getWidget(symbol2.name);
80694
81067
  if (widget) {
80695
81068
  addEdit(widget.uri, widget.nameRange, newName);
80696
81069
  }
@@ -80843,7 +81216,7 @@ function resolveSymbolAtCursor(uri, position, workspace) {
80843
81216
  const head = doc && macroHeadAt(doc, position);
80844
81217
  if (head) {
80845
81218
  const widget = workspace.widgets.getWidget(head.name);
80846
- if (!workspace.macros.getMacro(head.name) && widget && (!head.closing || widget.block)) {
81219
+ if (!workspace.getEffectiveMacro(head.name) && widget && (!head.closing || workspace.isContainer(head.name))) {
80847
81220
  return { kind: "widget", name: head.name, range: head.range };
80848
81221
  }
80849
81222
  }
@@ -81032,7 +81405,7 @@ function addWidgetParamHints(passages, inRange, workspace, hints) {
81032
81405
  for (const token of macroTokens(passage)) {
81033
81406
  if (token.isClose || token.rawArgs === "") continue;
81034
81407
  if (!inRange(passage.position(token.start).line)) continue;
81035
- if (workspace.macros.getMacro(token.name)) continue;
81408
+ if (workspace.getEffectiveMacro(token.name)) continue;
81036
81409
  const widget = workspace.widgets.getWidget(token.name);
81037
81410
  if (!widget || widget.params.length === 0) continue;
81038
81411
  const args2 = splitArgs(token.rawArgs);
@@ -81139,7 +81512,7 @@ function computeSemanticTokensAbsolute(uri, workspace) {
81139
81512
  tokenModifiers: encodeModifiers(["declaration"])
81140
81513
  });
81141
81514
  for (const macro of macroTokens(passage)) {
81142
- push2(passage, macro.nameStart, macro.nameEnd, "function", workspace.macros.getMacro(macro.name) ? ["defaultLibrary"] : []);
81515
+ push2(passage, macro.nameStart, macro.nameEnd, "function", workspace.getEffectiveMacro(macro.name) ? ["defaultLibrary"] : []);
81143
81516
  }
81144
81517
  for (const use of variableUses(passage)) {
81145
81518
  push2(passage, use.start, use.end, "variable", VARIABLE_MODIFIERS[use.sigil]);