@rohal12/spindle-lsp 0.15.10 → 0.15.12

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.10";
58
+ VERSION = false ? pkg.version : "0.15.12";
59
59
  }
60
60
  });
61
61
 
@@ -31258,7 +31258,7 @@ function parsePassageHeader(rawLine, lineNumber) {
31258
31258
  const first = delimiter.exec(escaped);
31259
31259
  const boundary = first?.index ?? line.length;
31260
31260
  const nameEnd = escaped.slice(0, boundary).trimEnd().length;
31261
- const name = line.slice(nameStart, nameEnd).replace(/\\(.)/g, "$1").trim();
31261
+ const name = line.slice(nameStart, nameEnd).replace(/\\(.|$)/g, "$1").trim();
31262
31262
  if (!name) return null;
31263
31263
  let rest = boundary;
31264
31264
  let tags = [];
@@ -31306,7 +31306,7 @@ function isScriptOrStylesheetPassage(passage) {
31306
31306
  return passage.tags?.some((t) => CODE_PASSAGE_TAGS.has(t)) ?? false;
31307
31307
  }
31308
31308
  function isRuntimePassage(passage) {
31309
- return passage.name !== "StoryTitle" && passage.name !== "StoryData" && !isScriptOrStylesheetPassage(passage) && !passage.tags?.includes("Twine.private");
31309
+ return !passage.runtimeOmitted && passage.name !== "StoryTitle" && passage.name !== "StoryData" && !isScriptOrStylesheetPassage(passage) && !passage.tags?.includes("Twine.private");
31310
31310
  }
31311
31311
  function isMarkupPassage(passage) {
31312
31312
  return isRuntimePassage(passage) && !(passage.name !== void 0 && NON_MARKUP_PASSAGES.has(passage.name));
@@ -31355,6 +31355,16 @@ var init_passage_parser = __esm({
31355
31355
  });
31356
31356
 
31357
31357
  // src/core/workspace/passage-index.ts
31358
+ import { createRequire as createSettingsRequire } from "node:module";
31359
+ function compareSourceUris(left, right) {
31360
+ const a = left.split("/").map(decodeURIComponent);
31361
+ const b = right.split("/").map(decodeURIComponent);
31362
+ for (let i2 = 0; i2 < Math.min(a.length, b.length); i2++) {
31363
+ const order = Buffer.compare(Buffer.from(a[i2], "utf8"), Buffer.from(b[i2], "utf8"));
31364
+ if (order !== 0) return order;
31365
+ }
31366
+ return a.length - b.length;
31367
+ }
31358
31368
  var PassageIndex;
31359
31369
  var init_passage_index = __esm({
31360
31370
  "src/core/workspace/passage-index.ts"() {
@@ -31402,6 +31412,12 @@ var init_passage_index = __esm({
31402
31412
  start: passages[i2].range.start,
31403
31413
  end: { line: nextStart, character: lines[nextStart]?.length ?? 0 }
31404
31414
  };
31415
+ if (passages[i2].name === "StorySettings") {
31416
+ const { StoryBuilder } = createSettingsRequire(import.meta.url)("@rohal12/twee-ts");
31417
+ const builder = new StoryBuilder();
31418
+ builder.add({ name: "StorySettings", tags: [], text: lines.slice(passages[i2].headerEnd.end.line + 1, nextStart + 1).join("\n") }, []);
31419
+ passages[i2].runtimeOmitted = builder.build().twine1.settings.size === 0;
31420
+ }
31405
31421
  }
31406
31422
  if (passages.length > 0) {
31407
31423
  this.byUri.set(uri, passages);
@@ -31436,6 +31452,10 @@ var init_passage_index = __esm({
31436
31452
  }
31437
31453
  return found;
31438
31454
  }
31455
+ /** The compiler replaces duplicate passages with the later source declaration. */
31456
+ getEffectivePassage(name) {
31457
+ return this.getPassages(name).sort((a, b) => compareSourceUris(a.uri, b.uri)).at(-1);
31458
+ }
31439
31459
  /** Get the passage containing a specific line in a document. */
31440
31460
  getPassageAt(uri, line) {
31441
31461
  const passages = this.byUri.get(uri);
@@ -31468,11 +31488,11 @@ var init_passage_index = __esm({
31468
31488
  }
31469
31489
  /** Get the StoryVariables passage, if any. */
31470
31490
  getStoryVariables() {
31471
- return this.getPassage("StoryVariables");
31491
+ return this.getEffectivePassage("StoryVariables");
31472
31492
  }
31473
31493
  /** Get the StoryTransients passage, if any. */
31474
31494
  getStoryTransients() {
31475
- return this.getPassage("StoryTransients");
31495
+ return this.getEffectivePassage("StoryTransients");
31476
31496
  }
31477
31497
  /** Get the StoryInit passage, if any. */
31478
31498
  getStoryInit() {
@@ -32297,6 +32317,10 @@ var init_macro_registry2 = __esm({
32297
32317
  hasConfiguredParameters(name) {
32298
32318
  return this.configEntries.get(name.toLowerCase())?.parameters !== void 0;
32299
32319
  }
32320
+ /** Whether a project definition replaces the original runtime macro. */
32321
+ hasDiscoveredMacro(name) {
32322
+ return this.discovered.has(name.toLowerCase());
32323
+ }
32300
32324
  /** Check if a macro is a block (container) macro. */
32301
32325
  isBlock(name) {
32302
32326
  return this.getMacro(name)?.block ?? false;
@@ -33363,11 +33387,10 @@ var init_macro_supplements = __esm({
33363
33387
  button: {
33364
33388
  name: "button",
33365
33389
  container: true,
33366
- description: 'Creates a button that executes its contents when clicked.\n\nUsage:\n\n```\n{button "text" "passage"}\n ...\n{/button}\n```',
33367
- parameters: ["text |+ text", ""],
33390
+ description: 'Creates a button that executes its contents when clicked. Navigate by placing {goto} in its body.\n\nUsage:\n\n```\n{button "Go"}\n {goto "Hall"}\n{/button}\n```',
33391
+ parameters: ["text"],
33368
33392
  parameterDocs: [
33369
- { name: "label", documentation: "Text of the button." },
33370
- { name: "passage", documentation: "Passage to go to when clicked; omit to run the contents." }
33393
+ { name: "label", documentation: "Text of the button; its body runs when clicked." }
33371
33394
  ]
33372
33395
  },
33373
33396
  timed: {
@@ -33448,7 +33471,7 @@ var init_macro_supplements = __esm({
33448
33471
  },
33449
33472
  meter: {
33450
33473
  name: "meter",
33451
- description: "Displays a progress meter.\n\nUsage: `{meter $var min max}`",
33474
+ description: 'Displays the current value relative to a maximum, with an optional quoted label.\n\nUsage: `{meter 5 10 "%"}`',
33452
33475
  parameters: ["...text"],
33453
33476
  parameterDocs: [
33454
33477
  { name: "current", documentation: 'Current value expression; a maximum expression and an optional label mode (`"%"`, `"none"` or a unit) follow.' }
@@ -33595,27 +33618,19 @@ var init_macro_supplements = __esm({
33595
33618
  },
33596
33619
  watch: {
33597
33620
  name: "watch",
33598
- description: "Watches a variable for changes and triggers a re-render.\n\nUsage: `{watch $var}`",
33621
+ description: 'Watches a quoted condition and runs an action when it changes from false to true. Use name to allow removal with {unwatch}.\n\nUsage: `{watch "$x >= 2" run "$x = 0" name "reset-x"}`',
33599
33622
  parameters: ["...text"],
33600
33623
  parameterDocs: [
33601
- { name: "variable", documentation: "Variable to watch for changes." }
33624
+ { name: "condition", documentation: "Quoted expression checked when variables change or the story navigates." },
33625
+ { name: "options", documentation: "Action (goto, dialog or run), optional name, priority and once flag." }
33602
33626
  ]
33603
33627
  },
33604
33628
  unwatch: {
33605
33629
  name: "unwatch",
33606
- description: "Stops watching a variable.\n\nUsage: `{unwatch $var}`",
33630
+ description: 'Removes a watcher by its registered name.\n\nUsage: `{unwatch "reset-x"}`',
33607
33631
  parameters: ["...text"],
33608
33632
  parameterDocs: [
33609
- { name: "variable", documentation: "Variable to stop watching." }
33610
- ]
33611
- },
33612
- unless: {
33613
- name: "unless",
33614
- container: true,
33615
- description: "Conditionally displays its contents when the expression is falsy. The inverse of `{if}`.\n\nUsage:\n\n```\n{unless condition}\n ...\n{/unless}\n```",
33616
- parameters: ["...text"],
33617
- parameterDocs: [
33618
- { name: "condition", documentation: "Expression; the contents show when it is falsy." }
33633
+ { name: "name", documentation: "Name supplied to the watch macro." }
33619
33634
  ]
33620
33635
  }
33621
33636
  };
@@ -33641,6 +33656,7 @@ var init_workspace_model = __esm({
33641
33656
  init_macro_sources();
33642
33657
  init_story_format();
33643
33658
  init_spindle_version();
33659
+ init_passage_parser();
33644
33660
  init_macro_supplements();
33645
33661
  WorkspaceModel = class extends EventEmitter2 {
33646
33662
  documents;
@@ -33869,7 +33885,8 @@ var init_workspace_model = __esm({
33869
33885
  }
33870
33886
  let lines;
33871
33887
  for (const passage of this.passages.getPassagesInDocument(uri)) {
33872
- const isStoryInit = passage.name === "StoryInit";
33888
+ if (passage.tags?.includes("Twine.private")) continue;
33889
+ const isStoryInit = passage.name === "StoryInit" && isRuntimePassage(passage);
33873
33890
  const isScript = passage.tags?.includes("script") ?? false;
33874
33891
  if (!isStoryInit && !isScript) continue;
33875
33892
  lines ??= text3.split("\n");
@@ -33894,8 +33911,8 @@ var init_workspace_model = __esm({
33894
33911
  this.declaredVersion = storyDataFormatVersion(this.passages.getAllPassages(), (uri) => this.documents.getText(uri));
33895
33912
  this._target = resolveSpindleTarget(this.installedVersion, this.declaredVersion);
33896
33913
  const declarations = (name) => {
33897
- const passage = this.passages.getPassage(name);
33898
- return passage ? this.markup.get(passage.uri)?.passages.find((p) => p.passage === passage) : void 0;
33914
+ const passage = this.passages.getEffectivePassage(name);
33915
+ return passage && isRuntimePassage(passage) ? this.markup.get(passage.uri)?.passages.find((p) => p.passage === passage) : void 0;
33899
33916
  };
33900
33917
  const variables = declarations("StoryVariables");
33901
33918
  if (variables) this.variables.parseStoryVariables(variables.content, variables.passage.headerEnd.end.line + 1, variables.doc.uri);
@@ -34201,6 +34218,7 @@ var init_passage_refs = __esm({
34201
34218
 
34202
34219
  // src/core/parsing/link-parser.ts
34203
34220
  function markupOfText(text3) {
34221
+ if (typeof text3 !== "string") return text3;
34204
34222
  if (lastMarkup?.text === text3) return lastMarkup;
34205
34223
  const markup = readText2(text3);
34206
34224
  lastMarkup = markup;
@@ -34224,7 +34242,7 @@ function readText2(text3) {
34224
34242
  return new DocumentMarkup(TEXT_URI, text3, passages, TEXT_CONTEXT);
34225
34243
  }
34226
34244
  function findLinkRuntimeMismatches(text3) {
34227
- if (!text3.includes("[[")) return [];
34245
+ if (!(typeof text3 === "string" ? text3 : text3.text).includes("[[")) return [];
34228
34246
  const found = [];
34229
34247
  for (const ref2 of documentPassageRefs(markupOfText(text3))) {
34230
34248
  if (ref2.form !== "bracket") continue;
@@ -34384,7 +34402,7 @@ function buildReport(documents, workspace) {
34384
34402
  }
34385
34403
  function declarationsOf(workspace, name) {
34386
34404
  const passage = name === "StoryVariables" ? workspace.passages.getStoryVariables() : workspace.passages.getStoryTransients();
34387
- if (!passage) return void 0;
34405
+ if (!passage || !isRuntimePassage(passage)) return void 0;
34388
34406
  const markup = workspace.markup.get(passage.uri)?.passages.find((candidate) => candidate.passage === passage);
34389
34407
  if (!markup) return void 0;
34390
34408
  return new Map(markup.declarations.declarations.map((declaration) => [declaration.name, declaration.schema]));
@@ -34517,7 +34535,7 @@ function structureDiagnostics(passage, workspace) {
34517
34535
  if (token.type === "macro" && !token.isClose) {
34518
34536
  diagnostics.push(...parentDiagnostics(passage, token, ancestors, workspace));
34519
34537
  diagnostics.push(...childDiagnostics(passage, node, token, workspace));
34520
- diagnostics.push(...loopDiagnostics(passage, token, ancestors));
34538
+ diagnostics.push(...loopDiagnostics(passage, token, ancestors, workspace));
34521
34539
  }
34522
34540
  if (!node.body) continue;
34523
34541
  const inside = [...ancestors, {
@@ -34643,13 +34661,13 @@ function statementEnd(code, from) {
34643
34661
  }
34644
34662
  return code.length;
34645
34663
  }
34646
- function loopDiagnostics(passage, token, ancestors) {
34664
+ function loopDiagnostics(passage, token, ancestors, workspace) {
34647
34665
  const macro = token.name.toLowerCase();
34648
- if (macro !== "computed" && macro !== "set" || !token.rawArgs) return [];
34666
+ if (macro !== "computed" && macro !== "set" || !token.rawArgs || workspace.macros.getMacro(macro)?.source !== "builtin") return [];
34649
34667
  let loop;
34650
34668
  for (let i2 = ancestors.length - 1; i2 >= 0; i2--) {
34651
34669
  const ancestor = ancestors[i2];
34652
- if (ancestor.element) continue;
34670
+ if (ancestor.element || workspace.macros.getMacro(ancestor.name)?.source !== "builtin") continue;
34653
34671
  if (DEFERRED_CONTAINERS.has(ancestor.name)) return [];
34654
34672
  if (ancestor.name === "for") {
34655
34673
  loop = ancestor;
@@ -34694,7 +34712,8 @@ function argumentDiagnostics(passage, workspace) {
34694
34712
  for (const token of passage.isMarkup ? macroTokens(passage) : []) {
34695
34713
  if (token.type !== "macro" || token.isClose) continue;
34696
34714
  const info = workspace.macros.getMacro(token.name);
34697
- if (!info?.parameters || info.skipArgs || SPINDLE_MACROS.has(token.name.toLowerCase())) continue;
34715
+ if (!info?.parameters || info.skipArgs || info.parameterDefs?.length) continue;
34716
+ if (SPINDLE_MACROS.has(token.name.toLowerCase()) && !workspace.macros.hasDiscoveredMacro(token.name)) continue;
34698
34717
  let params;
34699
34718
  try {
34700
34719
  params = new Parameters(info.parameters);
@@ -34777,14 +34796,14 @@ function describeNavigation(passage) {
34777
34796
  }
34778
34797
  function linkDiagnostics(doc) {
34779
34798
  const diagnostics = [];
34780
- for (const link of findLinkRuntimeMismatches(doc.text)) {
34799
+ for (const link of findLinkRuntimeMismatches(doc)) {
34781
34800
  diagnostics.push(makeDiag(
34782
34801
  link.range,
34783
34802
  DiagnosticCode.LinkRuntimeMismatch,
34784
34803
  `Spindle reads this link differently from how it is written: the link macro reads the label as ${JSON.stringify(link.runtime.display)} and a click ${describeNavigation(link.runtime.passage)}, not to ${JSON.stringify(link.target)}.`
34785
34804
  ));
34786
34805
  }
34787
- for (const found of findLiteralLinkInterpolations(doc.text)) {
34806
+ for (const found of findLiteralLinkInterpolations(doc)) {
34788
34807
  diagnostics.push(makeDiag(
34789
34808
  found.range,
34790
34809
  DiagnosticCode.LiteralLinkInterpolation,
@@ -79927,7 +79946,7 @@ function linkable(name) {
79927
79946
  return tokens2.length === 1 && link.type === "link" && link.target === name && !bracketLinkMismatch("label", name);
79928
79947
  }
79929
79948
  function getPassageNameCompletions(workspace) {
79930
- return workspace.passages.getAllPassages().filter((p) => linkable(p.name)).map((p) => ({
79949
+ return workspace.passages.getAllPassages().filter((p) => isRuntimePassage(p) && linkable(p.name)).map((p) => ({
79931
79950
  label: p.name,
79932
79951
  kind: 18,
79933
79952
  // CompletionItemKind.Reference
@@ -79942,6 +79961,7 @@ var init_completions = __esm({
79942
79961
  init_widgets();
79943
79962
  init_passage_refs();
79944
79963
  init_link_runtime();
79964
+ init_passage_parser();
79945
79965
  init_markup_cursor();
79946
79966
  init_markup_symbols();
79947
79967
  VARIABLE_DETAIL = { $: "story variable", "%": "transient variable", _: "temporary variable", "@": "local/parameter variable" };
@@ -81388,7 +81408,7 @@ function declareVariable(diag, workspace, sigil) {
81388
81408
  if (diag.data?.kind !== "undeclared-variable" || diag.data.sigil !== sigil) return [];
81389
81409
  const { name } = diag.data;
81390
81410
  const passage = sigil === "$" ? workspace.passages.getStoryVariables() : workspace.passages.getStoryTransients();
81391
- if (!passage) return [];
81411
+ if (!passage || !isRuntimePassage(passage)) return [];
81392
81412
  const text3 = workspace.documents.getText(passage.uri);
81393
81413
  if (text3 === void 0) return [];
81394
81414
  const markup = workspace.markup.get(passage.uri)?.passages.find((p) => p.passage === passage);
@@ -81404,6 +81424,7 @@ function declareVariable(diag, workspace, sigil) {
81404
81424
  }];
81405
81425
  }
81406
81426
  function fixNoStoryVariables(workspace) {
81427
+ if (workspace.passages.getStoryVariables()) return null;
81407
81428
  const targetUri = missingStoryVariablesOwner(workspace);
81408
81429
  if (targetUri === void 0) return null;
81409
81430
  const text3 = workspace.documents.getText(targetUri);
@@ -81454,7 +81475,7 @@ function fixBrokenLink(uri, diag, workspace) {
81454
81475
  });
81455
81476
  }
81456
81477
  const header = `:: ${name.replace(/[\\[\]{}]/g, "\\$&")}`;
81457
- if (name.trim() === name && name !== "" && !/[\r\n]/.test(name)) {
81478
+ if (!workspace.passages.getPassage(name) && name.trim() === name && name !== "" && !/[\r\n]/.test(name)) {
81458
81479
  const edit = insertLinesAt(text3, text3.split("\n").length, `
81459
81480
  ${header}
81460
81481
  `, lineEndingFor(workspace, uri));
@@ -81523,6 +81544,7 @@ var init_code_actions = __esm({
81523
81544
  init_rename();
81524
81545
  init_js_string_literal();
81525
81546
  init_text();
81547
+ init_passage_parser();
81526
81548
  DEFAULT_CONFIG_FILENAME = "spindle.config.yaml";
81527
81549
  NEW_MACRO_ENTRY = { description: "" };
81528
81550
  fixUndeclaredVariable = (diag, workspace) => declareVariable(diag, workspace, "$");