@rohal12/spindle-lsp 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -6855,8 +6855,8 @@ function walk(node, src, out, binding, shorthand) {
6855
6855
  }
6856
6856
  }
6857
6857
  }
6858
- function findCodeEnd(src, start, { goal = "expression", stop } = {}) {
6859
- const end = scanCodeEnd(src, start, goal, stop);
6858
+ function findCodeEnd(src, start, { goal = "expression", stop, onRunOut } = {}) {
6859
+ const end = scanCodeEnd(src, start, goal, stop, onRunOut);
6860
6860
  return end < 0 && !stop ? parsedCodeEnd(src, start, goal) : end;
6861
6861
  }
6862
6862
  function parsedCodeEnd(src, start, goal) {
@@ -6874,7 +6874,11 @@ function parsedCodeEnd(src, start, goal) {
6874
6874
  }
6875
6875
  return -1;
6876
6876
  }
6877
- function scanCodeEnd(src, start, goal, stop) {
6877
+ function scanCodeEnd(src, start, goal, stop, onRunOut) {
6878
+ const ranOut = () => {
6879
+ onRunOut?.();
6880
+ return -1;
6881
+ };
6878
6882
  const stack = [];
6879
6883
  let prevEnd = -1;
6880
6884
  let prevWord = false;
@@ -6890,11 +6894,12 @@ function scanCodeEnd(src, start, goal, stop) {
6890
6894
  if (!/^Unterminated/.test(message) && !inTemplate) {
6891
6895
  resumeAt = Math.max(tok.pos, from) + 1;
6892
6896
  afterOperand = false;
6893
- if (resumeAt >= src.length) return -1;
6897
+ if (resumeAt >= src.length) return ranOut();
6894
6898
  break;
6895
6899
  }
6896
6900
  const k2 = stack.lastIndexOf("${");
6897
- if (!/^Unterminated template/.test(message) || k2 < 0 || templateSlip || stop) return -1;
6901
+ if (!/^Unterminated template/.test(message) || k2 < 0 || templateSlip || stop) return /^Unterminated/.test(message) && !/^Unterminated regular/.test(message) && !(/^Unterminated string/.test(message) && prevWord && tok.pos === prevEnd) ? ranOut() : -1;
6902
+ onRunOut?.();
6898
6903
  templateSlip = true;
6899
6904
  stack.length = k2;
6900
6905
  resumeAt = tok.pos;
@@ -6903,7 +6908,7 @@ function scanCodeEnd(src, start, goal, stop) {
6903
6908
  }
6904
6909
  if (tok.restarted && stack.includes("${")) return -1;
6905
6910
  const t = tok.type;
6906
- if (t === types$1.eof) return -1;
6911
+ if (t === types$1.eof) return ranOut();
6907
6912
  if (t === types$1.string && prevWord && tok.start === prevEnd) return -1;
6908
6913
  prevWord = t === types$1.name && !WORDS_BEFORE_STRING.has(tok.value) || t === types$1.num;
6909
6914
  prevEnd = tok.end;
@@ -7131,48 +7136,107 @@ function scanStringLiteral(src, start) {
7131
7136
  function skipLiteral(input, i2) {
7132
7137
  const quote = input[i2];
7133
7138
  const template = quote === "`";
7139
+ let ranOut = false;
7134
7140
  for (let j2 = i2 + 1; j2 < input.length; ) {
7135
7141
  const c = input[j2];
7136
7142
  if (c === "\\") j2 += 2;
7137
- else if (c === quote) return j2 + 1;
7138
- else if (!template && c === "\n") return -1;
7143
+ else if (c === quote) return {
7144
+ end: j2 + 1,
7145
+ ranOut
7146
+ };
7147
+ else if (!template && c === "\n") return {
7148
+ end: -1,
7149
+ ranOut
7150
+ };
7139
7151
  else if (template && c === "$" && input[j2 + 1] === "{") {
7140
- const close = lenientClose(input, j2 + 2);
7141
- if (close === -1) return -1;
7142
- j2 = close + 1;
7152
+ const close = lenientRead(input, j2 + 2);
7153
+ ranOut || (ranOut = close.ranOut);
7154
+ if (close.end === -1) return {
7155
+ end: -1,
7156
+ ranOut
7157
+ };
7158
+ j2 = close.end + 1;
7143
7159
  } else j2++;
7144
7160
  }
7145
- return -1;
7161
+ return {
7162
+ end: -1,
7163
+ ranOut: ranOut || template
7164
+ };
7165
+ }
7166
+ function lenientClose(input, i2, onRunOut) {
7167
+ const { end, ranOut } = lenientRead(input, i2);
7168
+ if (ranOut) onRunOut?.();
7169
+ return end;
7146
7170
  }
7147
- function lenientClose(input, i2) {
7171
+ function lenientRead(input, i2) {
7148
7172
  if (memoInput !== input) {
7149
7173
  memoInput = input;
7150
7174
  memo.clear();
7151
7175
  }
7152
- let end = memo.get(i2);
7153
- if (end === void 0) {
7154
- end = scanLenient(input, i2);
7155
- memo.set(i2, end);
7176
+ let read = memo.get(i2);
7177
+ if (read === void 0) {
7178
+ read = scanLenient(input, i2);
7179
+ memo.set(i2, read);
7156
7180
  }
7157
- return end;
7181
+ return read;
7158
7182
  }
7159
7183
  function scanLenient(input, i2) {
7160
7184
  let depth = 0;
7185
+ let ranOut = false;
7161
7186
  while (i2 < input.length) {
7162
7187
  const c = input[i2];
7163
7188
  if (c === "{") {
7164
7189
  depth++;
7165
7190
  i2++;
7166
7191
  } else if (c === "}") {
7167
- if (depth === 0) return i2;
7192
+ if (depth === 0) return {
7193
+ end: i2,
7194
+ ranOut
7195
+ };
7168
7196
  depth--;
7169
7197
  i2++;
7170
7198
  } else if (c === "`" || (c === '"' || c === "'") && !(i2 > 0 && NON_STRING_QUOTE_PREFIX.test(input[i2 - 1]))) {
7171
7199
  const close = skipLiteral(input, i2);
7172
- i2 = close === -1 ? i2 + 1 : close;
7200
+ ranOut || (ranOut = close.ranOut);
7201
+ i2 = close.end === -1 ? i2 + 1 : close.end;
7173
7202
  } else i2++;
7174
7203
  }
7175
- return -1;
7204
+ return {
7205
+ end: -1,
7206
+ ranOut: true
7207
+ };
7208
+ }
7209
+ function jsCodeEnd(onOpenEnd) {
7210
+ return {
7211
+ closeBrace(input, codeStart, lenientStart) {
7212
+ let ranOut = false;
7213
+ const onRunOut = () => ranOut = true;
7214
+ const strict = findCodeEnd(input, codeStart, { onRunOut });
7215
+ const end = strict !== -1 ? strict : lenientClose(input, lenientStart, onRunOut);
7216
+ if (ranOut) onOpenEnd?.(input, codeStart);
7217
+ return end;
7218
+ },
7219
+ rawBodyEnd(input, bodyStart, name) {
7220
+ const closer = `\\{/${name}\\s*\\}`;
7221
+ const first = new RegExp(closer, "gi");
7222
+ first.lastIndex = bodyStart;
7223
+ const firstAt = first.exec(input)?.index ?? -1;
7224
+ if (firstAt === -1) return -1;
7225
+ const at3 = new RegExp(closer, "iy");
7226
+ let ranOut = false;
7227
+ const end = findCodeEnd(input, bodyStart, {
7228
+ goal: "statements",
7229
+ stop: (k2) => {
7230
+ at3.lastIndex = k2;
7231
+ return at3.test(input);
7232
+ },
7233
+ onRunOut: () => ranOut = true
7234
+ });
7235
+ if (end !== -1) return end;
7236
+ if (onOpenEnd && (ranOut || new RegExp(`\\{/${name}\\s*$`, "i").test(input))) onOpenEnd(input, bodyStart);
7237
+ return firstAt;
7238
+ }
7239
+ };
7176
7240
  }
7177
7241
  function isCodeAttribute(name) {
7178
7242
  const lower = name.toLowerCase();
@@ -7204,7 +7268,7 @@ function splitSigilTemplate(template) {
7204
7268
  if (text2) parts.push({ text: text2 });
7205
7269
  return parts;
7206
7270
  }
7207
- function run(source, startRule, options) {
7271
+ function run$1(source, startRule, options) {
7208
7272
  const hooks = options.hooks ? {
7209
7273
  ...defaultHooks,
7210
7274
  ...options.hooks
@@ -7226,7 +7290,7 @@ function run(source, startRule, options) {
7226
7290
  }
7227
7291
  }
7228
7292
  function analyzeMarkup(source, options = {}) {
7229
- const { tokens: tokens2, errors, damage } = scan(source, options);
7293
+ const { tokens: tokens2, errors, damage } = scanMarkupTolerant(source, options);
7230
7294
  const hooks = {
7231
7295
  ...defaultHooks,
7232
7296
  ...options.hooks
@@ -7261,23 +7325,23 @@ function pairingError(source, error48) {
7261
7325
  return new MarkupError(error48.message, error48.start, line, column, error48.code, error48.end, error48.data);
7262
7326
  }
7263
7327
  function tokenizeMarkup(source, options = {}) {
7264
- return run(source, "Tokens", options);
7328
+ return run$1(source, "Tokens", options);
7265
7329
  }
7266
7330
  function parseSelectors(source, at3 = 0) {
7267
- const found = run(source.slice(at3), "SelectorsPrefix", {});
7331
+ const found = run$1(source.slice(at3), "SelectorsPrefix", {});
7268
7332
  return {
7269
7333
  ...found,
7270
7334
  end: at3 + found.end
7271
7335
  };
7272
7336
  }
7273
7337
  function tokenizeMarkupTolerant(source, options = {}) {
7274
- const { tokens: tokens2, errors } = scan(source, options);
7338
+ const { tokens: tokens2, errors } = scanMarkupTolerant(source, options);
7275
7339
  return {
7276
7340
  tokens: tokens2,
7277
7341
  errors
7278
7342
  };
7279
7343
  }
7280
- function scan(source, options) {
7344
+ function scanMarkupTolerant(source, options = {}) {
7281
7345
  const tokens2 = [];
7282
7346
  const errors = [];
7283
7347
  const damage = [];
@@ -8810,6 +8874,554 @@ function discoverMacros(source) {
8810
8874
  }
8811
8875
  return found;
8812
8876
  }
8877
+ function segmentRegions(body) {
8878
+ const lines = body.split("\n");
8879
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
8880
+ if (lines.length === 0) return [];
8881
+ const regions = [];
8882
+ let i2 = 0;
8883
+ while (i2 < lines.length) {
8884
+ const line = lines[i2];
8885
+ const trimmed2 = line.trim();
8886
+ if (/^<script(\s|>)/i.test(trimmed2)) {
8887
+ const block = blockLines(lines, i2, /<\/script\s*>/i);
8888
+ regions.push({
8889
+ type: "script",
8890
+ lines: block
8891
+ });
8892
+ i2 += block.length;
8893
+ continue;
8894
+ }
8895
+ if (/^<svg(\s|>)/i.test(trimmed2)) {
8896
+ const block = blockLines(lines, i2, /<\/svg\s*>/i);
8897
+ regions.push({
8898
+ type: "svg",
8899
+ lines: block
8900
+ });
8901
+ i2 += block.length;
8902
+ continue;
8903
+ }
8904
+ if (/^<([a-zA-Z][\w-]*)/.test(trimmed2)) {
8905
+ const tagName = trimmed2.match(/^<([a-zA-Z][\w-]*)/)[1].toLowerCase();
8906
+ if (VOID_ELEMENTS.has(tagName) && !lines[i2 + 1]?.trim().startsWith("<")) {
8907
+ pushSpindleLine(regions, line);
8908
+ i2++;
8909
+ continue;
8910
+ }
8911
+ if (new RegExp(`</${tagName}\\s*>`, "i").test(trimmed2)) {
8912
+ pushSpindleLine(regions, line);
8913
+ i2++;
8914
+ continue;
8915
+ }
8916
+ const htmlLines = [line];
8917
+ let depth = 1;
8918
+ i2++;
8919
+ while (i2 < lines.length && depth > 0) {
8920
+ htmlLines.push(lines[i2]);
8921
+ const opens = (lines[i2].match(new RegExp(`<${tagName}(?:[\\s>]|$)`, "gi")) ?? []).length;
8922
+ const closes2 = (lines[i2].match(new RegExp(`</${tagName}\\s*>`, "gi")) ?? []).length;
8923
+ depth += opens - closes2;
8924
+ i2++;
8925
+ }
8926
+ regions.push({
8927
+ type: "html",
8928
+ lines: htmlLines
8929
+ });
8930
+ continue;
8931
+ }
8932
+ pushSpindleLine(regions, line);
8933
+ i2++;
8934
+ }
8935
+ return regions;
8936
+ }
8937
+ function blockLines(lines, start, close) {
8938
+ let end = start;
8939
+ while (end < lines.length - 1 && !close.test(lines[end])) end++;
8940
+ return lines.slice(start, end + 1);
8941
+ }
8942
+ function pushSpindleLine(regions, line) {
8943
+ const last2 = regions[regions.length - 1];
8944
+ if (last2?.type === "spindle") last2.lines.push(line);
8945
+ else regions.push({
8946
+ type: "spindle",
8947
+ lines: [line]
8948
+ });
8949
+ }
8950
+ async function run(code, prettier2, options, overrides) {
8951
+ if (!prettier2) return void 0;
8952
+ try {
8953
+ return await prettier2.format(code, {
8954
+ ...options ?? DEFAULT_PRETTIER_OPTIONS,
8955
+ ...overrides
8956
+ });
8957
+ } catch {
8958
+ return;
8959
+ }
8960
+ }
8961
+ function formatJS(code, prettier2, options) {
8962
+ return run(code, prettier2, options, { parser: "babel" });
8963
+ }
8964
+ function formatCSS(code, prettier2, options) {
8965
+ return run(code, prettier2, options, { parser: "css" });
8966
+ }
8967
+ function formatHTML(code, prettier2, options) {
8968
+ return run(code, prettier2, options, {
8969
+ parser: "html",
8970
+ htmlWhitespaceSensitivity: "css"
8971
+ });
8972
+ }
8973
+ function scanSpindleMarkup(text2) {
8974
+ const tokens2 = [];
8975
+ const tags = [];
8976
+ const doBlocks = [];
8977
+ let open;
8978
+ const push = (t, inAttribute) => {
8979
+ const match = {
8980
+ start: t.start,
8981
+ end: t.end,
8982
+ token: text2.slice(t.start, t.end)
8983
+ };
8984
+ if (inAttribute) match.inAttribute = true;
8985
+ tokens2.push(match);
8986
+ };
8987
+ for (const t of tokenizeMarkupTolerant(text2).tokens) if (t.type === "html") {
8988
+ tags.push({
8989
+ start: t.start,
8990
+ end: t.end
8991
+ });
8992
+ for (const attribute of t.attributeSpans) {
8993
+ if (attribute.valueStart === void 0 || attribute.valueEnd === void 0) continue;
8994
+ const value = text2.slice(attribute.valueStart, attribute.valueEnd);
8995
+ if (!value.includes("{")) continue;
8996
+ for (const inner of tokenizeMarkupTolerant(value, { text: true }).tokens) if (inner.type === "macro" || inner.type === "variable" || inner.type === "expression") push({
8997
+ start: attribute.valueStart + inner.start,
8998
+ end: attribute.valueStart + inner.end
8999
+ }, true);
9000
+ }
9001
+ } else if (t.type !== "text") {
9002
+ if (t.type === "macro" && t.name === "do") {
9003
+ if (!t.isClose) open = t;
9004
+ else if (open) {
9005
+ doBlocks.push({
9006
+ start: open.start,
9007
+ end: t.end,
9008
+ bodyStart: open.end,
9009
+ bodyEnd: t.start
9010
+ });
9011
+ open = void 0;
9012
+ }
9013
+ }
9014
+ push(t);
9015
+ }
9016
+ return {
9017
+ tokens: tokens2,
9018
+ tags,
9019
+ doBlocks
9020
+ };
9021
+ }
9022
+ function multilineJsLiterals(code) {
9023
+ const spans = [];
9024
+ let skipUntil = 0;
9025
+ lexJs(code, { literal(text2, index, nesting) {
9026
+ if (nesting > 0 || index < skipUntil) return;
9027
+ const quote = text2[0];
9028
+ if (quote === "`") {
9029
+ skipUntil = lexTemplate(code, index);
9030
+ if (code.slice(index, skipUntil).includes("\n")) spans.push([index, skipUntil]);
9031
+ } else if ((quote === '"' || quote === "'") && text2.includes("\n")) spans.push([index, index + text2.length]);
9032
+ } }, "statements");
9033
+ return spans;
9034
+ }
9035
+ function lineHasTag(tags, start, end) {
9036
+ return tags.some((tag) => tag.start < end && tag.end > start);
9037
+ }
9038
+ function replaceSvgBlocks(html) {
9039
+ const tokens2 = [];
9040
+ return {
9041
+ text: html.replace(/<svg[\s>][\s\S]*?<\/svg>/gi, (match) => {
9042
+ const idx = tokens2.length;
9043
+ tokens2.push(match);
9044
+ return `<!--SVG:${idx}-->`;
9045
+ }),
9046
+ tokens: tokens2
9047
+ };
9048
+ }
9049
+ function placeholdersIntact(text2, tokens2, placeholdersOf) {
9050
+ return tokens2.every((_2, index) => placeholdersOf(index).reduce((count, placeholder) => count + text2.split(placeholder).length - 1, 0) === 1);
9051
+ }
9052
+ function sameSpindleTokens(before, after) {
9053
+ const tokensOf2 = (text2) => scanSpindleMarkup(text2).tokens.map((m) => m.token);
9054
+ const expected = tokensOf2(before);
9055
+ const found = tokensOf2(after);
9056
+ return expected.length === found.length && expected.every((t, i2) => t === found[i2]);
9057
+ }
9058
+ function restorePlaceholders(text2, tokens2, placeholdersOf) {
9059
+ let result = text2;
9060
+ tokens2.forEach((token, index) => {
9061
+ for (const placeholder of placeholdersOf(index)) result = result.replace(placeholder, () => token);
9062
+ });
9063
+ return result;
9064
+ }
9065
+ function restoreSvgBlocks(text2, tokens2) {
9066
+ return restorePlaceholders(text2, tokens2, SVG_PLACEHOLDERS);
9067
+ }
9068
+ function replaceSpindleTokens(html) {
9069
+ const { tokens: found, tags, doBlocks } = scanSpindleMarkup(html);
9070
+ const units = [];
9071
+ const pushDoBlock = (block) => units.push({
9072
+ start: block.start,
9073
+ end: block.end,
9074
+ token: html.slice(block.start, block.end)
9075
+ });
9076
+ let doIndex = 0;
9077
+ for (const m of found) {
9078
+ for (let block = doBlocks[doIndex]; block && block.end <= m.start; ) {
9079
+ pushDoBlock(block);
9080
+ block = doBlocks[++doIndex];
9081
+ }
9082
+ const open = doBlocks[doIndex];
9083
+ if (open && m.start >= open.start) continue;
9084
+ units.push(m);
9085
+ }
9086
+ for (; doIndex < doBlocks.length; doIndex++) pushDoBlock(doBlocks[doIndex]);
9087
+ units.sort((a, b) => a.start - b.start);
9088
+ const tokens2 = [];
9089
+ const resultLines = [];
9090
+ let unit = 0;
9091
+ let lineStart = 0;
9092
+ while (lineStart <= html.length) {
9093
+ let lineEnd = lineStart;
9094
+ const lineUnits = [];
9095
+ for (; ; ) {
9096
+ while (unit < units.length && units[unit].end <= lineEnd) unit++;
9097
+ const next = units[unit];
9098
+ const inside = next && next.start <= lineEnd ? next : void 0;
9099
+ if (inside) {
9100
+ lineUnits.push(inside);
9101
+ lineEnd = inside.end;
9102
+ unit++;
9103
+ continue;
9104
+ }
9105
+ if (lineEnd >= html.length || html[lineEnd] === "\n") break;
9106
+ lineEnd++;
9107
+ }
9108
+ const line = html.slice(lineStart, lineEnd);
9109
+ const indent = line.match(/^(\s*)/)?.[1] ?? "";
9110
+ if (lineUnits.length === 0) resultLines.push(line);
9111
+ else if (!lineHasTag(tags, lineStart, lineEnd)) {
9112
+ const idx = tokens2.length;
9113
+ tokens2.push(line.trim());
9114
+ resultLines.push(`${indent}<!--SP:${idx}-->`);
9115
+ } else {
9116
+ let replaced = "";
9117
+ let last2 = lineStart;
9118
+ for (const u of lineUnits) {
9119
+ const idx = tokens2.length;
9120
+ tokens2.push(u.token);
9121
+ replaced += html.slice(last2, u.start) + (u.inAttribute ? `__SP${idx}__` : `<!--SP:${idx}-->`);
9122
+ last2 = u.end;
9123
+ }
9124
+ resultLines.push(replaced + html.slice(last2, lineEnd));
9125
+ }
9126
+ lineStart = lineEnd + 1;
9127
+ }
9128
+ return {
9129
+ text: resultLines.join("\n"),
9130
+ tokens: tokens2
9131
+ };
9132
+ }
9133
+ function restoreSpindleTokens(text2, tokens2) {
9134
+ return restorePlaceholders(text2, tokens2, SPINDLE_PLACEHOLDERS);
9135
+ }
9136
+ function closedMacroNames(text2) {
9137
+ const found = /* @__PURE__ */ new Set();
9138
+ for (const t of tokenizeMarkupTolerant(text2).tokens) if (t.type === "macro" && t.isClose) found.add(t.name.toLowerCase());
9139
+ return found;
9140
+ }
9141
+ function defaultIsBlock(text2, extra = []) {
9142
+ const closed = closedMacroNames(text2);
9143
+ for (const name of extra) closed.add(name.toLowerCase());
9144
+ return (name) => isBlockMacro(name) || closed.has(name.toLowerCase());
9145
+ }
9146
+ async function formatPassage(body, options = {}) {
9147
+ if (body.charCodeAt(0) === 65279) return "\uFEFF" + await formatPassage(body.slice(1), options);
9148
+ const eol = dominantEol(body);
9149
+ const output = await formatLf(body.replace(/\r\n/g, "\n"), options);
9150
+ return eol === "\r\n" ? output.replace(/\n/g, "\r\n") : output;
9151
+ }
9152
+ function dominantEol(text2) {
9153
+ const crlf = (text2.match(/\r\n/g) ?? []).length;
9154
+ const lf = (text2.match(/\n/g) ?? []).length - crlf;
9155
+ if (crlf !== lf) return crlf > lf ? "\r\n" : "\n";
9156
+ return text2.indexOf("\r\n") !== -1 && text2.indexOf("\r\n") < text2.indexOf("\n") ? "\r\n" : "\n";
9157
+ }
9158
+ function replaceAll(text2, from, to) {
9159
+ return text2.split(from).join(to);
9160
+ }
9161
+ function newlineSentinel(text2) {
9162
+ for (let code = 57344; code <= 63743; code++) {
9163
+ const ch = String.fromCharCode(code);
9164
+ if (!text2.includes(ch)) return ch;
9165
+ }
9166
+ throw new Error("no free private-use character for the newline stand-in");
9167
+ }
9168
+ function protectMultilineTokens(body, sentinel) {
9169
+ const { tokens: tokens2, doBlocks } = scanSpindleMarkup(body);
9170
+ const spans = [];
9171
+ for (const m of tokens2) if (m.token.includes("\n")) spans.push([m.start, m.end]);
9172
+ for (const block of doBlocks) for (const [a, b] of multilineJsLiterals(body.slice(block.bodyStart, block.bodyEnd))) spans.push([block.bodyStart + a, block.bodyStart + b]);
9173
+ return joinSpans(body, spans, sentinel);
9174
+ }
9175
+ function joinJsLiterals(code, sentinel) {
9176
+ return joinSpans(code, multilineJsLiterals(code), sentinel);
9177
+ }
9178
+ function joinSpans(text2, spans, sentinel) {
9179
+ let out = "";
9180
+ let last2 = 0;
9181
+ for (const [a, b] of [...spans].sort((x2, y) => x2[0] - y[0])) {
9182
+ out += text2.slice(last2, a) + replaceAll(text2.slice(a, b), "\n", sentinel);
9183
+ last2 = b;
9184
+ }
9185
+ return out + text2.slice(last2);
9186
+ }
9187
+ async function formatLf(text2, options) {
9188
+ const { prettier: prettier2, prettierOptions } = options;
9189
+ const body = text2.replace(/(?:[ \t]*\n)+[ \t]*$/, "\n");
9190
+ if (options.kind === "script" || options.kind === "stylesheet") {
9191
+ const format = options.kind === "script" ? formatJS : formatCSS;
9192
+ const code = body.trim();
9193
+ return (await format(code, prettier2, prettierOptions) ?? code).trim().replace(/\n*$/, "\n");
9194
+ }
9195
+ const isBlock = options.isBlock ?? defaultIsBlock(body, options.blockMacros);
9196
+ const isDedenting = options.isDedentingSubMacro ?? ((name) => DEFAULT_DEDENTING.has(name.toLowerCase()));
9197
+ const sentinel = newlineSentinel(body);
9198
+ const lines = [];
9199
+ const hardBreakLines = /* @__PURE__ */ new Set();
9200
+ const regions = segmentRegions(protectMultilineTokens(body, sentinel));
9201
+ for (const region of regions) {
9202
+ if (region.type !== "spindle") region.lines = region.lines.map((l) => replaceAll(l, sentinel, "\n"));
9203
+ if (region.type === "script") {
9204
+ if (region.lines.length === 1) {
9205
+ lines.push(...region.lines);
9206
+ continue;
9207
+ }
9208
+ const innerCode = region.lines.slice(1, -1).join("\n");
9209
+ const formatted = await formatJS(innerCode.trim(), prettier2, prettierOptions);
9210
+ if (formatted === void 0 || !sameSpindleTokens(innerCode, formatted)) {
9211
+ lines.push(...region.lines);
9212
+ continue;
9213
+ }
9214
+ lines.push(region.lines[0]);
9215
+ if (formatted.trim()) for (const fLine of joinJsLiterals(formatted.trim(), sentinel).split("\n")) lines.push(" " + fLine);
9216
+ lines.push(region.lines[region.lines.length - 1]);
9217
+ continue;
9218
+ }
9219
+ if (region.type === "svg") {
9220
+ lines.push(...region.lines);
9221
+ continue;
9222
+ }
9223
+ if (region.type === "html") {
9224
+ const htmlText = region.lines.join("\n");
9225
+ if (WHITESPACE_AS_WRITTEN.test(htmlText)) {
9226
+ lines.push(...region.lines);
9227
+ continue;
9228
+ }
9229
+ const { text: svgPlaceholdered, tokens: svgTokens } = replaceSvgBlocks(htmlText);
9230
+ const { text: placeholdered, tokens: tokens2 } = replaceSpindleTokens(svgPlaceholdered);
9231
+ const formatted = await formatHTML(placeholdered, prettier2, prettierOptions);
9232
+ if (formatted === void 0 || !placeholdersIntact(formatted, tokens2, SPINDLE_PLACEHOLDERS) || !placeholdersIntact(formatted, svgTokens, SVG_PLACEHOLDERS)) {
9233
+ lines.push(...region.lines);
9234
+ continue;
9235
+ }
9236
+ const restored = restoreSvgBlocks(restoreSpindleTokens(formatted.trim(), tokens2.map((t) => replaceAll(t, "\n", sentinel))), svgTokens);
9237
+ for (const fLine of restored.split("\n")) lines.push(fLine);
9238
+ continue;
9239
+ }
9240
+ let indented = indentMacros(region.lines, isBlock, isDedenting);
9241
+ if (options.maxLineLength) indented = wrapLines(indented, options.maxLineLength);
9242
+ for (const line of indented) {
9243
+ if (line.endsWith(" ")) hardBreakLines.add(lines.length);
9244
+ lines.push(line);
9245
+ }
9246
+ }
9247
+ let output = lines.map((l, i2) => hardBreakLines.has(i2) ? l : l.replace(/\s+$/, "")).join("\n");
9248
+ output = output.replace(/\n*$/, "\n");
9249
+ return replaceAll(output, sentinel, "\n");
9250
+ }
9251
+ function indentMacros(lines, isBlock, isDedenting) {
9252
+ const macrosOnLine = macroTokensByLine(lines);
9253
+ const bodies = [{
9254
+ openLine: -1,
9255
+ minCol: Infinity,
9256
+ keepRelative: false
9257
+ }];
9258
+ const stack = [0];
9259
+ const info = [];
9260
+ const openBody = (line) => {
9261
+ bodies.push({
9262
+ openLine: line,
9263
+ minCol: Infinity,
9264
+ keepRelative: false
9265
+ });
9266
+ stack.push(bodies.length - 1);
9267
+ };
9268
+ const currentBody = () => stack[stack.length - 1];
9269
+ const closeBody = () => stack.length > 1 ? stack.pop() : void 0;
9270
+ const addLine = (col, text2) => {
9271
+ const body = bodies[currentBody()];
9272
+ body.minCol = Math.min(body.minCol, col);
9273
+ if (LIST_ITEM_REGEX.test(text2) || CODE_FENCE_REGEX.test(text2)) body.keepRelative = true;
9274
+ info.push({
9275
+ body: currentBody(),
9276
+ col,
9277
+ text: text2
9278
+ });
9279
+ };
9280
+ for (let i2 = 0; i2 < lines.length; i2++) {
9281
+ const lineText = lines[i2];
9282
+ const trimmed2 = lineText.trim();
9283
+ if (trimmed2 === "") {
9284
+ info.push(null);
9285
+ continue;
9286
+ }
9287
+ const col = indentColumn(lineText);
9288
+ const lead = lineText.length - lineText.trimStart().length;
9289
+ const macros = macrosOnLine[i2];
9290
+ const [first, ...rest] = macros;
9291
+ const startsWithMacro = first !== void 0 && first.start === lead;
9292
+ if (startsWithMacro && !first.isClose && isDedenting(first.name)) {
9293
+ const closed2 = closeBody();
9294
+ if (closed2 === void 0) addLine(col, trimmed2);
9295
+ else info.push({
9296
+ body: currentBody(),
9297
+ closes: closed2,
9298
+ col,
9299
+ text: trimmed2
9300
+ });
9301
+ openBody(i2);
9302
+ continue;
9303
+ }
9304
+ const closed = startsWithMacro && first.isClose ? closeBody() : void 0;
9305
+ if (closed === void 0) addLine(col, trimmed2);
9306
+ else info.push({
9307
+ body: currentBody(),
9308
+ closes: closed,
9309
+ col,
9310
+ text: trimmed2
9311
+ });
9312
+ if (startsWithMacro && !first.isClose && isBlock(first.name)) openBody(i2);
9313
+ for (const macro of startsWithMacro ? rest : macros) if (macro.isClose && macro.start > lead) closeBody();
9314
+ }
9315
+ const outCol = [];
9316
+ const result = [];
9317
+ for (let i2 = 0; i2 < info.length; i2++) {
9318
+ const line = info[i2];
9319
+ if (!line) {
9320
+ outCol.push(0);
9321
+ result.push("");
9322
+ continue;
9323
+ }
9324
+ let col;
9325
+ if (line.closes !== void 0) col = outCol[bodies[line.closes].openLine];
9326
+ else {
9327
+ const body = bodies[line.body];
9328
+ const isRoot = body.openLine === -1;
9329
+ col = (isRoot ? 0 : outCol[body.openLine] + 2) + (body.keepRelative ? line.col - (isRoot ? 0 : body.minCol) : 0);
9330
+ }
9331
+ outCol.push(col);
9332
+ const hardBreak = / {2,}$/.test(lines[i2]) && info[i2 + 1] ? " " : "";
9333
+ result.push(" ".repeat(col) + line.text + hardBreak);
9334
+ }
9335
+ return result;
9336
+ }
9337
+ function macroTokensByLine(lines) {
9338
+ const byLine = lines.map(() => []);
9339
+ const starts = [];
9340
+ let offset2 = 0;
9341
+ for (const line2 of lines) {
9342
+ starts.push(offset2);
9343
+ offset2 += line2.length + 1;
9344
+ }
9345
+ let line = 0;
9346
+ for (const token of tokenizeMarkupTolerant(lines.join("\n")).tokens) {
9347
+ if (token.type !== "macro") continue;
9348
+ while (line + 1 < lines.length && starts[line + 1] <= token.start) line++;
9349
+ const lineStart = starts[line];
9350
+ byLine[line].push({
9351
+ ...token,
9352
+ start: token.start - lineStart,
9353
+ end: token.end - lineStart
9354
+ });
9355
+ }
9356
+ return byLine;
9357
+ }
9358
+ function indentColumn(line) {
9359
+ let col = 0;
9360
+ for (const ch of line) if (ch === " ") col++;
9361
+ else if (ch === " ") col += 4 - col % 4;
9362
+ else break;
9363
+ return col;
9364
+ }
9365
+ function wrapLines(lines, maxLen) {
9366
+ const result = [];
9367
+ const javaScript = doBodyLines(lines);
9368
+ for (const [index, line] of lines.entries()) {
9369
+ if (line.length <= maxLen || javaScript.has(index)) {
9370
+ result.push(line);
9371
+ continue;
9372
+ }
9373
+ const trimmed2 = line.trim();
9374
+ if (trimmed2 === "" || /^\{[\/#.]/.test(trimmed2) || /^\[\[/.test(trimmed2) || /^\{choice\b/.test(trimmed2) || /^\{choices\b/.test(trimmed2)) {
9375
+ result.push(line);
9376
+ continue;
9377
+ }
9378
+ const wrapped = wordWrap(trimmed2, maxLen, line.match(/^(\s*)/)?.[1] ?? "");
9379
+ if (line.endsWith(" ")) wrapped[wrapped.length - 1] += " ";
9380
+ result.push(...wrapped);
9381
+ }
9382
+ return result;
9383
+ }
9384
+ function doBodyLines(lines) {
9385
+ const found = /* @__PURE__ */ new Set();
9386
+ const text2 = lines.join("\n");
9387
+ for (const block of scanSpindleMarkup(text2).doBlocks) {
9388
+ const first = text2.slice(0, block.bodyStart).split("\n").length - 1;
9389
+ const last2 = text2.slice(0, block.bodyEnd).split("\n").length - 1;
9390
+ for (let i2 = first; i2 <= last2; i2++) found.add(i2);
9391
+ }
9392
+ return found;
9393
+ }
9394
+ function wordWrap(text2, maxLen, indent) {
9395
+ const lines = [];
9396
+ const inToken = new Array(text2.length).fill(false);
9397
+ for (const token of tokenizeMarkupTolerant(text2).tokens) {
9398
+ if (token.type === "text" || token.type === "html") continue;
9399
+ inToken.fill(true, token.start, token.end);
9400
+ }
9401
+ const room = maxLen - indent.length;
9402
+ let pos = 0;
9403
+ while (pos < text2.length) {
9404
+ if (text2.length - pos <= room) {
9405
+ lines.push(indent + text2.slice(pos));
9406
+ break;
9407
+ }
9408
+ let breakIdx = -1;
9409
+ for (let i2 = pos + 1; i2 < text2.length && i2 - pos <= room; i2++) if (text2[i2] === " " && !inToken[i2]) breakIdx = i2;
9410
+ if (breakIdx === -1) {
9411
+ for (let i2 = pos + room + 1; i2 < text2.length; i2++) if (text2[i2] === " " && !inToken[i2]) {
9412
+ breakIdx = i2;
9413
+ break;
9414
+ }
9415
+ }
9416
+ if (breakIdx === -1) {
9417
+ lines.push(indent + text2.slice(pos));
9418
+ break;
9419
+ }
9420
+ lines.push(indent + text2.slice(pos, breakIdx));
9421
+ pos = breakIdx + 1;
9422
+ }
9423
+ return lines;
9424
+ }
8813
9425
  function validateStoryMarkup(passages, macros, options = {}) {
8814
9426
  const list3 = [...passages];
8815
9427
  const all = [...macros];
@@ -8843,8 +9455,10 @@ function blockLookup(macros) {
8843
9455
  return (name) => blocks.has(name.toLowerCase()) || isBlockMacro(name);
8844
9456
  }
8845
9457
  function passagePieces(source, macros = []) {
9458
+ return passagePiecesOfTokens(source, macros, tokenizeMarkupTolerant(source).tokens);
9459
+ }
9460
+ function passagePiecesOfTokens(source, macros, tokens2) {
8846
9461
  const all = [...macros];
8847
- const { tokens: tokens2 } = tokenizeMarkupTolerant(source);
8848
9462
  return [...passagePiecesOf(source, tokens2, parameterLookup(all), { isBlock: blockLookup(all) })];
8849
9463
  }
8850
9464
  function widgetDefinitions(passages, macros = []) {
@@ -8867,7 +9481,7 @@ function validateVariableReferences(passages, declarations, macros = []) {
8867
9481
  const list3 = [...passages];
8868
9482
  return checkVariableReferences(list3.filter((p) => !p.tags?.some((t) => NOT_MARKUP_TAGS.includes(t))), declarations, referenceOptions(macros, false, list3));
8869
9483
  }
8870
- var SIGIL_SCOPES, SIGIL_CHARS, grammar_shared_default, LB, RB, SIGIL_SCOPES$1, withSelectors, VOID_TAGS, BRANCH_PARENT$1, LINK_SEPARATORS, NO_SELECTORS, EMAIL_AUTOLINK, peg$SyntaxError, NameMap, NameSet, BLOCK_MACROS, BLOCK_WIDGETS, RAW_BODY_MACROS, BRANCHING, quoteArg, last$1, BRANCH_PARENT, isBranchTag, lowerName, nameOf, closerOf, closes, astralIdentifierCodes, astralIdentifierStartCodes, nonASCIIidentifierChars, nonASCIIidentifierStartChars, reservedWords, ecma5AndLessKeywords, keywords$1, keywordRelationalOperator, nonASCIIidentifierStart, nonASCIIidentifier, TokenType, beforeExpr, startsExpr, keywords, types$1, lineBreak, lineBreakG, nonASCIIwhitespace, skipWhiteSpace, ref, hasOwnProperty, toString, hasOwn$1, isArray, regexpCache, loneSurrogate, Position, SourceLocation, defaultOptions, warnedAboutEcmaVersion, SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_ARROW, SCOPE_SIMPLE_CATCH, SCOPE_SUPER, SCOPE_DIRECT_SUPER, SCOPE_CLASS_STATIC_BLOCK, SCOPE_CLASS_FIELD_INIT, SCOPE_SWITCH, SCOPE_VAR, BIND_NONE, BIND_VAR, BIND_LEXICAL, BIND_FUNCTION, BIND_SIMPLE_CATCH, BIND_OUTSIDE, Parser, prototypeAccessors, pp$9, literal, DestructuringErrors, pp$8, loopLabel, switchLabel, empty$1, FUNC_STATEMENT, FUNC_HANGING_STATEMENT, FUNC_NULLABLE_ID, pp$7, TokContext, types, pp$6, pp$5, empty, pp$4, pp$3, Scope, Node, pp$2, scriptValuesAddedInUnicode, ecma9BinaryProperties, ecma10BinaryProperties, ecma11BinaryProperties, ecma12BinaryProperties, ecma13BinaryProperties, unicodeBinaryProperties, unicodeBinaryPropertiesOfStrings, unicodeGeneralCategoryValues, ecma9ScriptValues, ecma10ScriptValues, ecma11ScriptValues, ecma12ScriptValues, ecma13ScriptValues, unicodeScriptValues, data, ecmaVersion, i, list, pp$1, BranchID, RegExpValidationState, CharSetNone, CharSetOk, CharSetString, Token, pp, INVALID_TEMPLATE_ESCAPE_ERROR, OPTIONS, AT_NAME_RE, TRANS_NAME_RE, SIGIL_IDENT_RE, LINE_BREAK_RE, NOT_A_BODY_RE, OPERAND_ENDS, SigilParser, OPENERS, CLOSERS$1, CodeSyntaxError, last, CLOSER, isNode, NAMESPACE_NAME, WORD_RE$2, FLAGS_RE, WORDS_BEFORE_STRING, NON_STRING_QUOTE_PREFIX, memoInput, memo, defaultCodeEnd, defaultHooks, MarkupError, OFFSET_FIELD, listeners, TRAILING_OPERATOR_RE, slice, MacroArgumentError, WORD_RE$1, NUMBER_RE$1, WORD_OR_PUNCT_RE, LEADING_OPERATOR_RE, takesRest, PARAMETER_TYPES, STRING_HOLDS, metadataRegistry, SUB_MACRO_PARAMETERS, RESERVED_NAME, isArrayIndex, hasOwn, createNamespace, parses, CONDITION_MACROS, PASSAGE_BODIES, holdsByDefault, JS_ESCAPE_RE, textPiece, registeredParameters, WIDGET_PARAMETERS, NOT_MARKUP, NOT_MARKUP_TAGS$1, BRANCH_MACROS, didYouMean, PASSAGE_ARGUMENT_HINT, BARE_WORD_RE, NUMBER_RE, IDENT_RE, KEY_RE, ARROW_RE, LINE_END_RE, ARRAY_INDEX_RE, UNSUPPORTED_UNDEFINED, Reader, VAR_PATH_RE, QUOTED_VAR_ARG_RE, VALID_VAR_TYPES, PRIMITIVE_SAMPLES, UNSUPPORTED_TYPE, NO_STORE_VAR_MACROS, FIELD_PATH_RE, CLOSERS, BLOCK_WORDS, NOT_PARAMETERS, DECLARATIONS, WRITE_RE, MUTATORS, MUTATING_CALL_RE, BEFORE_BINDING, isWordChar, Bindings, CALL_RE, IDENTIFIER_ARGUMENT_RE, WORD_RE, MODIFIER_RE, KEY_START_RE, CONFIG_KEYS, ConfigReader, stringOf, booleanOf, textOf, stringsOf, NOT_MARKUP_TAGS;
9484
+ var SIGIL_SCOPES, SIGIL_CHARS, grammar_shared_default, LB, RB, SIGIL_SCOPES$1, withSelectors, VOID_TAGS, BRANCH_PARENT$1, LINK_SEPARATORS, NO_SELECTORS, EMAIL_AUTOLINK, peg$SyntaxError, NameMap, NameSet, BLOCK_MACROS, BLOCK_WIDGETS, RAW_BODY_MACROS, BRANCHING, quoteArg, last$1, BRANCH_PARENT, isBranchTag, lowerName, nameOf, closerOf, closes, astralIdentifierCodes, astralIdentifierStartCodes, nonASCIIidentifierChars, nonASCIIidentifierStartChars, reservedWords, ecma5AndLessKeywords, keywords$1, keywordRelationalOperator, nonASCIIidentifierStart, nonASCIIidentifier, TokenType, beforeExpr, startsExpr, keywords, types$1, lineBreak, lineBreakG, nonASCIIwhitespace, skipWhiteSpace, ref, hasOwnProperty, toString, hasOwn$1, isArray, regexpCache, loneSurrogate, Position, SourceLocation, defaultOptions, warnedAboutEcmaVersion, SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_ARROW, SCOPE_SIMPLE_CATCH, SCOPE_SUPER, SCOPE_DIRECT_SUPER, SCOPE_CLASS_STATIC_BLOCK, SCOPE_CLASS_FIELD_INIT, SCOPE_SWITCH, SCOPE_VAR, BIND_NONE, BIND_VAR, BIND_LEXICAL, BIND_FUNCTION, BIND_SIMPLE_CATCH, BIND_OUTSIDE, Parser, prototypeAccessors, pp$9, literal, DestructuringErrors, pp$8, loopLabel, switchLabel, empty$1, FUNC_STATEMENT, FUNC_HANGING_STATEMENT, FUNC_NULLABLE_ID, pp$7, TokContext, types, pp$6, pp$5, empty, pp$4, pp$3, Scope, Node, pp$2, scriptValuesAddedInUnicode, ecma9BinaryProperties, ecma10BinaryProperties, ecma11BinaryProperties, ecma12BinaryProperties, ecma13BinaryProperties, unicodeBinaryProperties, unicodeBinaryPropertiesOfStrings, unicodeGeneralCategoryValues, ecma9ScriptValues, ecma10ScriptValues, ecma11ScriptValues, ecma12ScriptValues, ecma13ScriptValues, unicodeScriptValues, data, ecmaVersion, i, list, pp$1, BranchID, RegExpValidationState, CharSetNone, CharSetOk, CharSetString, Token, pp, INVALID_TEMPLATE_ESCAPE_ERROR, OPTIONS, AT_NAME_RE, TRANS_NAME_RE, SIGIL_IDENT_RE, LINE_BREAK_RE, NOT_A_BODY_RE, OPERAND_ENDS, SigilParser, OPENERS, CLOSERS$1, CodeSyntaxError, last, CLOSER, isNode, NAMESPACE_NAME, WORD_RE$2, FLAGS_RE, WORDS_BEFORE_STRING, NON_STRING_QUOTE_PREFIX, memoInput, memo, defaultCodeEnd, defaultHooks, MarkupError, OFFSET_FIELD, listeners, TRAILING_OPERATOR_RE, slice, MacroArgumentError, WORD_RE$1, NUMBER_RE$1, WORD_OR_PUNCT_RE, LEADING_OPERATOR_RE, takesRest, PARAMETER_TYPES, STRING_HOLDS, metadataRegistry, SUB_MACRO_PARAMETERS, RESERVED_NAME, isArrayIndex, hasOwn, createNamespace, parses, CONDITION_MACROS, PASSAGE_BODIES, holdsByDefault, JS_ESCAPE_RE, textPiece, registeredParameters, WIDGET_PARAMETERS, NOT_MARKUP, NOT_MARKUP_TAGS$1, BRANCH_MACROS, didYouMean, PASSAGE_ARGUMENT_HINT, BARE_WORD_RE, NUMBER_RE, IDENT_RE, KEY_RE, ARROW_RE, LINE_END_RE, ARRAY_INDEX_RE, UNSUPPORTED_UNDEFINED, Reader, VAR_PATH_RE, QUOTED_VAR_ARG_RE, VALID_VAR_TYPES, PRIMITIVE_SAMPLES, UNSUPPORTED_TYPE, NO_STORE_VAR_MACROS, FIELD_PATH_RE, CLOSERS, BLOCK_WORDS, NOT_PARAMETERS, DECLARATIONS, WRITE_RE, MUTATORS, MUTATING_CALL_RE, BEFORE_BINDING, isWordChar, Bindings, CALL_RE, IDENTIFIER_ARGUMENT_RE, WORD_RE, MODIFIER_RE, KEY_START_RE, CONFIG_KEYS, ConfigReader, stringOf, booleanOf, textOf, stringsOf, VOID_ELEMENTS, DEFAULT_PRETTIER_OPTIONS, SVG_PLACEHOLDERS, SPINDLE_PLACEHOLDERS, DEFAULT_DEDENTING, WHITESPACE_AS_WRITTEN, LIST_ITEM_REGEX, CODE_FENCE_REGEX, NOT_MARKUP_TAGS;
8871
9485
  var init_story_variables = __esm({
8872
9486
  "node_modules/@rohal12/spindle/dist/pkg/story-variables.js"() {
8873
9487
  SIGIL_SCOPES = {
@@ -14251,28 +14865,7 @@ var init_story_variables = __esm({
14251
14865
  NON_STRING_QUOTE_PREFIX = /[\p{L}\p{N}_\\]/u;
14252
14866
  memoInput = "";
14253
14867
  memo = /* @__PURE__ */ new Map();
14254
- defaultCodeEnd = {
14255
- closeBrace(input, codeStart, lenientStart) {
14256
- const end = findCodeEnd(input, codeStart);
14257
- return end !== -1 ? end : lenientClose(input, lenientStart);
14258
- },
14259
- rawBodyEnd(input, bodyStart, name) {
14260
- const closer = `\\{/${name}\\s*\\}`;
14261
- const first = new RegExp(closer, "gi");
14262
- first.lastIndex = bodyStart;
14263
- const firstAt = first.exec(input)?.index ?? -1;
14264
- if (firstAt === -1) return -1;
14265
- const at3 = new RegExp(closer, "iy");
14266
- const end = findCodeEnd(input, bodyStart, {
14267
- goal: "statements",
14268
- stop: (k2) => {
14269
- at3.lastIndex = k2;
14270
- return at3.test(input);
14271
- }
14272
- });
14273
- return end === -1 ? firstAt : end;
14274
- }
14275
- };
14868
+ defaultCodeEnd = jsCodeEnd();
14276
14869
  defaultHooks = {
14277
14870
  ...defaultCodeEnd,
14278
14871
  isBlock: isBlockMacro,
@@ -15374,6 +15967,35 @@ var init_story_variables = __esm({
15374
15967
  booleanOf = (d) => d?.type === "boolean" ? d.value : void 0;
15375
15968
  textOf = (d) => stringOf(d)?.value;
15376
15969
  stringsOf = (d) => d?.type === "array" ? d.items.flatMap((item) => stringOf(item)?.value ?? []) : [];
15970
+ VOID_ELEMENTS = /* @__PURE__ */ new Set([
15971
+ "area",
15972
+ "base",
15973
+ "br",
15974
+ "col",
15975
+ "embed",
15976
+ "hr",
15977
+ "img",
15978
+ "input",
15979
+ "link",
15980
+ "meta",
15981
+ "param",
15982
+ "source",
15983
+ "track",
15984
+ "wbr"
15985
+ ]);
15986
+ DEFAULT_PRETTIER_OPTIONS = { tabWidth: 2 };
15987
+ SVG_PLACEHOLDERS = (i2) => [`<!--SVG:${i2}-->`];
15988
+ SPINDLE_PLACEHOLDERS = (i2) => [`<!--SP:${i2}-->`, `__SP${i2}__`];
15989
+ DEFAULT_DEDENTING = /* @__PURE__ */ new Set([
15990
+ "else",
15991
+ "elseif",
15992
+ "next",
15993
+ "case",
15994
+ "default"
15995
+ ]);
15996
+ WHITESPACE_AS_WRITTEN = /<(?:pre|textarea)[\s>]|white-space\s*:/i;
15997
+ LIST_ITEM_REGEX = /^(?:[-+*]|\d{1,9}[.)])(?:[ \t]|$)/;
15998
+ CODE_FENCE_REGEX = /^(?:`{3,}|~{3,})/;
15377
15999
  NOT_MARKUP_TAGS = ["script", "stylesheet"];
15378
16000
  }
15379
16001
  });
@@ -24615,7 +25237,7 @@ var MINIMUM_SPINDLE_VERSION, DEFAULT_TARGET;
24615
25237
  var init_spindle_version = __esm({
24616
25238
  "src/core/workspace/spindle-version.ts"() {
24617
25239
  "use strict";
24618
- MINIMUM_SPINDLE_VERSION = "0.59.20";
25240
+ MINIMUM_SPINDLE_VERSION = "0.60.0";
24619
25241
  DEFAULT_TARGET = { version: void 0, source: "default", supported: true };
24620
25242
  }
24621
25243
  });
@@ -27641,322 +28263,31 @@ function headerTags(header) {
27641
28263
  const unescaped = header.replace(/\\./g, "ec");
27642
28264
  return (unescaped.match(/\[[^\]]*\]/g) ?? []).flatMap((t) => t.slice(1, -1).split(/\s+/));
27643
28265
  }
27644
- function segmentRegions(body) {
27645
- const lines = body.split("\n");
27646
- if (lines.length > 0 && lines[lines.length - 1] === "") {
27647
- lines.pop();
27648
- }
27649
- if (lines.length === 0) return [];
27650
- const regions = [];
27651
- let i2 = 0;
27652
- while (i2 < lines.length) {
27653
- const line = lines[i2];
27654
- const trimmed2 = line.trim();
27655
- if (/^<script(\s|>)/i.test(trimmed2)) {
27656
- const scriptLines = [line];
27657
- i2++;
27658
- if (/<\/script\s*>/i.test(line)) {
27659
- regions.push({ type: "script", lines: scriptLines });
27660
- continue;
27661
- }
27662
- while (i2 < lines.length && !/<\/script>/i.test(lines[i2])) {
27663
- scriptLines.push(lines[i2]);
27664
- i2++;
27665
- }
27666
- if (i2 < lines.length) {
27667
- scriptLines.push(lines[i2]);
27668
- i2++;
27669
- }
27670
- regions.push({ type: "script", lines: scriptLines });
27671
- continue;
27672
- }
27673
- if (/^<svg(\s|>)/i.test(trimmed2)) {
27674
- const svgLines = [line];
27675
- i2++;
27676
- while (i2 < lines.length && !/<\/svg>/i.test(lines[i2])) {
27677
- svgLines.push(lines[i2]);
27678
- i2++;
27679
- }
27680
- if (i2 < lines.length) {
27681
- svgLines.push(lines[i2]);
27682
- i2++;
27683
- }
27684
- regions.push({ type: "svg", lines: svgLines });
27685
- continue;
27686
- }
27687
- if (/^<([a-zA-Z][\w-]*)/.test(trimmed2)) {
27688
- const tagMatch = trimmed2.match(/^<([a-zA-Z][\w-]*)/);
27689
- const tagName = tagMatch[1].toLowerCase();
27690
- if (VOID_ELEMENTS.has(tagName) && !lines[i2 + 1]?.trim().startsWith("<")) {
27691
- pushSpindleLine(regions, line);
27692
- i2++;
27693
- continue;
27694
- }
27695
- const selfClosing = new RegExp(`</${tagName}\\s*>`, "i");
27696
- if (selfClosing.test(trimmed2)) {
27697
- pushSpindleLine(regions, line);
27698
- i2++;
27699
- continue;
27700
- }
27701
- const htmlLines = [line];
27702
- let depth = 1;
27703
- i2++;
27704
- while (i2 < lines.length && depth > 0) {
27705
- htmlLines.push(lines[i2]);
27706
- const opens = (lines[i2].match(new RegExp(`<${tagName}(?:[\\s>]|$)`, "gi")) ?? []).length;
27707
- const closes2 = (lines[i2].match(new RegExp(`</${tagName}\\s*>`, "gi")) ?? []).length;
27708
- depth += opens - closes2;
27709
- i2++;
27710
- }
27711
- regions.push({ type: "html", lines: htmlLines });
27712
- continue;
27713
- }
27714
- pushSpindleLine(regions, line);
27715
- i2++;
27716
- }
27717
- return regions;
27718
- }
27719
- function pushSpindleLine(regions, line) {
27720
- const last2 = regions[regions.length - 1];
27721
- if (last2?.type === "spindle") {
27722
- last2.lines.push(line);
27723
- } else {
27724
- regions.push({ type: "spindle", lines: [line] });
27725
- }
27726
- }
27727
- var PASSAGE_HEADER_REGEX, VOID_ELEMENTS;
28266
+ var PASSAGE_HEADER_REGEX;
27728
28267
  var init_segment = __esm({
27729
28268
  "src/plugins/format/segment.ts"() {
27730
28269
  "use strict";
27731
28270
  init_passage_parser();
27732
28271
  PASSAGE_HEADER_REGEX = /^::\s+/;
27733
- VOID_ELEMENTS = /* @__PURE__ */ new Set([
27734
- "area",
27735
- "base",
27736
- "br",
27737
- "col",
27738
- "embed",
27739
- "hr",
27740
- "img",
27741
- "input",
27742
- "link",
27743
- "meta",
27744
- "param",
27745
- "source",
27746
- "track",
27747
- "wbr"
27748
- ]);
27749
28272
  }
27750
28273
  });
27751
28274
 
27752
28275
  // src/plugins/format/prettier-bridge.ts
27753
28276
  async function loadPrettier() {
27754
- if (prettierLoaded) return prettier;
27755
- prettierLoaded = true;
27756
- try {
27757
- prettier = await import("prettier");
27758
- } catch {
27759
- prettier = null;
27760
- }
27761
- return prettier;
27762
- }
27763
- async function getBaseConfig() {
27764
- const p = await loadPrettier();
27765
- if (!p) return {};
28277
+ prettier ??= import("prettier").catch(() => null);
28278
+ const p = await prettier;
28279
+ if (!p) return void 0;
27766
28280
  try {
27767
28281
  const resolved = await p.resolveConfig(process.cwd());
27768
- return { tabWidth: 2, ...resolved ?? {} };
27769
- } catch {
27770
- return { tabWidth: 2 };
27771
- }
27772
- }
27773
- async function formatJS(code) {
27774
- const p = await loadPrettier();
27775
- if (!p) return code;
27776
- try {
27777
- const config2 = await getBaseConfig();
27778
- return await p.format(code, { ...config2, parser: "babel" });
27779
- } catch {
27780
- return code;
27781
- }
27782
- }
27783
- async function formatCSS(code) {
27784
- const p = await loadPrettier();
27785
- if (!p) return code;
27786
- try {
27787
- const config2 = await getBaseConfig();
27788
- return await p.format(code, { ...config2, parser: "css" });
27789
- } catch {
27790
- return code;
27791
- }
27792
- }
27793
- async function formatHTML(code) {
27794
- const p = await loadPrettier();
27795
- if (!p) return code;
27796
- try {
27797
- const config2 = await getBaseConfig();
27798
- return await p.format(code, {
27799
- ...config2,
27800
- parser: "html",
27801
- htmlWhitespaceSensitivity: "ignore"
27802
- });
28282
+ return { prettier: p, options: { tabWidth: 2, ...resolved ?? {} } };
27803
28283
  } catch {
27804
- return code;
28284
+ return { prettier: p, options: { tabWidth: 2 } };
27805
28285
  }
27806
28286
  }
27807
- var prettier, prettierLoaded;
28287
+ var prettier;
27808
28288
  var init_prettier_bridge = __esm({
27809
28289
  "src/plugins/format/prettier-bridge.ts"() {
27810
28290
  "use strict";
27811
- prettier = null;
27812
- prettierLoaded = false;
27813
- }
27814
- });
27815
-
27816
- // src/plugins/format/placeholders.ts
27817
- function scanSpindleMarkup(text2) {
27818
- const tokens2 = [];
27819
- const tags = [];
27820
- const doBlocks = [];
27821
- let open;
27822
- const push = (t, inAttribute) => {
27823
- const match = { start: t.start, end: t.end, token: text2.slice(t.start, t.end) };
27824
- if (inAttribute) match.inAttribute = true;
27825
- tokens2.push(match);
27826
- };
27827
- for (const t of tokenizeMarkupTolerant(text2).tokens) {
27828
- if (t.type === "html") {
27829
- tags.push({ start: t.start, end: t.end });
27830
- for (const attribute of t.attributeSpans) {
27831
- if (attribute.valueStart === void 0 || attribute.valueEnd === void 0) continue;
27832
- const value = text2.slice(attribute.valueStart, attribute.valueEnd);
27833
- if (!value.includes("{")) continue;
27834
- for (const inner of tokenizeMarkupTolerant(value, { text: true }).tokens) {
27835
- if (inner.type === "macro" || inner.type === "variable" || inner.type === "expression") {
27836
- push({ start: attribute.valueStart + inner.start, end: attribute.valueStart + inner.end }, true);
27837
- }
27838
- }
27839
- }
27840
- } else if (t.type !== "text") {
27841
- if (t.type === "macro" && t.name === "do") {
27842
- if (!t.isClose) open = t;
27843
- else if (open) {
27844
- doBlocks.push({ start: open.start, end: t.end, bodyStart: open.end, bodyEnd: t.start });
27845
- open = void 0;
27846
- }
27847
- }
27848
- push(t);
27849
- }
27850
- }
27851
- return { tokens: tokens2, tags, doBlocks };
27852
- }
27853
- function multilineJsLiterals(code) {
27854
- const spans = [];
27855
- let skipUntil = 0;
27856
- lexJs(code, {
27857
- literal(text2, index, nesting) {
27858
- if (nesting > 0 || index < skipUntil) return;
27859
- const quote = text2[0];
27860
- if (quote === "`") {
27861
- skipUntil = lexTemplate(code, index);
27862
- if (code.slice(index, skipUntil).includes("\n")) spans.push([index, skipUntil]);
27863
- } else if ((quote === '"' || quote === "'") && text2.includes("\n")) {
27864
- spans.push([index, index + text2.length]);
27865
- }
27866
- }
27867
- }, "statements");
27868
- return spans;
27869
- }
27870
- function lineHasTag(tags, start, end) {
27871
- return tags.some((tag) => tag.start < end && tag.end > start);
27872
- }
27873
- function replaceSvgBlocks(html) {
27874
- const tokens2 = [];
27875
- const result = html.replace(/<svg[\s>][\s\S]*?<\/svg>/gi, (match) => {
27876
- const idx = tokens2.length;
27877
- tokens2.push(match);
27878
- return `<!--SVG:${idx}-->`;
27879
- });
27880
- return { text: result, tokens: tokens2 };
27881
- }
27882
- function restoreSvgBlocks(text2, tokens2) {
27883
- let result = text2;
27884
- for (let i2 = 0; i2 < tokens2.length; i2++) {
27885
- result = result.replace(`<!--SVG:${i2}-->`, () => tokens2[i2]);
27886
- }
27887
- return result;
27888
- }
27889
- function replaceSpindleTokens(html) {
27890
- const { tokens: found, tags, doBlocks } = scanSpindleMarkup(html);
27891
- const units = [];
27892
- let doIndex = 0;
27893
- for (const m of found) {
27894
- while (doIndex < doBlocks.length && doBlocks[doIndex].end <= m.start) {
27895
- const block = doBlocks[doIndex++];
27896
- units.push({ start: block.start, end: block.end, token: html.slice(block.start, block.end) });
27897
- }
27898
- if (doBlocks[doIndex] && m.start >= doBlocks[doIndex].start) continue;
27899
- units.push(m);
27900
- }
27901
- for (; doIndex < doBlocks.length; doIndex++) {
27902
- const block = doBlocks[doIndex];
27903
- units.push({ start: block.start, end: block.end, token: html.slice(block.start, block.end) });
27904
- }
27905
- units.sort((a, b) => a.start - b.start);
27906
- const tokens2 = [];
27907
- const resultLines = [];
27908
- let unit = 0;
27909
- let lineStart = 0;
27910
- while (lineStart <= html.length) {
27911
- let lineEnd = lineStart;
27912
- const lineUnits = [];
27913
- for (; ; ) {
27914
- while (unit < units.length && units[unit].end <= lineEnd) unit++;
27915
- const inside = units[unit] && units[unit].start <= lineEnd ? units[unit] : void 0;
27916
- if (inside) {
27917
- lineUnits.push(inside);
27918
- lineEnd = inside.end;
27919
- unit++;
27920
- continue;
27921
- }
27922
- if (lineEnd >= html.length || html[lineEnd] === "\n") break;
27923
- lineEnd++;
27924
- }
27925
- const line = html.slice(lineStart, lineEnd);
27926
- const indent = line.match(/^(\s*)/)?.[1] ?? "";
27927
- if (lineUnits.length === 0) {
27928
- resultLines.push(line);
27929
- } else if (!lineHasTag(tags, lineStart, lineEnd)) {
27930
- const idx = tokens2.length;
27931
- tokens2.push(line.trim());
27932
- resultLines.push(`${indent}<!--SP:${idx}-->`);
27933
- } else {
27934
- let replaced = "";
27935
- let last2 = lineStart;
27936
- for (const u of lineUnits) {
27937
- const idx = tokens2.length;
27938
- tokens2.push(u.token);
27939
- replaced += html.slice(last2, u.start) + (u.inAttribute ? `__SP${idx}__` : `<!--SP:${idx}-->`);
27940
- last2 = u.end;
27941
- }
27942
- resultLines.push(replaced + html.slice(last2, lineEnd));
27943
- }
27944
- lineStart = lineEnd + 1;
27945
- }
27946
- return { text: resultLines.join("\n"), tokens: tokens2 };
27947
- }
27948
- function restoreSpindleTokens(text2, tokens2) {
27949
- let result = text2;
27950
- for (let i2 = 0; i2 < tokens2.length; i2++) {
27951
- result = result.replace(`<!--SP:${i2}-->`, () => tokens2[i2]);
27952
- result = result.replace(`__SP${i2}__`, () => tokens2[i2]);
27953
- }
27954
- return result;
27955
- }
27956
- var init_placeholders = __esm({
27957
- "src/plugins/format/placeholders.ts"() {
27958
- "use strict";
27959
- init_tooling();
27960
28291
  }
27961
28292
  });
27962
28293
 
@@ -27974,50 +28305,22 @@ function buildDefaultIsBlock(text2) {
27974
28305
  }
27975
28306
  async function formatDocument(text2, options) {
27976
28307
  if (text2.charCodeAt(0) === 65279) return "\uFEFF" + await formatDocument(text2.slice(1), options);
27977
- const eol = dominantEol(text2);
27978
- const output = await formatLf(text2.replace(/\r\n/g, "\n"), options);
28308
+ const eol = dominantEol2(text2);
28309
+ const output = await formatLf2(text2.replace(/\r\n/g, "\n"), options);
27979
28310
  return eol === "\r\n" ? output.replace(/\n/g, "\r\n") : output;
27980
28311
  }
27981
- function dominantEol(text2) {
28312
+ function dominantEol2(text2) {
27982
28313
  const crlf = (text2.match(/\r\n/g) ?? []).length;
27983
28314
  const lf = (text2.match(/\n/g) ?? []).length - crlf;
27984
28315
  if (crlf !== lf) return crlf > lf ? "\r\n" : "\n";
27985
28316
  return text2.indexOf("\r\n") !== -1 && text2.indexOf("\r\n") < text2.indexOf("\n") ? "\r\n" : "\n";
27986
28317
  }
27987
- function newlineSentinel(text2) {
27988
- for (let code = 57344; code <= 63743; code++) {
27989
- const ch = String.fromCharCode(code);
27990
- if (!text2.includes(ch)) return ch;
27991
- }
27992
- throw new Error("no free private-use character for the newline stand-in");
27993
- }
27994
- function protectMultilineTokens(body, sentinel) {
27995
- const { tokens: tokens2, doBlocks } = scanSpindleMarkup(body);
27996
- const spans = [];
27997
- for (const m of tokens2) {
27998
- if (m.token.includes("\n")) spans.push([m.start, m.end]);
27999
- }
28000
- for (const block of doBlocks) {
28001
- for (const [a, b] of multilineJsLiterals(body.slice(block.bodyStart, block.bodyEnd))) {
28002
- spans.push([block.bodyStart + a, block.bodyStart + b]);
28003
- }
28004
- }
28005
- spans.sort((x2, y) => x2[0] - y[0]);
28006
- let out = "";
28007
- let last2 = 0;
28008
- for (const [a, b] of spans) {
28009
- out += body.slice(last2, a) + body.slice(a, b).replaceAll("\n", sentinel);
28010
- last2 = b;
28011
- }
28012
- return out + body.slice(last2);
28013
- }
28014
- async function formatLf(text2, options) {
28318
+ async function formatLf2(text2, options) {
28015
28319
  const isBlock = options?.isBlock ?? buildDefaultIsBlock(text2);
28016
- const isDedenting = options?.isDedentingSubMacro ?? ((name) => DEFAULT_DEDENTING.has(name.toLowerCase()));
28320
+ const isDedentingSubMacro = options?.isDedentingSubMacro ?? ((name) => DEFAULT_DEDENTING2.has(name.toLowerCase()));
28321
+ const prettier2 = await loadPrettier();
28017
28322
  const passages = splitPassages(text2);
28018
- const sentinel = newlineSentinel(text2);
28019
28323
  const resultLines = [];
28020
- const hardBreakLines = /* @__PURE__ */ new Set();
28021
28324
  for (let pi2 = 0; pi2 < passages.length; pi2++) {
28022
28325
  const passage = passages[pi2];
28023
28326
  if (pi2 > 0 && passage.header) {
@@ -28030,226 +28333,24 @@ async function formatLf(text2, options) {
28030
28333
  }
28031
28334
  if (passage.header) {
28032
28335
  const header = PASSAGE_HEADER_REGEX2.test(passage.header) ? normalizePassageHeader(passage.header) : passage.header;
28033
- resultLines.push(header);
28034
- }
28035
- const kind = classifyPassage(passage.header);
28036
- if (kind === "script") {
28037
- const formatted = await formatJS(passage.body.trim());
28038
- resultLines.push(formatted.trim());
28039
- continue;
28040
- }
28041
- if (kind === "stylesheet") {
28042
- const formatted = await formatCSS(passage.body.trim());
28043
- resultLines.push(formatted.trim());
28044
- continue;
28045
- }
28046
- const regions = segmentRegions(protectMultilineTokens(passage.body, sentinel));
28047
- for (const region of regions) {
28048
- if (region.type !== "spindle") {
28049
- region.lines = region.lines.map((l) => l.replaceAll(sentinel, "\n"));
28050
- }
28051
- if (region.type === "script") {
28052
- if (region.lines.length === 1) {
28053
- resultLines.push(region.lines[0]);
28054
- continue;
28055
- }
28056
- const firstLine = region.lines[0];
28057
- const lastLine = region.lines[region.lines.length - 1];
28058
- const innerLines = region.lines.slice(1, region.lines.length - 1);
28059
- const innerCode = innerLines.join("\n");
28060
- const formatted = await formatJS(innerCode.trim());
28061
- resultLines.push(firstLine);
28062
- if (formatted.trim()) {
28063
- for (const fLine of formatted.trim().split("\n")) {
28064
- resultLines.push(" " + fLine);
28065
- }
28066
- }
28067
- resultLines.push(lastLine);
28068
- continue;
28069
- }
28070
- if (region.type === "svg") {
28071
- resultLines.push(...region.lines);
28072
- continue;
28073
- }
28074
- if (region.type === "html") {
28075
- const htmlText = region.lines.join("\n");
28076
- const { text: svgPlaceholdered, tokens: svgTokens } = replaceSvgBlocks(htmlText);
28077
- const { text: placeholdered, tokens: tokens2 } = replaceSpindleTokens(svgPlaceholdered);
28078
- const formatted = await formatHTML(placeholdered);
28079
- const restoredSpindle = restoreSpindleTokens(formatted.trim(), tokens2.map((t) => t.replaceAll("\n", sentinel)));
28080
- const restored = restoreSvgBlocks(restoredSpindle, svgTokens);
28081
- for (const fLine of restored.split("\n")) {
28082
- resultLines.push(fLine);
28083
- }
28084
- continue;
28085
- }
28086
- let indented = indentMacros(region.lines, isBlock, isDedenting);
28087
- if (options?.maxLineLength) {
28088
- indented = wrapLines(indented, options.maxLineLength);
28089
- }
28090
- for (const line of indented) {
28091
- if (line.endsWith(" ")) hardBreakLines.add(resultLines.length);
28092
- resultLines.push(line);
28093
- }
28094
- }
28095
- }
28096
- let output = resultLines.map((l, i2) => hardBreakLines.has(i2) ? l : l.replace(/\s+$/, "")).join("\n");
28097
- output = output.replace(/\n*$/, "\n");
28098
- return output.replaceAll(sentinel, "\n");
28099
- }
28100
- function indentMacros(lines, isBlock, isDedenting) {
28101
- const macrosOnLine = macroTokensByLine(lines);
28102
- const bodies = [{ openLine: -1, minCol: Infinity, keepRelative: false }];
28103
- const stack = [0];
28104
- const info = [];
28105
- const openBody = (line) => {
28106
- bodies.push({ openLine: line, minCol: Infinity, keepRelative: false });
28107
- stack.push(bodies.length - 1);
28108
- };
28109
- const closeBody = () => stack.length > 1 ? stack.pop() : void 0;
28110
- const addLine = (col, text2) => {
28111
- const body = bodies[stack[stack.length - 1]];
28112
- body.minCol = Math.min(body.minCol, col);
28113
- if (LIST_ITEM_REGEX.test(text2) || CODE_FENCE_REGEX.test(text2)) body.keepRelative = true;
28114
- info.push({ body: stack[stack.length - 1], col, text: text2 });
28115
- };
28116
- for (let i2 = 0; i2 < lines.length; i2++) {
28117
- const trimmed2 = lines[i2].trim();
28118
- if (trimmed2 === "") {
28119
- info.push(null);
28120
- continue;
28121
- }
28122
- const col = indentColumn(lines[i2]);
28123
- const lead = lines[i2].length - lines[i2].trimStart().length;
28124
- const [first, ...rest] = macrosOnLine[i2];
28125
- const startsWithMacro = first !== void 0 && first.start === lead;
28126
- if (startsWithMacro && !first.isClose && isDedenting(first.name)) {
28127
- const closed2 = closeBody();
28128
- if (closed2 === void 0) addLine(col, trimmed2);
28129
- else info.push({ body: stack[stack.length - 1], closes: closed2, col, text: trimmed2 });
28130
- openBody(i2);
28131
- continue;
28132
- }
28133
- const closed = startsWithMacro && first.isClose ? closeBody() : void 0;
28134
- if (closed === void 0) addLine(col, trimmed2);
28135
- else info.push({ body: stack[stack.length - 1], closes: closed, col, text: trimmed2 });
28136
- if (startsWithMacro && !first.isClose && isBlock(first.name)) {
28137
- openBody(i2);
28138
- }
28139
- for (const macro of startsWithMacro ? rest : macrosOnLine[i2]) {
28140
- if (macro.isClose && macro.start > lead) closeBody();
28141
- }
28142
- }
28143
- const outCol = [];
28144
- const result = [];
28145
- for (let i2 = 0; i2 < info.length; i2++) {
28146
- const line = info[i2];
28147
- if (!line) {
28148
- outCol.push(0);
28149
- result.push("");
28150
- continue;
28151
- }
28152
- let col;
28153
- if (line.closes !== void 0) {
28154
- col = outCol[bodies[line.closes].openLine];
28155
- } else {
28156
- const body = bodies[line.body];
28157
- const isRoot = body.openLine === -1;
28158
- const base = isRoot ? 0 : outCol[body.openLine] + 2;
28159
- col = base + (body.keepRelative ? line.col - (isRoot ? 0 : body.minCol) : 0);
28160
- }
28161
- outCol.push(col);
28162
- const hardBreak = / {2,}$/.test(lines[i2]) && info[i2 + 1] ? " " : "";
28163
- result.push(" ".repeat(col) + line.text + hardBreak);
28164
- }
28165
- return result;
28166
- }
28167
- function macroTokensByLine(lines) {
28168
- const byLine = lines.map(() => []);
28169
- const starts = [];
28170
- let offset2 = 0;
28171
- for (const line2 of lines) {
28172
- starts.push(offset2);
28173
- offset2 += line2.length + 1;
28174
- }
28175
- let line = 0;
28176
- for (const token of tokenizeMarkupTolerant(lines.join("\n")).tokens) {
28177
- if (token.type !== "macro") continue;
28178
- while (line + 1 < lines.length && starts[line + 1] <= token.start) line++;
28179
- byLine[line].push({ ...token, start: token.start - starts[line], end: token.end - starts[line] });
28180
- }
28181
- return byLine;
28182
- }
28183
- function indentColumn(line) {
28184
- let col = 0;
28185
- for (const ch of line) {
28186
- if (ch === " ") col++;
28187
- else if (ch === " ") col += 4 - col % 4;
28188
- else break;
28336
+ resultLines.push(header.replace(/\s+$/, ""));
28337
+ }
28338
+ if (passage.body === "") continue;
28339
+ const body = await formatPassage(passage.body, {
28340
+ kind: classifyPassage(passage.header),
28341
+ prettier: prettier2?.prettier,
28342
+ prettierOptions: prettier2?.options,
28343
+ isBlock,
28344
+ isDedentingSubMacro,
28345
+ maxLineLength: options?.maxLineLength
28346
+ });
28347
+ resultLines.push(body.replace(/\n$/, ""));
28189
28348
  }
28190
- return col;
28349
+ return resultLines.join("\n").replace(/\n*$/, "\n");
28191
28350
  }
28192
28351
  async function formatRange(text2, _range, options) {
28193
28352
  return formatDocument(text2, options);
28194
28353
  }
28195
- function wrapLines(lines, maxLen) {
28196
- const result = [];
28197
- for (const line of lines) {
28198
- if (line.length <= maxLen) {
28199
- result.push(line);
28200
- continue;
28201
- }
28202
- const trimmed2 = line.trim();
28203
- if (trimmed2 === "" || /^::/.test(trimmed2) || /^\{[\/#.]/.test(trimmed2) || // closing/opening-only macro lines
28204
- /^\[\[/.test(trimmed2) || // links
28205
- /^\{choice\b/.test(trimmed2) || // choice macros
28206
- /^\{choices\b/.test(trimmed2)) {
28207
- result.push(line);
28208
- continue;
28209
- }
28210
- const indentMatch = line.match(/^(\s*)/);
28211
- const indent = indentMatch ? indentMatch[1] : "";
28212
- const wrapped = wordWrap(trimmed2, maxLen, indent);
28213
- if (line.endsWith(" ")) wrapped[wrapped.length - 1] += " ";
28214
- result.push(...wrapped);
28215
- }
28216
- return result;
28217
- }
28218
- function wordWrap(text2, maxLen, indent) {
28219
- const lines = [];
28220
- const inToken = new Array(text2.length).fill(false);
28221
- for (const token of tokenizeMarkupTolerant(text2).tokens) {
28222
- if (token.type === "text" || token.type === "html") continue;
28223
- inToken.fill(true, token.start, token.end);
28224
- }
28225
- const room = maxLen - indent.length;
28226
- let pos = 0;
28227
- while (pos < text2.length) {
28228
- if (text2.length - pos <= room) {
28229
- lines.push(indent + text2.slice(pos));
28230
- break;
28231
- }
28232
- let breakIdx = -1;
28233
- for (let i2 = pos + 1; i2 < text2.length && i2 - pos <= room; i2++) {
28234
- if (text2[i2] === " " && !inToken[i2]) breakIdx = i2;
28235
- }
28236
- if (breakIdx === -1) {
28237
- for (let i2 = pos + room + 1; i2 < text2.length; i2++) {
28238
- if (text2[i2] === " " && !inToken[i2]) {
28239
- breakIdx = i2;
28240
- break;
28241
- }
28242
- }
28243
- }
28244
- if (breakIdx === -1) {
28245
- lines.push(indent + text2.slice(pos));
28246
- break;
28247
- }
28248
- lines.push(indent + text2.slice(pos, breakIdx));
28249
- pos = breakIdx + 1;
28250
- }
28251
- return lines;
28252
- }
28253
28354
  function normalizePassageHeader(line) {
28254
28355
  const headerMatch = line.match(/^::\s+(.*)/);
28255
28356
  if (!headerMatch) return line;
@@ -28270,19 +28371,16 @@ function normalizePassageHeader(line) {
28270
28371
  }
28271
28372
  return `:: ${name}${suffix}`;
28272
28373
  }
28273
- var PASSAGE_HEADER_REGEX2, DEFAULT_DEDENTING, BUILTIN_CONTAINERS, LIST_ITEM_REGEX, CODE_FENCE_REGEX, formatPlugin;
28374
+ var PASSAGE_HEADER_REGEX2, DEFAULT_DEDENTING2, BUILTIN_CONTAINERS, formatPlugin;
28274
28375
  var init_format = __esm({
28275
28376
  "src/plugins/format.ts"() {
28276
28377
  "use strict";
28277
28378
  init_tooling();
28278
28379
  init_segment();
28279
28380
  init_prettier_bridge();
28280
- init_placeholders();
28281
28381
  PASSAGE_HEADER_REGEX2 = /^(::)\s+/;
28282
- DEFAULT_DEDENTING = /* @__PURE__ */ new Set(["else", "elseif", "next", "case", "default"]);
28382
+ DEFAULT_DEDENTING2 = /* @__PURE__ */ new Set(["else", "elseif", "next", "case", "default"]);
28283
28383
  BUILTIN_CONTAINERS = new Set(builtinMacros.filter((m) => m.block).map((m) => m.name.toLowerCase()));
28284
- LIST_ITEM_REGEX = /^(?:[-+*]|\d{1,9}[.)])(?:[ \t]|$)/;
28285
- CODE_FENCE_REGEX = /^(?:`{3,}|~{3,})/;
28286
28384
  formatPlugin = {
28287
28385
  id: "format",
28288
28386
  capabilities: {
@@ -28293,7 +28391,7 @@ var init_format = __esm({
28293
28391
  const formatOpts = {
28294
28392
  // Block widgets (whose body renders {@children}) are containers too
28295
28393
  isBlock: (name) => ctx.workspace.isContainer(name),
28296
- isDedentingSubMacro: (name) => DEFAULT_DEDENTING.has(name.toLowerCase())
28394
+ isDedentingSubMacro: (name) => DEFAULT_DEDENTING2.has(name.toLowerCase())
28297
28395
  };
28298
28396
  ctx.connection.onDocumentFormatting(async (params) => {
28299
28397
  const text2 = ctx.workspace.documents.getText(params.textDocument.uri);
@@ -72604,7 +72702,7 @@ var init_server4 = __esm({
72604
72702
 
72605
72703
  // src/bin.ts
72606
72704
  var args = process.argv.slice(2);
72607
- var VERSION = "0.13.3";
72705
+ var VERSION = "0.14.0";
72608
72706
  var HELP = `spindle-lsp v${VERSION}
72609
72707
  Language Server Protocol server for Spindle story format
72610
72708