@rohal12/spindle-lsp 0.15.16 → 0.15.18
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 +121 -57
- package/dist/bin.js.map +3 -3
- package/package.json +1 -1
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.
|
|
58
|
+
VERSION = false ? pkg.version : "0.15.18";
|
|
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,10 @@ 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",
|
|
34513
|
+
/** StoryData explicitly selects an entry passage absent from the story. */
|
|
34514
|
+
MissingEntryPassage: "SP004",
|
|
34505
34515
|
/** `unknown-macro`: a macro that is none of the built-in, configured or defined ones. */
|
|
34506
34516
|
UndefinedMacro: "SP100",
|
|
34507
34517
|
/** `unclosed-block`, `mismatched-closer`, `stray-closer` of a macro. */
|
|
@@ -34550,38 +34560,40 @@ var init_diagnostic_codes = __esm({
|
|
|
34550
34560
|
LineTooLong: "SP500"
|
|
34551
34561
|
};
|
|
34552
34562
|
severityMap = {
|
|
34553
|
-
|
|
34554
|
-
|
|
34555
|
-
|
|
34556
|
-
|
|
34557
|
-
|
|
34558
|
-
|
|
34559
|
-
|
|
34560
|
-
|
|
34561
|
-
|
|
34562
|
-
|
|
34563
|
-
|
|
34564
|
-
|
|
34565
|
-
|
|
34566
|
-
|
|
34567
|
-
|
|
34568
|
-
|
|
34569
|
-
|
|
34570
|
-
|
|
34571
|
-
|
|
34572
|
-
|
|
34573
|
-
|
|
34574
|
-
|
|
34575
|
-
|
|
34576
|
-
|
|
34577
|
-
|
|
34578
|
-
|
|
34579
|
-
|
|
34580
|
-
|
|
34581
|
-
|
|
34582
|
-
|
|
34583
|
-
|
|
34584
|
-
|
|
34563
|
+
[DiagnosticCode.UnsupportedSpindleVersion]: "warning",
|
|
34564
|
+
[DiagnosticCode.TweeSyntaxError]: "error",
|
|
34565
|
+
[DiagnosticCode.MissingEntryPassage]: "error",
|
|
34566
|
+
[DiagnosticCode.UndefinedMacro]: "warning",
|
|
34567
|
+
[DiagnosticCode.MalformedContainer]: "error",
|
|
34568
|
+
[DiagnosticCode.MalformedElement]: "error",
|
|
34569
|
+
[DiagnosticCode.IllegalClosingTag]: "error",
|
|
34570
|
+
[DiagnosticCode.UnclosedMarkup]: "error",
|
|
34571
|
+
[DiagnosticCode.CodeSyntaxError]: "error",
|
|
34572
|
+
[DiagnosticCode.InvalidChildren]: "error",
|
|
34573
|
+
[DiagnosticCode.ExpectedNoArguments]: "error",
|
|
34574
|
+
[DiagnosticCode.ParameterTypeError]: "error",
|
|
34575
|
+
[DiagnosticCode.ParameterWarning]: "warning",
|
|
34576
|
+
[DiagnosticCode.TooManyArguments]: "error",
|
|
34577
|
+
[DiagnosticCode.UnquotedPassageName]: "error",
|
|
34578
|
+
[DiagnosticCode.ChildMaxExceeded]: "error",
|
|
34579
|
+
[DiagnosticCode.ChildMinNotMet]: "error",
|
|
34580
|
+
[DiagnosticCode.UndeclaredVariable]: "error",
|
|
34581
|
+
[DiagnosticCode.PrimitiveFieldAccess]: "error",
|
|
34582
|
+
[DiagnosticCode.NoStoryVariables]: "info",
|
|
34583
|
+
[DiagnosticCode.UndeclaredTransient]: "warning",
|
|
34584
|
+
[DiagnosticCode.TemporaryAssignedInLoop]: "warning",
|
|
34585
|
+
[DiagnosticCode.ArrayMemberAccess]: "warning",
|
|
34586
|
+
[DiagnosticCode.InvalidDeclaration]: "error",
|
|
34587
|
+
[DiagnosticCode.ReservedVariableName]: "error",
|
|
34588
|
+
[DiagnosticCode.BrokenPassageLink]: "warning",
|
|
34589
|
+
[DiagnosticCode.WidgetArgCountMismatch]: "warning",
|
|
34590
|
+
[DiagnosticCode.IncludeWidgetPassage]: "warning",
|
|
34591
|
+
[DiagnosticCode.UnusedWidget]: "hint",
|
|
34592
|
+
[DiagnosticCode.LinkRuntimeMismatch]: "warning",
|
|
34593
|
+
[DiagnosticCode.LiteralLinkInterpolation]: "warning",
|
|
34594
|
+
[DiagnosticCode.DeadEndPassage]: "hint",
|
|
34595
|
+
[DiagnosticCode.UnreachablePassage]: "hint",
|
|
34596
|
+
[DiagnosticCode.LineTooLong]: "warning"
|
|
34585
34597
|
};
|
|
34586
34598
|
}
|
|
34587
34599
|
});
|
|
@@ -34833,12 +34845,14 @@ var init_story_variables_owner = __esm({
|
|
|
34833
34845
|
});
|
|
34834
34846
|
|
|
34835
34847
|
// src/plugins/diagnostics.ts
|
|
34848
|
+
import { createRequire as createCompilerRequire3 } from "node:module";
|
|
34836
34849
|
function computeDiagnostics(uri, workspace, options) {
|
|
34837
34850
|
if (!workspace.initialized) return [];
|
|
34838
34851
|
if (!workspace.isSpindleProject()) return [];
|
|
34839
34852
|
const doc = workspace.markup.get(uri);
|
|
34840
|
-
if (!doc ||
|
|
34841
|
-
const diagnostics = [];
|
|
34853
|
+
if (!doc || isMacroSource(uri)) return [];
|
|
34854
|
+
const diagnostics = [...tweeDiagnostics(doc)];
|
|
34855
|
+
if (doc.passages.length === 0) return diagnostics;
|
|
34842
34856
|
const attempt = (check2) => {
|
|
34843
34857
|
try {
|
|
34844
34858
|
check2();
|
|
@@ -34846,6 +34860,7 @@ function computeDiagnostics(uri, workspace, options) {
|
|
|
34846
34860
|
}
|
|
34847
34861
|
};
|
|
34848
34862
|
const story = storyReport(workspace);
|
|
34863
|
+
diagnostics.push(...story.metadata.get(doc) ?? []);
|
|
34849
34864
|
const effective = new Set(workspace.passages.getEffectivePassages());
|
|
34850
34865
|
for (const passage of doc.passages) {
|
|
34851
34866
|
if (!effective.has(passage.passage)) continue;
|
|
@@ -34869,6 +34884,27 @@ function computeDiagnostics(uri, workspace, options) {
|
|
|
34869
34884
|
}
|
|
34870
34885
|
return diagnostics.sort((a, b) => a.range.start.line - b.range.start.line || a.range.start.character - b.range.start.character);
|
|
34871
34886
|
}
|
|
34887
|
+
function tweeDiagnostics(doc) {
|
|
34888
|
+
const kept3 = tweeReports.get(doc);
|
|
34889
|
+
if (kept3) return kept3;
|
|
34890
|
+
const lines = splitLines(doc.text);
|
|
34891
|
+
const indexed = new Set(doc.passages.map((p) => p.passage.headerEnd.start.line));
|
|
34892
|
+
if (!lines.some((line, i2) => /^\uFEFF*::/.test(line) && (!indexed.has(i2) || /[[\]{}]/.test(line)))) {
|
|
34893
|
+
tweeReports.set(doc, []);
|
|
34894
|
+
return [];
|
|
34895
|
+
}
|
|
34896
|
+
const { parseTwee } = createCompilerRequire3(import.meta.url)("@rohal12/twee-ts");
|
|
34897
|
+
const diagnostics = parseTwee(doc.text).diagnostics.filter((d) => d.level === "error").map((d) => {
|
|
34898
|
+
const line = Math.min(Math.max((d.line ?? 1) - 1, 0), lines.length - 1);
|
|
34899
|
+
return makeDiag(
|
|
34900
|
+
{ start: { line, character: 0 }, end: { line, character: lines[line].length } },
|
|
34901
|
+
DiagnosticCode.TweeSyntaxError,
|
|
34902
|
+
d.message
|
|
34903
|
+
);
|
|
34904
|
+
});
|
|
34905
|
+
tweeReports.set(doc, diagnostics);
|
|
34906
|
+
return diagnostics;
|
|
34907
|
+
}
|
|
34872
34908
|
function storyReport(workspace) {
|
|
34873
34909
|
const documents = [];
|
|
34874
34910
|
for (const uri of workspace.passages.getDocumentUris()) {
|
|
@@ -34930,6 +34966,7 @@ function buildReport(documents, workspace) {
|
|
|
34930
34966
|
}
|
|
34931
34967
|
return {
|
|
34932
34968
|
documents,
|
|
34969
|
+
metadata: storyDataDiagnostics(documents, workspace),
|
|
34933
34970
|
passages,
|
|
34934
34971
|
macros,
|
|
34935
34972
|
markup,
|
|
@@ -34941,6 +34978,22 @@ function buildReport(documents, workspace) {
|
|
|
34941
34978
|
variableDiagnostics: /* @__PURE__ */ new Map()
|
|
34942
34979
|
};
|
|
34943
34980
|
}
|
|
34981
|
+
function storyDataDiagnostics(documents, workspace) {
|
|
34982
|
+
const found = /* @__PURE__ */ new Map();
|
|
34983
|
+
const effective = workspace.passages.getEffectivePassage("StoryData");
|
|
34984
|
+
for (const doc of documents) {
|
|
34985
|
+
const diagnostics = [];
|
|
34986
|
+
for (const passage of doc.passages) {
|
|
34987
|
+
if (passage.passage !== effective) continue;
|
|
34988
|
+
const data2 = readStoryData(passage.content);
|
|
34989
|
+
if (data2.start && !workspace.passages.getEffectivePassage(data2.start)) {
|
|
34990
|
+
diagnostics.push(makeDiag(passage.passage.nameRange, DiagnosticCode.MissingEntryPassage, `Starting passage "${data2.start}" not found.`));
|
|
34991
|
+
}
|
|
34992
|
+
}
|
|
34993
|
+
found.set(doc, diagnostics);
|
|
34994
|
+
}
|
|
34995
|
+
return found;
|
|
34996
|
+
}
|
|
34944
34997
|
function declarationsOf(workspace, name) {
|
|
34945
34998
|
const passage = name === "StoryVariables" ? workspace.passages.getStoryVariables() : workspace.passages.getStoryTransients();
|
|
34946
34999
|
if (!passage || !isRuntimePassage(passage)) return void 0;
|
|
@@ -35148,7 +35201,7 @@ function temporaryRefRegex(name) {
|
|
|
35148
35201
|
return new RegExp(`(?<![.\\w$@%])_(${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})(?![\\w$])`, "g");
|
|
35149
35202
|
}
|
|
35150
35203
|
function maskLiterals(code) {
|
|
35151
|
-
const chars =
|
|
35204
|
+
const chars = code.split("").map((ch) => ch === "\n" ? "\n" : " ");
|
|
35152
35205
|
const copy = (text3, index) => {
|
|
35153
35206
|
for (let i2 = 0; i2 < text3.length; i2++) chars[index + i2] = text3[i2];
|
|
35154
35207
|
};
|
|
@@ -35218,13 +35271,15 @@ function loopDiagnostics(passage, token, ancestors, workspace) {
|
|
|
35218
35271
|
const close = loop?.node.body?.close;
|
|
35219
35272
|
if (!loop || !close) return [];
|
|
35220
35273
|
const targets = macro === "computed" ? computedTemporaryTarget(token.rawArgs) : setTemporaryTargets(token.rawArgs);
|
|
35274
|
+
const view = sourceView(passage, token.argsStart, token.argsEnd);
|
|
35275
|
+
const argsIndex = viewIndex(view, token.argsStart);
|
|
35221
35276
|
const bodyStart = loop.node.token.end;
|
|
35222
35277
|
const diagnostics = [];
|
|
35223
35278
|
for (const target of targets) {
|
|
35224
35279
|
if (!isReadInBody(passage, bodyStart, close.start, target.name, token.start, token.end)) continue;
|
|
35225
|
-
const start =
|
|
35280
|
+
const start = argsIndex + target.offset;
|
|
35226
35281
|
diagnostics.push(makeDiag(
|
|
35227
|
-
passage.range(start, start + 1 + target.name.length),
|
|
35282
|
+
passage.range(pieceOffset(view, start), pieceOffset(view, start + 1 + target.name.length)),
|
|
35228
35283
|
DiagnosticCode.TemporaryAssignedInLoop,
|
|
35229
35284
|
`Temporary '_${target.name}' is assigned inside {${loop.name}} and read in the loop body. Temporaries are shared by every iteration, so all iterations end up seeing the value assigned by the last one. Use the iteration-local '@${target.name}' instead, or assign it once before the loop if the value does not depend on the iteration.`
|
|
35230
35285
|
));
|
|
@@ -35405,7 +35460,7 @@ function toLspDiagnostic(d) {
|
|
|
35405
35460
|
data: d.data
|
|
35406
35461
|
};
|
|
35407
35462
|
}
|
|
35408
|
-
var reports, COMPILER_PASSAGES, MARKUP_CODES, text2, list2, VARIABLE_CODES, WILL_NOT_START, BRANCH_MACROS2, DIRECT_CHILD_MACROS, DEFERRED_CONTAINERS, SPINDLE_MACROS, diagnosticsPlugin;
|
|
35463
|
+
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
35464
|
var init_diagnostics = __esm({
|
|
35410
35465
|
"src/plugins/diagnostics.ts"() {
|
|
35411
35466
|
"use strict";
|
|
@@ -35420,8 +35475,10 @@ var init_diagnostics = __esm({
|
|
|
35420
35475
|
init_link_parser();
|
|
35421
35476
|
init_passage_parser();
|
|
35422
35477
|
init_story_variables_owner();
|
|
35478
|
+
init_story_data();
|
|
35423
35479
|
init_macro_sources();
|
|
35424
35480
|
init_spindle_version();
|
|
35481
|
+
tweeReports = /* @__PURE__ */ new WeakMap();
|
|
35425
35482
|
reports = /* @__PURE__ */ new WeakMap();
|
|
35426
35483
|
COMPILER_PASSAGES = /* @__PURE__ */ new Set(["StoryTitle", "StoryData"]);
|
|
35427
35484
|
MARKUP_CODES = {
|
|
@@ -80094,7 +80151,11 @@ function gateRegistrations(target, methods, enabled, nested = {}) {
|
|
|
80094
80151
|
}
|
|
80095
80152
|
if (methods.includes(prop) && typeof value === "function") {
|
|
80096
80153
|
return (handler, ...rest) => Reflect.apply(value, obj, [
|
|
80097
|
-
(...args2) =>
|
|
80154
|
+
(...args2) => {
|
|
80155
|
+
const allowed = enabled();
|
|
80156
|
+
const answer = (yes) => yes ? handler(...args2) : null;
|
|
80157
|
+
return typeof allowed === "boolean" ? answer(allowed) : allowed.then(answer);
|
|
80158
|
+
},
|
|
80098
80159
|
...rest
|
|
80099
80160
|
]);
|
|
80100
80161
|
}
|
|
@@ -81143,10 +81204,13 @@ function computeRename(uri, position, newName, workspace) {
|
|
|
81143
81204
|
if (isReservedPassageName(newName)) {
|
|
81144
81205
|
throw new RenameError(`Cannot rename to ${JSON.stringify(newName)}: it is a reserved passage name with a fixed meaning.`).at(uri, symbol2.range);
|
|
81145
81206
|
}
|
|
81207
|
+
const encoded = encodePassageName(newName);
|
|
81146
81208
|
for (const declaration of workspace.passages.getPassages(symbol2.name)) {
|
|
81147
|
-
|
|
81148
|
-
|
|
81149
|
-
|
|
81209
|
+
if (encoded === void 0) {
|
|
81210
|
+
const reason = /[\r\n]/.test(newName) ? "a passage header is a single line" : "a passage header cannot represent that name exactly";
|
|
81211
|
+
throw new RenameError(`Cannot rename to ${JSON.stringify(newName)}: ${reason}.`).at(declaration.uri, declaration.nameRange);
|
|
81212
|
+
}
|
|
81213
|
+
addEdit(declaration.uri, declaration.nameRange, encoded);
|
|
81150
81214
|
}
|
|
81151
81215
|
for (const { uri: refUri, ref: ref2, text: text3 } of refEdits) addEdit(refUri, ref2.range, text3);
|
|
81152
81216
|
preserveEntryPassage(symbol2.name, newName, workspace, addEdit);
|
|
@@ -81295,12 +81359,6 @@ function preserveEntryPassage(oldName, newName, workspace, addEdit) {
|
|
|
81295
81359
|
function isEntryField(key) {
|
|
81296
81360
|
return readStoryData(JSON.stringify({ [key]: "Start" })).start === "Start";
|
|
81297
81361
|
}
|
|
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
81362
|
function unrepresentable(newName, where2) {
|
|
81305
81363
|
return new RenameError(`Cannot rename to ${JSON.stringify(newName)}: it cannot be written inside ${where2}.`);
|
|
81306
81364
|
}
|
|
@@ -81364,9 +81422,6 @@ function misreadRewrite(passage, edits, oldName, newName) {
|
|
|
81364
81422
|
const owner = edits.filter((edit) => edit.ref.piece <= index).at(-1) ?? edits[0];
|
|
81365
81423
|
return owner.ref;
|
|
81366
81424
|
}
|
|
81367
|
-
function escapePassageName(name) {
|
|
81368
|
-
return name.replace(/[[\]{}\\]/g, "\\$&");
|
|
81369
|
-
}
|
|
81370
81425
|
function resolveSymbolAtCursor(uri, position, workspace) {
|
|
81371
81426
|
const text3 = workspace.documents.getText(uri);
|
|
81372
81427
|
if (text3 === void 0 || !workspace.hasPassages(uri)) return null;
|
|
@@ -82055,10 +82110,10 @@ function fixBrokenLink(uri, diag, workspace) {
|
|
|
82055
82110
|
edits: [{ uri, ...edit }]
|
|
82056
82111
|
});
|
|
82057
82112
|
}
|
|
82058
|
-
const
|
|
82059
|
-
if (!workspace.passages.getPassage(name) &&
|
|
82113
|
+
const encoded = encodePassageName(name);
|
|
82114
|
+
if (!workspace.passages.getPassage(name) && encoded !== void 0) {
|
|
82060
82115
|
const edit = insertLinesAt(text3, splitLines(text3).length, `
|
|
82061
|
-
${
|
|
82116
|
+
:: ${encoded}
|
|
82062
82117
|
`, lineEndingFor(workspace, uri));
|
|
82063
82118
|
actions.push({
|
|
82064
82119
|
title: `Create passage '${name}'`,
|
|
@@ -82517,6 +82572,10 @@ function startServer(args2) {
|
|
|
82517
82572
|
let activePlugins = [];
|
|
82518
82573
|
let workspaceRoot;
|
|
82519
82574
|
let startupWarning;
|
|
82575
|
+
let completeInitialScan;
|
|
82576
|
+
const initialScan = new Promise((resolve8) => {
|
|
82577
|
+
completeInitialScan = resolve8;
|
|
82578
|
+
});
|
|
82520
82579
|
connection.onInitialize((params) => {
|
|
82521
82580
|
console.error("[spindle-lsp] onInitialize called");
|
|
82522
82581
|
const initOptions = params.initializationOptions ?? {};
|
|
@@ -82544,7 +82603,10 @@ function startServer(args2) {
|
|
|
82544
82603
|
activePlugins = loadPlugins(allPlugins, config2);
|
|
82545
82604
|
console.error("[spindle-lsp] plugins loaded:", activePlugins.map((p) => p.id).join(", "));
|
|
82546
82605
|
const model = workspace;
|
|
82547
|
-
const pluginConnection = gateConnection(connection, () =>
|
|
82606
|
+
const pluginConnection = gateConnection(connection, async () => {
|
|
82607
|
+
await initialScan;
|
|
82608
|
+
return model.initialized && model.isSpindleProject();
|
|
82609
|
+
});
|
|
82548
82610
|
for (const plugin of activePlugins) {
|
|
82549
82611
|
plugin.initialize({ connection: pluginConnection, workspace, config: config2 });
|
|
82550
82612
|
}
|
|
@@ -82596,6 +82658,8 @@ function startServer(args2) {
|
|
|
82596
82658
|
console.error("[spindle-lsp] workspace initialized, macros:", workspace.macros.getAllMacros().length);
|
|
82597
82659
|
} catch (err) {
|
|
82598
82660
|
console.error("[spindle-lsp] workspace scan failed:", err);
|
|
82661
|
+
} finally {
|
|
82662
|
+
completeInitialScan();
|
|
82599
82663
|
}
|
|
82600
82664
|
});
|
|
82601
82665
|
}
|