@rohal12/spindle-lsp 0.15.16 → 0.15.17

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.16";
58
+ VERSION = false ? pkg.version : "0.15.17";
59
59
  }
60
60
  });
61
61
 
@@ -31737,6 +31737,13 @@ var init_text = __esm({
31737
31737
 
31738
31738
  // src/core/parsing/passage-parser.ts
31739
31739
  import { createRequire as createCompilerRequire } from "node:module";
31740
+ function encodePassageName(name) {
31741
+ if (!name || /[\r\n]/.test(name)) return void 0;
31742
+ const encoded = name.replace(/[[\]{}\\]/g, "\\$&");
31743
+ const { parseTwee } = createCompilerRequire(import.meta.url)("@rohal12/twee-ts");
31744
+ const parsed = parseTwee(`:: ${encoded}`);
31745
+ return parsed.passages.length === 1 && parsed.passages[0].name === name && !parsed.diagnostics.some((d) => d.level === "error") ? encoded : void 0;
31746
+ }
31740
31747
  function parsePassageHeader(rawLine, lineNumber) {
31741
31748
  const line = withoutCr(rawLine);
31742
31749
  const bomLength = /^\uFEFF*/.exec(line)[0].length;
@@ -33704,7 +33711,6 @@ function indexStoryDataTexts(texts) {
33704
33711
  const index = new PassageIndex();
33705
33712
  const byUri = /* @__PURE__ */ new Map();
33706
33713
  for (const text3 of texts) {
33707
- if (!text3.includes("StoryData")) continue;
33708
33714
  const uri = `story-format:${String(byUri.size).padStart(16, "0")}`;
33709
33715
  byUri.set(uri, text3);
33710
33716
  index.rebuild(uri, text3);
@@ -34502,6 +34508,8 @@ var init_diagnostic_codes = __esm({
34502
34508
  DiagnosticCode = {
34503
34509
  /** The Spindle the project targets is older than the oldest one supported. */
34504
34510
  UnsupportedSpindleVersion: "SP001",
34511
+ /** A fatal Twee source error reported by the public compiler parser. */
34512
+ TweeSyntaxError: "SP002",
34505
34513
  /** `unknown-macro`: a macro that is none of the built-in, configured or defined ones. */
34506
34514
  UndefinedMacro: "SP100",
34507
34515
  /** `unclosed-block`, `mismatched-closer`, `stray-closer` of a macro. */
@@ -34550,38 +34558,39 @@ var init_diagnostic_codes = __esm({
34550
34558
  LineTooLong: "SP500"
34551
34559
  };
34552
34560
  severityMap = {
34553
- SP001: "warning",
34554
- SP100: "warning",
34555
- SP101: "error",
34556
- SP102: "error",
34557
- SP104: "error",
34558
- SP105: "error",
34559
- SP106: "error",
34560
- SP107: "error",
34561
- SP108: "error",
34562
- SP109: "error",
34563
- SP110: "warning",
34564
- SP111: "error",
34565
- SP113: "error",
34566
- SP114: "error",
34567
- SP115: "error",
34568
- SP200: "error",
34569
- SP201: "error",
34570
- SP202: "info",
34571
- SP203: "warning",
34572
- SP205: "warning",
34573
- SP206: "warning",
34574
- SP207: "error",
34575
- SP208: "error",
34576
- SP300: "warning",
34577
- SP301: "warning",
34578
- SP302: "warning",
34579
- SP303: "hint",
34580
- SP304: "warning",
34581
- SP305: "warning",
34582
- SP400: "hint",
34583
- SP401: "hint",
34584
- SP500: "warning"
34561
+ [DiagnosticCode.UnsupportedSpindleVersion]: "warning",
34562
+ [DiagnosticCode.TweeSyntaxError]: "error",
34563
+ [DiagnosticCode.UndefinedMacro]: "warning",
34564
+ [DiagnosticCode.MalformedContainer]: "error",
34565
+ [DiagnosticCode.MalformedElement]: "error",
34566
+ [DiagnosticCode.IllegalClosingTag]: "error",
34567
+ [DiagnosticCode.UnclosedMarkup]: "error",
34568
+ [DiagnosticCode.CodeSyntaxError]: "error",
34569
+ [DiagnosticCode.InvalidChildren]: "error",
34570
+ [DiagnosticCode.ExpectedNoArguments]: "error",
34571
+ [DiagnosticCode.ParameterTypeError]: "error",
34572
+ [DiagnosticCode.ParameterWarning]: "warning",
34573
+ [DiagnosticCode.TooManyArguments]: "error",
34574
+ [DiagnosticCode.UnquotedPassageName]: "error",
34575
+ [DiagnosticCode.ChildMaxExceeded]: "error",
34576
+ [DiagnosticCode.ChildMinNotMet]: "error",
34577
+ [DiagnosticCode.UndeclaredVariable]: "error",
34578
+ [DiagnosticCode.PrimitiveFieldAccess]: "error",
34579
+ [DiagnosticCode.NoStoryVariables]: "info",
34580
+ [DiagnosticCode.UndeclaredTransient]: "warning",
34581
+ [DiagnosticCode.TemporaryAssignedInLoop]: "warning",
34582
+ [DiagnosticCode.ArrayMemberAccess]: "warning",
34583
+ [DiagnosticCode.InvalidDeclaration]: "error",
34584
+ [DiagnosticCode.ReservedVariableName]: "error",
34585
+ [DiagnosticCode.BrokenPassageLink]: "warning",
34586
+ [DiagnosticCode.WidgetArgCountMismatch]: "warning",
34587
+ [DiagnosticCode.IncludeWidgetPassage]: "warning",
34588
+ [DiagnosticCode.UnusedWidget]: "hint",
34589
+ [DiagnosticCode.LinkRuntimeMismatch]: "warning",
34590
+ [DiagnosticCode.LiteralLinkInterpolation]: "warning",
34591
+ [DiagnosticCode.DeadEndPassage]: "hint",
34592
+ [DiagnosticCode.UnreachablePassage]: "hint",
34593
+ [DiagnosticCode.LineTooLong]: "warning"
34585
34594
  };
34586
34595
  }
34587
34596
  });
@@ -34833,12 +34842,14 @@ var init_story_variables_owner = __esm({
34833
34842
  });
34834
34843
 
34835
34844
  // src/plugins/diagnostics.ts
34845
+ import { createRequire as createCompilerRequire3 } from "node:module";
34836
34846
  function computeDiagnostics(uri, workspace, options) {
34837
34847
  if (!workspace.initialized) return [];
34838
34848
  if (!workspace.isSpindleProject()) return [];
34839
34849
  const doc = workspace.markup.get(uri);
34840
- if (!doc || doc.passages.length === 0) return [];
34841
- const diagnostics = [];
34850
+ if (!doc || isMacroSource(uri)) return [];
34851
+ const diagnostics = [...tweeDiagnostics(doc)];
34852
+ if (doc.passages.length === 0) return diagnostics;
34842
34853
  const attempt = (check2) => {
34843
34854
  try {
34844
34855
  check2();
@@ -34869,6 +34880,27 @@ function computeDiagnostics(uri, workspace, options) {
34869
34880
  }
34870
34881
  return diagnostics.sort((a, b) => a.range.start.line - b.range.start.line || a.range.start.character - b.range.start.character);
34871
34882
  }
34883
+ function tweeDiagnostics(doc) {
34884
+ const kept3 = tweeReports.get(doc);
34885
+ if (kept3) return kept3;
34886
+ const lines = splitLines(doc.text);
34887
+ const indexed = new Set(doc.passages.map((p) => p.passage.headerEnd.start.line));
34888
+ if (!lines.some((line, i2) => /^\uFEFF*::/.test(line) && (!indexed.has(i2) || /[[\]{}]/.test(line)))) {
34889
+ tweeReports.set(doc, []);
34890
+ return [];
34891
+ }
34892
+ const { parseTwee } = createCompilerRequire3(import.meta.url)("@rohal12/twee-ts");
34893
+ const diagnostics = parseTwee(doc.text).diagnostics.filter((d) => d.level === "error").map((d) => {
34894
+ const line = Math.min(Math.max((d.line ?? 1) - 1, 0), lines.length - 1);
34895
+ return makeDiag(
34896
+ { start: { line, character: 0 }, end: { line, character: lines[line].length } },
34897
+ DiagnosticCode.TweeSyntaxError,
34898
+ d.message
34899
+ );
34900
+ });
34901
+ tweeReports.set(doc, diagnostics);
34902
+ return diagnostics;
34903
+ }
34872
34904
  function storyReport(workspace) {
34873
34905
  const documents = [];
34874
34906
  for (const uri of workspace.passages.getDocumentUris()) {
@@ -35405,7 +35437,7 @@ function toLspDiagnostic(d) {
35405
35437
  data: d.data
35406
35438
  };
35407
35439
  }
35408
- var reports, COMPILER_PASSAGES, MARKUP_CODES, text2, list2, VARIABLE_CODES, WILL_NOT_START, BRANCH_MACROS2, DIRECT_CHILD_MACROS, DEFERRED_CONTAINERS, SPINDLE_MACROS, diagnosticsPlugin;
35440
+ var tweeReports, reports, COMPILER_PASSAGES, MARKUP_CODES, text2, list2, VARIABLE_CODES, WILL_NOT_START, BRANCH_MACROS2, DIRECT_CHILD_MACROS, DEFERRED_CONTAINERS, SPINDLE_MACROS, diagnosticsPlugin;
35409
35441
  var init_diagnostics = __esm({
35410
35442
  "src/plugins/diagnostics.ts"() {
35411
35443
  "use strict";
@@ -35422,6 +35454,7 @@ var init_diagnostics = __esm({
35422
35454
  init_story_variables_owner();
35423
35455
  init_macro_sources();
35424
35456
  init_spindle_version();
35457
+ tweeReports = /* @__PURE__ */ new WeakMap();
35425
35458
  reports = /* @__PURE__ */ new WeakMap();
35426
35459
  COMPILER_PASSAGES = /* @__PURE__ */ new Set(["StoryTitle", "StoryData"]);
35427
35460
  MARKUP_CODES = {
@@ -80094,7 +80127,11 @@ function gateRegistrations(target, methods, enabled, nested = {}) {
80094
80127
  }
80095
80128
  if (methods.includes(prop) && typeof value === "function") {
80096
80129
  return (handler, ...rest) => Reflect.apply(value, obj, [
80097
- (...args2) => enabled() ? handler(...args2) : null,
80130
+ (...args2) => {
80131
+ const allowed = enabled();
80132
+ const answer = (yes) => yes ? handler(...args2) : null;
80133
+ return typeof allowed === "boolean" ? answer(allowed) : allowed.then(answer);
80134
+ },
80098
80135
  ...rest
80099
80136
  ]);
80100
80137
  }
@@ -81143,10 +81180,13 @@ function computeRename(uri, position, newName, workspace) {
81143
81180
  if (isReservedPassageName(newName)) {
81144
81181
  throw new RenameError(`Cannot rename to ${JSON.stringify(newName)}: it is a reserved passage name with a fixed meaning.`).at(uri, symbol2.range);
81145
81182
  }
81183
+ const encoded = encodePassageName(newName);
81146
81184
  for (const declaration of workspace.passages.getPassages(symbol2.name)) {
81147
- const problem = passageHeaderProblem(newName);
81148
- if (problem) throw new RenameError(`Cannot rename to ${JSON.stringify(newName)}: ${problem}.`).at(declaration.uri, declaration.nameRange);
81149
- addEdit(declaration.uri, declaration.nameRange, escapePassageName(newName));
81185
+ if (encoded === void 0) {
81186
+ const reason = /[\r\n]/.test(newName) ? "a passage header is a single line" : "a passage header cannot represent that name exactly";
81187
+ throw new RenameError(`Cannot rename to ${JSON.stringify(newName)}: ${reason}.`).at(declaration.uri, declaration.nameRange);
81188
+ }
81189
+ addEdit(declaration.uri, declaration.nameRange, encoded);
81150
81190
  }
81151
81191
  for (const { uri: refUri, ref: ref2, text: text3 } of refEdits) addEdit(refUri, ref2.range, text3);
81152
81192
  preserveEntryPassage(symbol2.name, newName, workspace, addEdit);
@@ -81295,12 +81335,6 @@ function preserveEntryPassage(oldName, newName, workspace, addEdit) {
81295
81335
  function isEntryField(key) {
81296
81336
  return readStoryData(JSON.stringify({ [key]: "Start" })).start === "Start";
81297
81337
  }
81298
- function passageHeaderProblem(name) {
81299
- if (name.trim() === "") return "a passage name cannot be empty";
81300
- if (/[\r\n]/.test(name)) return "a passage header is a single line, so the name cannot contain a line break";
81301
- if (name !== name.trim()) return "a passage name cannot start or end with whitespace";
81302
- return null;
81303
- }
81304
81338
  function unrepresentable(newName, where2) {
81305
81339
  return new RenameError(`Cannot rename to ${JSON.stringify(newName)}: it cannot be written inside ${where2}.`);
81306
81340
  }
@@ -81364,9 +81398,6 @@ function misreadRewrite(passage, edits, oldName, newName) {
81364
81398
  const owner = edits.filter((edit) => edit.ref.piece <= index).at(-1) ?? edits[0];
81365
81399
  return owner.ref;
81366
81400
  }
81367
- function escapePassageName(name) {
81368
- return name.replace(/[[\]{}\\]/g, "\\$&");
81369
- }
81370
81401
  function resolveSymbolAtCursor(uri, position, workspace) {
81371
81402
  const text3 = workspace.documents.getText(uri);
81372
81403
  if (text3 === void 0 || !workspace.hasPassages(uri)) return null;
@@ -82055,10 +82086,10 @@ function fixBrokenLink(uri, diag, workspace) {
82055
82086
  edits: [{ uri, ...edit }]
82056
82087
  });
82057
82088
  }
82058
- const header = `:: ${name.replace(/[\\[\]{}]/g, "\\$&")}`;
82059
- if (!workspace.passages.getPassage(name) && name.trim() === name && name !== "" && !/[\r\n]/.test(name)) {
82089
+ const encoded = encodePassageName(name);
82090
+ if (!workspace.passages.getPassage(name) && encoded !== void 0) {
82060
82091
  const edit = insertLinesAt(text3, splitLines(text3).length, `
82061
- ${header}
82092
+ :: ${encoded}
82062
82093
  `, lineEndingFor(workspace, uri));
82063
82094
  actions.push({
82064
82095
  title: `Create passage '${name}'`,
@@ -82517,6 +82548,10 @@ function startServer(args2) {
82517
82548
  let activePlugins = [];
82518
82549
  let workspaceRoot;
82519
82550
  let startupWarning;
82551
+ let completeInitialScan;
82552
+ const initialScan = new Promise((resolve8) => {
82553
+ completeInitialScan = resolve8;
82554
+ });
82520
82555
  connection.onInitialize((params) => {
82521
82556
  console.error("[spindle-lsp] onInitialize called");
82522
82557
  const initOptions = params.initializationOptions ?? {};
@@ -82544,7 +82579,10 @@ function startServer(args2) {
82544
82579
  activePlugins = loadPlugins(allPlugins, config2);
82545
82580
  console.error("[spindle-lsp] plugins loaded:", activePlugins.map((p) => p.id).join(", "));
82546
82581
  const model = workspace;
82547
- const pluginConnection = gateConnection(connection, () => model.isSpindleProject());
82582
+ const pluginConnection = gateConnection(connection, async () => {
82583
+ await initialScan;
82584
+ return model.initialized && model.isSpindleProject();
82585
+ });
82548
82586
  for (const plugin of activePlugins) {
82549
82587
  plugin.initialize({ connection: pluginConnection, workspace, config: config2 });
82550
82588
  }
@@ -82596,6 +82634,8 @@ function startServer(args2) {
82596
82634
  console.error("[spindle-lsp] workspace initialized, macros:", workspace.macros.getAllMacros().length);
82597
82635
  } catch (err) {
82598
82636
  console.error("[spindle-lsp] workspace scan failed:", err);
82637
+ } finally {
82638
+ completeInitialScan();
82599
82639
  }
82600
82640
  });
82601
82641
  }