@rohal12/spindle-lsp 0.9.0 → 0.9.1
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 +1088 -436
- package/dist/bin.js.map +4 -4
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -6303,11 +6303,15 @@ function parsePassageHeader(line, lineNumber) {
|
|
|
6303
6303
|
nameRange
|
|
6304
6304
|
};
|
|
6305
6305
|
}
|
|
6306
|
-
|
|
6306
|
+
function isScriptOrStylesheetPassage(passage) {
|
|
6307
|
+
return passage.tags?.some((t) => CODE_PASSAGE_TAGS.has(t)) ?? false;
|
|
6308
|
+
}
|
|
6309
|
+
var passageHeaderRegex, CODE_PASSAGE_TAGS;
|
|
6307
6310
|
var init_passage_parser = __esm({
|
|
6308
6311
|
"src/core/parsing/passage-parser.ts"() {
|
|
6309
6312
|
"use strict";
|
|
6310
6313
|
passageHeaderRegex = /^(::\s+)(.*?)(\[.*?\]\s*)?(\{.*?\}\s*)?\r?$/;
|
|
6314
|
+
CODE_PASSAGE_TAGS = /* @__PURE__ */ new Set(["script", "stylesheet"]);
|
|
6311
6315
|
}
|
|
6312
6316
|
});
|
|
6313
6317
|
|
|
@@ -6339,6 +6343,7 @@ var init_passage_index = __esm({
|
|
|
6339
6343
|
// will be extended below
|
|
6340
6344
|
},
|
|
6341
6345
|
headerEnd: header.headerRange,
|
|
6346
|
+
nameRange: header.nameRange,
|
|
6342
6347
|
uri,
|
|
6343
6348
|
tags: header.tags.length > 0 ? header.tags : void 0,
|
|
6344
6349
|
meta: header.meta
|
|
@@ -6443,6 +6448,12 @@ var init_macro_registry = __esm({
|
|
|
6443
6448
|
"use strict";
|
|
6444
6449
|
MacroRegistry = class {
|
|
6445
6450
|
macros = /* @__PURE__ */ new Map();
|
|
6451
|
+
/** Entries as they were before the current user config was applied (undefined = absent). */
|
|
6452
|
+
configSnapshot = /* @__PURE__ */ new Map();
|
|
6453
|
+
/** Tier 3: macros discovered from Story.defineMacro() calls, keyed by lowercase name. */
|
|
6454
|
+
discovered = /* @__PURE__ */ new Map();
|
|
6455
|
+
/** Tier 4: user config entries, kept so they can be re-applied over discovered macros. */
|
|
6456
|
+
configEntries = /* @__PURE__ */ new Map();
|
|
6446
6457
|
/**
|
|
6447
6458
|
* Load built-in macro metadata from @rohal12/spindle's macro-registry.json.
|
|
6448
6459
|
* This is the base layer that provides name, block, subMacros, flags, source.
|
|
@@ -6499,10 +6510,67 @@ var init_macro_registry = __esm({
|
|
|
6499
6510
|
/**
|
|
6500
6511
|
* Overlay user config onto the registry.
|
|
6501
6512
|
* Same format as supplements; applied last so it wins.
|
|
6513
|
+
*
|
|
6514
|
+
* Replaces any previously loaded config: entries from the last call are
|
|
6515
|
+
* reverted first, so macros removed from the config disappear (or fall
|
|
6516
|
+
* back to their builtin/supplement definition).
|
|
6502
6517
|
*/
|
|
6503
6518
|
loadConfig(config2) {
|
|
6519
|
+
for (const [key, previous] of this.configSnapshot) {
|
|
6520
|
+
if (previous) {
|
|
6521
|
+
this.macros.set(key, previous);
|
|
6522
|
+
} else {
|
|
6523
|
+
this.macros.delete(key);
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
this.configSnapshot.clear();
|
|
6527
|
+
this.configEntries.clear();
|
|
6528
|
+
for (const rawKey of Object.keys(config2)) {
|
|
6529
|
+
const key = rawKey.toLowerCase();
|
|
6530
|
+
if (!this.configSnapshot.has(key)) {
|
|
6531
|
+
const existing = this.macros.get(key);
|
|
6532
|
+
this.configSnapshot.set(key, existing ? { ...existing } : void 0);
|
|
6533
|
+
}
|
|
6534
|
+
this.configEntries.set(key, { ...this.configEntries.get(key), ...config2[rawKey] });
|
|
6535
|
+
}
|
|
6504
6536
|
this.mergeEntries(config2);
|
|
6505
6537
|
}
|
|
6538
|
+
/**
|
|
6539
|
+
* Replace the set of macros discovered from Story.defineMacro() calls.
|
|
6540
|
+
* Previously discovered definitions that are no longer present are dropped,
|
|
6541
|
+
* restoring whatever the other tiers define for that name.
|
|
6542
|
+
*
|
|
6543
|
+
* Mirrors Story.defineMacro(): a macro is a block when `block: true`, or
|
|
6544
|
+
* when it declares sub-macros and does not set `block: false`. Each
|
|
6545
|
+
* sub-macro becomes a known macro that may only appear inside its parent.
|
|
6546
|
+
*/
|
|
6547
|
+
setDiscoveredMacros(macros) {
|
|
6548
|
+
this.discovered.clear();
|
|
6549
|
+
for (const m of macros) {
|
|
6550
|
+
const subMacros = m.subMacros ?? [];
|
|
6551
|
+
this.discovered.set(m.name.toLowerCase(), {
|
|
6552
|
+
name: m.name,
|
|
6553
|
+
block: m.block === true || m.block !== false && subMacros.length > 0,
|
|
6554
|
+
subMacros,
|
|
6555
|
+
storeVar: m.storeVar,
|
|
6556
|
+
interpolate: m.interpolate,
|
|
6557
|
+
merged: m.merged,
|
|
6558
|
+
description: m.description,
|
|
6559
|
+
source: "user"
|
|
6560
|
+
});
|
|
6561
|
+
}
|
|
6562
|
+
for (const m of macros) {
|
|
6563
|
+
for (const sub of m.subMacros ?? []) {
|
|
6564
|
+
const key = sub.toLowerCase();
|
|
6565
|
+
const existing = this.discovered.get(key);
|
|
6566
|
+
if (existing) {
|
|
6567
|
+
existing.parents = [...existing.parents ?? [], m.name];
|
|
6568
|
+
} else {
|
|
6569
|
+
this.discovered.set(key, { name: sub, block: false, subMacros: [], source: "user", parents: [m.name] });
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
}
|
|
6506
6574
|
/** Add or replace a single macro entry. */
|
|
6507
6575
|
addMacro(info) {
|
|
6508
6576
|
const key = info.name.toLowerCase();
|
|
@@ -6524,20 +6592,54 @@ var init_macro_registry = __esm({
|
|
|
6524
6592
|
}
|
|
6525
6593
|
/** Get a macro by name (case-insensitive). */
|
|
6526
6594
|
getMacro(name) {
|
|
6527
|
-
return this.
|
|
6595
|
+
return this.resolve(name.toLowerCase());
|
|
6528
6596
|
}
|
|
6529
6597
|
/** Check if a macro is a block (container) macro. */
|
|
6530
6598
|
isBlock(name) {
|
|
6531
|
-
return this.
|
|
6599
|
+
return this.getMacro(name)?.block ?? false;
|
|
6532
6600
|
}
|
|
6533
6601
|
/** Check if a macro is a sub-macro (has parents). */
|
|
6534
6602
|
isSubMacro(name) {
|
|
6535
|
-
const info = this.
|
|
6603
|
+
const info = this.getMacro(name);
|
|
6536
6604
|
return info?.parents != null && info.parents.length > 0;
|
|
6537
6605
|
}
|
|
6538
6606
|
/** Get all registered macros. */
|
|
6539
6607
|
getAllMacros() {
|
|
6540
|
-
|
|
6608
|
+
const keys = /* @__PURE__ */ new Set([...this.macros.keys(), ...this.discovered.keys()]);
|
|
6609
|
+
return Array.from(keys, (key) => this.resolve(key));
|
|
6610
|
+
}
|
|
6611
|
+
/**
|
|
6612
|
+
* Compose the effective entry for a lowercase key: builtins/supplements/
|
|
6613
|
+
* config (stored in `macros`), overlaid by discovered metadata, with user
|
|
6614
|
+
* config re-applied on top so it keeps precedence.
|
|
6615
|
+
*/
|
|
6616
|
+
resolve(key) {
|
|
6617
|
+
const base = this.macros.get(key);
|
|
6618
|
+
const found = this.discovered.get(key);
|
|
6619
|
+
if (!found) return base;
|
|
6620
|
+
const info = {
|
|
6621
|
+
...base,
|
|
6622
|
+
name: base?.name ?? found.name,
|
|
6623
|
+
block: found.block ?? base?.block ?? false,
|
|
6624
|
+
subMacros: found.subMacros?.length ? found.subMacros : base?.subMacros ?? [],
|
|
6625
|
+
source: "user",
|
|
6626
|
+
storeVar: found.storeVar ?? base?.storeVar,
|
|
6627
|
+
interpolate: found.interpolate ?? base?.interpolate,
|
|
6628
|
+
merged: found.merged ?? base?.merged,
|
|
6629
|
+
description: found.description ?? base?.description,
|
|
6630
|
+
// A discovered sub-macro may also belong to other (e.g. built-in) parents
|
|
6631
|
+
parents: found.parents ? [.../* @__PURE__ */ new Set([...base?.parents ?? [], ...found.parents])] : base?.parents
|
|
6632
|
+
};
|
|
6633
|
+
const config2 = this.configEntries.get(key);
|
|
6634
|
+
if (config2) {
|
|
6635
|
+
if (config2.description !== void 0) info.description = config2.description;
|
|
6636
|
+
if (config2.parameters !== void 0) info.parameters = config2.parameters;
|
|
6637
|
+
if (config2.children !== void 0) info.children = config2.children;
|
|
6638
|
+
if (config2.parents !== void 0) info.parents = config2.parents;
|
|
6639
|
+
if (config2.skipArgs !== void 0) info.skipArgs = config2.skipArgs;
|
|
6640
|
+
if (config2.container !== void 0) info.block = config2.container;
|
|
6641
|
+
}
|
|
6642
|
+
return info;
|
|
6541
6643
|
}
|
|
6542
6644
|
/**
|
|
6543
6645
|
* Walk up directory tree from `startDir` to find the
|
|
@@ -6647,6 +6749,17 @@ function inferLiteralType(expr) {
|
|
|
6647
6749
|
if (first === "{") return "object";
|
|
6648
6750
|
return "string";
|
|
6649
6751
|
}
|
|
6752
|
+
function declarationLocation(line, name, absLine, uri) {
|
|
6753
|
+
const start = line.length - line.trimStart().length;
|
|
6754
|
+
const location = {
|
|
6755
|
+
declarationRange: {
|
|
6756
|
+
start: { line: absLine, character: start },
|
|
6757
|
+
end: { line: absLine, character: start + 1 + name.length }
|
|
6758
|
+
}
|
|
6759
|
+
};
|
|
6760
|
+
if (uri !== void 0) location.declarationUri = uri;
|
|
6761
|
+
return location;
|
|
6762
|
+
}
|
|
6650
6763
|
var varRefRegex, transientRefRegex, EXCLUDED_PASSAGES, CLEAN_PATTERNS, ARRAY_MEMBERS, VariableTracker;
|
|
6651
6764
|
var init_variable_tracker = __esm({
|
|
6652
6765
|
"src/core/workspace/variable-tracker.ts"() {
|
|
@@ -6705,7 +6818,7 @@ var init_variable_tracker = __esm({
|
|
|
6705
6818
|
* Parse the StoryVariables passage content for declarations.
|
|
6706
6819
|
* Each line like `$name = value` becomes a declaration.
|
|
6707
6820
|
*/
|
|
6708
|
-
parseStoryVariables(content, contentStartLine = 0) {
|
|
6821
|
+
parseStoryVariables(content, contentStartLine = 0, uri) {
|
|
6709
6822
|
this.declared.clear();
|
|
6710
6823
|
this._hasStoryVariables = true;
|
|
6711
6824
|
this._nullDeclarations = [];
|
|
@@ -6717,9 +6830,10 @@ var init_variable_tracker = __esm({
|
|
|
6717
6830
|
if (!match2) continue;
|
|
6718
6831
|
const name = match2[1];
|
|
6719
6832
|
const expr = match2[2].trim();
|
|
6833
|
+
const absLine = contentStartLine + i;
|
|
6834
|
+
const location = declarationLocation(lines[i], name, absLine, uri);
|
|
6720
6835
|
if (expr === "null") {
|
|
6721
6836
|
const charIdx = lines[i].indexOf("null", lines[i].indexOf("="));
|
|
6722
|
-
const absLine = contentStartLine + i;
|
|
6723
6837
|
this._nullDeclarations.push({
|
|
6724
6838
|
name,
|
|
6725
6839
|
sigil: "$",
|
|
@@ -6728,10 +6842,10 @@ var init_variable_tracker = __esm({
|
|
|
6728
6842
|
end: { line: absLine, character: charIdx + 4 }
|
|
6729
6843
|
}
|
|
6730
6844
|
});
|
|
6731
|
-
this.declared.set(name, { name, sigil: "$" });
|
|
6845
|
+
this.declared.set(name, { name, sigil: "$", ...location });
|
|
6732
6846
|
continue;
|
|
6733
6847
|
}
|
|
6734
|
-
const decl = { name, sigil: "$" };
|
|
6848
|
+
const decl = { name, sigil: "$", ...location };
|
|
6735
6849
|
const type = inferLiteralType(expr);
|
|
6736
6850
|
if (type) decl.type = type;
|
|
6737
6851
|
if (expr.startsWith("{")) {
|
|
@@ -6748,11 +6862,23 @@ var init_variable_tracker = __esm({
|
|
|
6748
6862
|
this.declared.set(name, decl);
|
|
6749
6863
|
}
|
|
6750
6864
|
}
|
|
6865
|
+
/** Forget StoryVariables declarations, e.g. after the passage was removed. */
|
|
6866
|
+
clearStoryVariables() {
|
|
6867
|
+
this.declared.clear();
|
|
6868
|
+
this._hasStoryVariables = false;
|
|
6869
|
+
this._nullDeclarations = [];
|
|
6870
|
+
}
|
|
6871
|
+
/** Forget StoryTransients declarations, e.g. after the passage was removed. */
|
|
6872
|
+
clearStoryTransients() {
|
|
6873
|
+
this.declaredTransient.clear();
|
|
6874
|
+
this._hasStoryTransients = false;
|
|
6875
|
+
this._nullTransientDeclarations = [];
|
|
6876
|
+
}
|
|
6751
6877
|
/**
|
|
6752
6878
|
* Parse the StoryTransients passage content for declarations.
|
|
6753
6879
|
* Each line like `%name = value` becomes a declaration.
|
|
6754
6880
|
*/
|
|
6755
|
-
parseStoryTransients(content, contentStartLine = 0) {
|
|
6881
|
+
parseStoryTransients(content, contentStartLine = 0, uri) {
|
|
6756
6882
|
this.declaredTransient.clear();
|
|
6757
6883
|
this._hasStoryTransients = true;
|
|
6758
6884
|
this._nullTransientDeclarations = [];
|
|
@@ -6764,9 +6890,10 @@ var init_variable_tracker = __esm({
|
|
|
6764
6890
|
if (!match2) continue;
|
|
6765
6891
|
const name = match2[1];
|
|
6766
6892
|
const expr = match2[2].trim();
|
|
6893
|
+
const absLine = contentStartLine + i;
|
|
6894
|
+
const location = declarationLocation(lines[i], name, absLine, uri);
|
|
6767
6895
|
if (expr === "null") {
|
|
6768
6896
|
const charIdx = lines[i].indexOf("null", lines[i].indexOf("="));
|
|
6769
|
-
const absLine = contentStartLine + i;
|
|
6770
6897
|
this._nullTransientDeclarations.push({
|
|
6771
6898
|
name,
|
|
6772
6899
|
sigil: "%",
|
|
@@ -6775,10 +6902,10 @@ var init_variable_tracker = __esm({
|
|
|
6775
6902
|
end: { line: absLine, character: charIdx + 4 }
|
|
6776
6903
|
}
|
|
6777
6904
|
});
|
|
6778
|
-
this.declaredTransient.set(name, { name, sigil: "%" });
|
|
6905
|
+
this.declaredTransient.set(name, { name, sigil: "%", ...location });
|
|
6779
6906
|
continue;
|
|
6780
6907
|
}
|
|
6781
|
-
const decl = { name, sigil: "%" };
|
|
6908
|
+
const decl = { name, sigil: "%", ...location };
|
|
6782
6909
|
const type = inferLiteralType(expr);
|
|
6783
6910
|
if (type) decl.type = type;
|
|
6784
6911
|
if (expr.startsWith("{")) {
|
|
@@ -6809,19 +6936,20 @@ var init_variable_tracker = __esm({
|
|
|
6809
6936
|
for (let i = 0; i < lines.length; i++) {
|
|
6810
6937
|
const header = parsePassageHeader(lines[i], i);
|
|
6811
6938
|
if (header) {
|
|
6812
|
-
passageBoundaries.push({ name: header.name, startLine: i });
|
|
6939
|
+
passageBoundaries.push({ name: header.name, tags: header.tags, startLine: i });
|
|
6813
6940
|
}
|
|
6814
6941
|
}
|
|
6815
6942
|
for (let pi = 0; pi < passageBoundaries.length; pi++) {
|
|
6816
6943
|
const passage = passageBoundaries[pi];
|
|
6817
6944
|
if (EXCLUDED_PASSAGES.has(passage.name)) continue;
|
|
6945
|
+
if (isScriptOrStylesheetPassage(passage)) continue;
|
|
6818
6946
|
const contentStartLine = passage.startLine + 1;
|
|
6819
6947
|
const contentEndLine = pi + 1 < passageBoundaries.length ? passageBoundaries[pi + 1].startLine : lines.length;
|
|
6820
6948
|
const contentLines = lines.slice(contentStartLine, contentEndLine);
|
|
6821
6949
|
const content = contentLines.join("\n");
|
|
6822
6950
|
let cleaned = content;
|
|
6823
6951
|
for (const pattern of CLEAN_PATTERNS) {
|
|
6824
|
-
cleaned = cleaned.replace(pattern, (m) => " "
|
|
6952
|
+
cleaned = cleaned.replace(pattern, (m) => m.replace(/[^\r\n]/g, " "));
|
|
6825
6953
|
}
|
|
6826
6954
|
const lineOffsets = [0];
|
|
6827
6955
|
for (let i = 0; i < cleaned.length; i++) {
|
|
@@ -6976,14 +7104,114 @@ var init_variable_tracker = __esm({
|
|
|
6976
7104
|
}
|
|
6977
7105
|
});
|
|
6978
7106
|
|
|
7107
|
+
// src/core/parsing/macro-parser.ts
|
|
7108
|
+
function buildLineStarts(text) {
|
|
7109
|
+
const starts = [0];
|
|
7110
|
+
for (let i = 0; i < text.length; i++) {
|
|
7111
|
+
if (text[i] === "\n") {
|
|
7112
|
+
starts.push(i + 1);
|
|
7113
|
+
}
|
|
7114
|
+
}
|
|
7115
|
+
return starts;
|
|
7116
|
+
}
|
|
7117
|
+
function offsetToPosition(offset, lineStarts) {
|
|
7118
|
+
let low = 0;
|
|
7119
|
+
let high = lineStarts.length - 1;
|
|
7120
|
+
while (low < high) {
|
|
7121
|
+
const mid = low + high + 1 >> 1;
|
|
7122
|
+
if (lineStarts[mid] <= offset) {
|
|
7123
|
+
low = mid;
|
|
7124
|
+
} else {
|
|
7125
|
+
high = mid - 1;
|
|
7126
|
+
}
|
|
7127
|
+
}
|
|
7128
|
+
return { line: low, character: offset - lineStarts[low] };
|
|
7129
|
+
}
|
|
7130
|
+
function parseMacros(text) {
|
|
7131
|
+
const cleaned = text.replace(variableInterpolationRegex, (match3) => {
|
|
7132
|
+
return " ".repeat(match3.length);
|
|
7133
|
+
});
|
|
7134
|
+
const lineStarts = buildLineStarts(text);
|
|
7135
|
+
const macros = [];
|
|
7136
|
+
let id = 0;
|
|
7137
|
+
macroRegex.lastIndex = 0;
|
|
7138
|
+
let match2;
|
|
7139
|
+
while ((match2 = macroRegex.exec(cleaned)) !== null) {
|
|
7140
|
+
const closeSlash = match2[1];
|
|
7141
|
+
const cssPrefix = (match2[2] || "").trim();
|
|
7142
|
+
const macroName = match2[3];
|
|
7143
|
+
const rawArgs = match2[4];
|
|
7144
|
+
const open = closeSlash !== "/";
|
|
7145
|
+
const matchStart = match2.index;
|
|
7146
|
+
const matchEnd = matchStart + match2[0].length;
|
|
7147
|
+
const startPos = offsetToPosition(matchStart, lineStarts);
|
|
7148
|
+
const endPos = offsetToPosition(matchEnd, lineStarts);
|
|
7149
|
+
const range2 = {
|
|
7150
|
+
start: startPos,
|
|
7151
|
+
end: endPos
|
|
7152
|
+
};
|
|
7153
|
+
macros.push({
|
|
7154
|
+
id,
|
|
7155
|
+
pair: -1,
|
|
7156
|
+
name: macroName,
|
|
7157
|
+
open,
|
|
7158
|
+
range: range2,
|
|
7159
|
+
cssPrefix: cssPrefix || void 0,
|
|
7160
|
+
rawArgs: rawArgs || void 0
|
|
7161
|
+
});
|
|
7162
|
+
id++;
|
|
7163
|
+
}
|
|
7164
|
+
return macros;
|
|
7165
|
+
}
|
|
7166
|
+
function pairMacros(macros, isBlock, passageStartLines = []) {
|
|
7167
|
+
const boundaries = [...passageStartLines].sort((a, b) => a - b);
|
|
7168
|
+
let nextBoundary = 0;
|
|
7169
|
+
let stack = [];
|
|
7170
|
+
for (const macro of macros) {
|
|
7171
|
+
let crossed = false;
|
|
7172
|
+
while (nextBoundary < boundaries.length && macro.range.start.line >= boundaries[nextBoundary]) {
|
|
7173
|
+
nextBoundary++;
|
|
7174
|
+
crossed = true;
|
|
7175
|
+
}
|
|
7176
|
+
if (crossed) stack = [];
|
|
7177
|
+
if (!isBlock(macro.name)) continue;
|
|
7178
|
+
if (macro.open) {
|
|
7179
|
+
stack.push(macro);
|
|
7180
|
+
continue;
|
|
7181
|
+
}
|
|
7182
|
+
const name = macro.name.toLowerCase();
|
|
7183
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
7184
|
+
if (stack[i].name.toLowerCase() === name) {
|
|
7185
|
+
const opener = stack[i];
|
|
7186
|
+
opener.pair = macro.id;
|
|
7187
|
+
macro.pair = opener.id;
|
|
7188
|
+
stack.length = i;
|
|
7189
|
+
break;
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
}
|
|
7194
|
+
var variableInterpolationRegex, macroRegex;
|
|
7195
|
+
var init_macro_parser = __esm({
|
|
7196
|
+
"src/core/parsing/macro-parser.ts"() {
|
|
7197
|
+
"use strict";
|
|
7198
|
+
variableInterpolationRegex = /(?<!\\)\{([$_@%][A-Za-z_$][\w$.]*)\}/g;
|
|
7199
|
+
macroRegex = /(?<!\\)\{(\/)?(?:((?:[#.][a-zA-Z][\w-]*\s*)*)([A-Za-z][\w-]*))(?:\s+((?:(?:`(?:\\.|[^`\\])*?`)|(?:"(?:\\.|[^"\\])*?")|(?:'(?:\\.|[^'\\])*?')|(?:\[(?:[<>]?[Ii][Mm][Gg])?\[[^\r\n]*?\]\]+)|[^}]|(?:}))*?))?\}/gmi;
|
|
7200
|
+
}
|
|
7201
|
+
});
|
|
7202
|
+
|
|
6979
7203
|
// src/core/workspace/widget-registry.ts
|
|
6980
|
-
|
|
7204
|
+
function parseParams(argString) {
|
|
7205
|
+
return argString.trim().split(/\s+/).filter((t) => /^[$_@]/.test(t) && t !== "@children");
|
|
7206
|
+
}
|
|
7207
|
+
var widgetDefRegex, WidgetRegistry;
|
|
6981
7208
|
var init_widget_registry = __esm({
|
|
6982
7209
|
"src/core/workspace/widget-registry.ts"() {
|
|
6983
7210
|
"use strict";
|
|
6984
|
-
|
|
6985
|
-
|
|
7211
|
+
init_macro_parser();
|
|
7212
|
+
widgetDefRegex = /(\{widget\s+["']?)([^\s"'}]+)["']?([^}]*)\}/gi;
|
|
6986
7213
|
WidgetRegistry = class {
|
|
7214
|
+
/** Widgets keyed by lower-cased name; Spindle resolves widgets case-insensitively. */
|
|
6987
7215
|
widgets = /* @__PURE__ */ new Map();
|
|
6988
7216
|
/** Per-URI set of lower-cased macro names opened in that document. */
|
|
6989
7217
|
invokedByUri = /* @__PURE__ */ new Map();
|
|
@@ -7009,40 +7237,43 @@ var init_widget_registry = __esm({
|
|
|
7009
7237
|
break;
|
|
7010
7238
|
}
|
|
7011
7239
|
}
|
|
7012
|
-
const
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7240
|
+
const content = lines.slice(contentStartLine, contentEndLine).join("\n");
|
|
7241
|
+
const lineStarts = buildLineStarts(content);
|
|
7242
|
+
const toPosition = (offset) => {
|
|
7243
|
+
const pos = offsetToPosition(offset, lineStarts);
|
|
7244
|
+
return { line: contentStartLine + pos.line, character: pos.character };
|
|
7245
|
+
};
|
|
7246
|
+
widgetDefRegex.lastIndex = 0;
|
|
7247
|
+
let match2;
|
|
7248
|
+
while ((match2 = widgetDefRegex.exec(content)) !== null) {
|
|
7249
|
+
const widgetName = match2[2];
|
|
7250
|
+
const nameStart = match2.index + match2[1].length;
|
|
7251
|
+
const range2 = {
|
|
7252
|
+
start: toPosition(match2.index),
|
|
7253
|
+
end: toPosition(match2.index + match2[0].length)
|
|
7254
|
+
};
|
|
7255
|
+
const nameRange = {
|
|
7256
|
+
start: toPosition(nameStart),
|
|
7257
|
+
end: toPosition(nameStart + widgetName.length)
|
|
7258
|
+
};
|
|
7259
|
+
const bodyStart = match2.index + match2[0].length;
|
|
7260
|
+
const rest = content.slice(bodyStart);
|
|
7261
|
+
const closeIdx = rest.search(/\{\/widget\}/i);
|
|
7262
|
+
const body = closeIdx >= 0 ? rest.slice(0, closeIdx) : rest;
|
|
7263
|
+
this.widgets.set(widgetName.toLowerCase(), {
|
|
7264
|
+
name: widgetName,
|
|
7265
|
+
params: parseParams(match2[3]),
|
|
7266
|
+
uri: passage.uri,
|
|
7267
|
+
range: range2,
|
|
7268
|
+
nameRange,
|
|
7269
|
+
block: /\{@children\}/.test(body)
|
|
7270
|
+
});
|
|
7040
7271
|
}
|
|
7041
7272
|
}
|
|
7042
7273
|
}
|
|
7043
|
-
/** Get a widget definition by name. */
|
|
7274
|
+
/** Get a widget definition by name (case-insensitive). */
|
|
7044
7275
|
getWidget(name) {
|
|
7045
|
-
return this.widgets.get(name);
|
|
7276
|
+
return this.widgets.get(name.toLowerCase());
|
|
7046
7277
|
}
|
|
7047
7278
|
/** Get all registered widget definitions. */
|
|
7048
7279
|
getAllWidgets() {
|
|
@@ -7076,89 +7307,111 @@ var init_widget_registry = __esm({
|
|
|
7076
7307
|
}
|
|
7077
7308
|
});
|
|
7078
7309
|
|
|
7079
|
-
// src/core/parsing/macro-
|
|
7080
|
-
function
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7310
|
+
// src/core/parsing/macro-discovery.ts
|
|
7311
|
+
function extractBalancedBraces(source, start) {
|
|
7312
|
+
let depth = 0;
|
|
7313
|
+
let inSingleQuote = false;
|
|
7314
|
+
let inDoubleQuote = false;
|
|
7315
|
+
let inBacktick = false;
|
|
7316
|
+
for (let i = start; i < source.length; i++) {
|
|
7317
|
+
const ch = source[i];
|
|
7318
|
+
const prev = i > start ? source[i - 1] : "";
|
|
7319
|
+
if (prev === "\\") continue;
|
|
7320
|
+
if (inSingleQuote) {
|
|
7321
|
+
if (ch === "'") inSingleQuote = false;
|
|
7322
|
+
continue;
|
|
7323
|
+
}
|
|
7324
|
+
if (inDoubleQuote) {
|
|
7325
|
+
if (ch === '"') inDoubleQuote = false;
|
|
7326
|
+
continue;
|
|
7327
|
+
}
|
|
7328
|
+
if (inBacktick) {
|
|
7329
|
+
if (ch === "`") inBacktick = false;
|
|
7330
|
+
continue;
|
|
7331
|
+
}
|
|
7332
|
+
if (ch === "'") {
|
|
7333
|
+
inSingleQuote = true;
|
|
7334
|
+
} else if (ch === '"') {
|
|
7335
|
+
inDoubleQuote = true;
|
|
7336
|
+
} else if (ch === "`") {
|
|
7337
|
+
inBacktick = true;
|
|
7338
|
+
} else if (ch === "{") {
|
|
7339
|
+
depth++;
|
|
7340
|
+
} else if (ch === "}") {
|
|
7341
|
+
depth--;
|
|
7342
|
+
if (depth === 0) {
|
|
7343
|
+
return source.slice(start, i + 1);
|
|
7344
|
+
}
|
|
7085
7345
|
}
|
|
7086
7346
|
}
|
|
7087
|
-
return
|
|
7347
|
+
return null;
|
|
7088
7348
|
}
|
|
7089
|
-
function
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7349
|
+
function parseConfigFields(config2) {
|
|
7350
|
+
const nameMatch = config2.match(/name:\s*['"]([^'"]+)['"]/);
|
|
7351
|
+
if (!nameMatch) return null;
|
|
7352
|
+
const macro = {
|
|
7353
|
+
name: nameMatch[1]
|
|
7354
|
+
};
|
|
7355
|
+
const boolFields = [
|
|
7356
|
+
"block",
|
|
7357
|
+
"storeVar",
|
|
7358
|
+
"merged",
|
|
7359
|
+
"interpolate"
|
|
7360
|
+
];
|
|
7361
|
+
for (const field of boolFields) {
|
|
7362
|
+
const re = new RegExp(`${field}:\\s*(true|false)`);
|
|
7363
|
+
const m = config2.match(re);
|
|
7364
|
+
if (m) {
|
|
7365
|
+
macro[field] = m[1] === "true";
|
|
7098
7366
|
}
|
|
7099
7367
|
}
|
|
7100
|
-
|
|
7368
|
+
const descMatch = config2.match(/description:\s*['"]([^'"]+)['"]/);
|
|
7369
|
+
if (descMatch) {
|
|
7370
|
+
macro.description = descMatch[1];
|
|
7371
|
+
}
|
|
7372
|
+
const subMatch = config2.match(/subMacros:\s*\[([^\]]*)\]/);
|
|
7373
|
+
if (subMatch) {
|
|
7374
|
+
const inner = subMatch[1].trim();
|
|
7375
|
+
if (inner) {
|
|
7376
|
+
macro.subMacros = inner.split(",").map(
|
|
7377
|
+
(s) => s.trim().replace(/^['"]|['"]$/g, "")
|
|
7378
|
+
).filter((s) => s.length > 0);
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
7381
|
+
return macro;
|
|
7101
7382
|
}
|
|
7102
|
-
function
|
|
7103
|
-
const cleaned = text.replace(variableInterpolationRegex, (match3) => {
|
|
7104
|
-
return " ".repeat(match3.length);
|
|
7105
|
-
});
|
|
7106
|
-
const lineStarts = buildLineStarts(text);
|
|
7383
|
+
function discoverMacrosFromSource(source) {
|
|
7107
7384
|
const macros = [];
|
|
7108
|
-
|
|
7109
|
-
macroRegex.lastIndex = 0;
|
|
7385
|
+
defineMacroRegex.lastIndex = 0;
|
|
7110
7386
|
let match2;
|
|
7111
|
-
while ((match2 =
|
|
7112
|
-
const
|
|
7113
|
-
const
|
|
7114
|
-
|
|
7115
|
-
const
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
const startPos = offsetToPosition(matchStart, lineStarts);
|
|
7120
|
-
const endPos = offsetToPosition(matchEnd, lineStarts);
|
|
7121
|
-
const range2 = {
|
|
7122
|
-
start: startPos,
|
|
7123
|
-
end: endPos
|
|
7124
|
-
};
|
|
7125
|
-
macros.push({
|
|
7126
|
-
id,
|
|
7127
|
-
pair: -1,
|
|
7128
|
-
name: macroName,
|
|
7129
|
-
open,
|
|
7130
|
-
range: range2,
|
|
7131
|
-
cssPrefix: cssPrefix || void 0,
|
|
7132
|
-
rawArgs: rawArgs || void 0
|
|
7133
|
-
});
|
|
7134
|
-
id++;
|
|
7387
|
+
while ((match2 = defineMacroRegex.exec(source)) !== null) {
|
|
7388
|
+
const braceStart = match2.index + match2[0].length - 1;
|
|
7389
|
+
const config2 = extractBalancedBraces(source, braceStart);
|
|
7390
|
+
if (!config2) continue;
|
|
7391
|
+
const macro = parseConfigFields(config2);
|
|
7392
|
+
if (macro) {
|
|
7393
|
+
macros.push(macro);
|
|
7394
|
+
}
|
|
7135
7395
|
}
|
|
7136
7396
|
return macros;
|
|
7137
7397
|
}
|
|
7138
|
-
function
|
|
7139
|
-
const
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
} else {
|
|
7147
|
-
const stack = stacks[name];
|
|
7148
|
-
if (stack && stack.length > 0) {
|
|
7149
|
-
const openId = stack.pop();
|
|
7150
|
-
macros[openId].pair = macro.id;
|
|
7151
|
-
macro.pair = openId;
|
|
7152
|
-
}
|
|
7153
|
-
}
|
|
7398
|
+
function discoverMacrosFromStoryInit(passageContent2) {
|
|
7399
|
+
const macros = [];
|
|
7400
|
+
doBlockRegex.lastIndex = 0;
|
|
7401
|
+
let doMatch;
|
|
7402
|
+
while ((doMatch = doBlockRegex.exec(passageContent2)) !== null) {
|
|
7403
|
+
const jsContent = doMatch[1];
|
|
7404
|
+
const found = discoverMacrosFromSource(jsContent);
|
|
7405
|
+
macros.push(...found);
|
|
7154
7406
|
}
|
|
7407
|
+
return macros;
|
|
7155
7408
|
}
|
|
7156
|
-
var
|
|
7157
|
-
var
|
|
7158
|
-
"src/core/parsing/macro-
|
|
7409
|
+
var defineMacroRegex, doBlockRegex;
|
|
7410
|
+
var init_macro_discovery = __esm({
|
|
7411
|
+
"src/core/parsing/macro-discovery.ts"() {
|
|
7159
7412
|
"use strict";
|
|
7160
|
-
|
|
7161
|
-
|
|
7413
|
+
defineMacroRegex = /Story\.defineMacro\s*\(\s*\{/g;
|
|
7414
|
+
doBlockRegex = /\{do\}([\s\S]*?)\{\/do\}/g;
|
|
7162
7415
|
}
|
|
7163
7416
|
});
|
|
7164
7417
|
|
|
@@ -7352,7 +7605,7 @@ var init_macro_supplements = __esm({
|
|
|
7352
7605
|
},
|
|
7353
7606
|
include: {
|
|
7354
7607
|
name: "include",
|
|
7355
|
-
description: 'Outputs the contents of the specified passage.\n\nUsage: `{include "passage"}`',
|
|
7608
|
+
description: 'Outputs the contents of the specified passage. The argument is a passage name or an expression that evaluates to one; add `inline` to render the passage as inline content without markdown processing.\n\nUsage: `{include "passage"}`, `{include $passageName}` or `{include "passage" inline}`',
|
|
7356
7609
|
parameters: ["text"]
|
|
7357
7610
|
},
|
|
7358
7611
|
passage: {
|
|
@@ -7362,28 +7615,28 @@ var init_macro_supplements = __esm({
|
|
|
7362
7615
|
},
|
|
7363
7616
|
textbox: {
|
|
7364
7617
|
name: "textbox",
|
|
7365
|
-
description: 'Creates a text input box.\n\nUsage: `{textbox
|
|
7366
|
-
parameters: ["receiver
|
|
7618
|
+
description: 'Creates a text input box bound to a story variable.\n\nUsage: `{textbox $receiver}` or `{textbox $receiver "placeholder"}`',
|
|
7619
|
+
parameters: ["receiver |+ text"]
|
|
7367
7620
|
},
|
|
7368
7621
|
numberbox: {
|
|
7369
7622
|
name: "numberbox",
|
|
7370
|
-
description: 'Creates a number input box.\n\nUsage: `{numberbox
|
|
7371
|
-
parameters: ["receiver
|
|
7623
|
+
description: 'Creates a number input box bound to a story variable.\n\nUsage: `{numberbox $receiver}` or `{numberbox $receiver "placeholder"}`',
|
|
7624
|
+
parameters: ["receiver |+ text"]
|
|
7372
7625
|
},
|
|
7373
7626
|
textarea: {
|
|
7374
7627
|
name: "textarea",
|
|
7375
|
-
description: 'Creates a multi-line text input area.\n\nUsage: `{textarea
|
|
7376
|
-
parameters: ["receiver
|
|
7628
|
+
description: 'Creates a multi-line text input area bound to a story variable.\n\nUsage: `{textarea $receiver}` or `{textarea $receiver "placeholder"}`',
|
|
7629
|
+
parameters: ["receiver |+ text"]
|
|
7377
7630
|
},
|
|
7378
7631
|
checkbox: {
|
|
7379
7632
|
name: "checkbox",
|
|
7380
|
-
description: 'Creates a checkbox
|
|
7381
|
-
parameters: ["receiver
|
|
7633
|
+
description: 'Creates a checkbox that toggles a boolean story variable.\n\nUsage: `{checkbox $receiver "label"}`',
|
|
7634
|
+
parameters: ["receiver |+ text"]
|
|
7382
7635
|
},
|
|
7383
7636
|
radiobutton: {
|
|
7384
7637
|
name: "radiobutton",
|
|
7385
|
-
description: 'Creates a radio button
|
|
7386
|
-
parameters: ["receiver &+ text
|
|
7638
|
+
description: 'Creates a radio button that sets a story variable to its value. Use the same variable for every option in a group.\n\nUsage: `{radiobutton $receiver "value" "label"}`',
|
|
7639
|
+
parameters: ["receiver &+ text |+ text"]
|
|
7387
7640
|
},
|
|
7388
7641
|
quicksave: {
|
|
7389
7642
|
name: "quicksave",
|
|
@@ -7413,8 +7666,8 @@ var init_macro_supplements = __esm({
|
|
|
7413
7666
|
dialog: {
|
|
7414
7667
|
name: "dialog",
|
|
7415
7668
|
container: true,
|
|
7416
|
-
description: '
|
|
7417
|
-
parameters: ["text"]
|
|
7669
|
+
description: 'Creates a button that opens the passage named in its body in a dialog. Add `noclose` to hide the dialog\'s close button.\n\nUsage:\n\n```\n{dialog "label"}Passage Name{/dialog}\n{dialog "label" noclose}Passage Name{/dialog}\n```',
|
|
7670
|
+
parameters: ["text |+ 'noclose'"]
|
|
7418
7671
|
},
|
|
7419
7672
|
nobr: {
|
|
7420
7673
|
name: "nobr",
|
|
@@ -7470,6 +7723,7 @@ var init_workspace_model = __esm({
|
|
|
7470
7723
|
init_variable_tracker();
|
|
7471
7724
|
init_widget_registry();
|
|
7472
7725
|
init_macro_parser();
|
|
7726
|
+
init_macro_discovery();
|
|
7473
7727
|
init_macro_supplements();
|
|
7474
7728
|
WorkspaceModel = class extends EventEmitter3 {
|
|
7475
7729
|
documents;
|
|
@@ -7517,6 +7771,14 @@ var init_workspace_model = __esm({
|
|
|
7517
7771
|
this.initialized = true;
|
|
7518
7772
|
this.scheduleModelReady();
|
|
7519
7773
|
}
|
|
7774
|
+
/**
|
|
7775
|
+
* Re-run cross-document analysis and schedule 'modelReady', e.g. after
|
|
7776
|
+
* the macro configuration changed without any document changing.
|
|
7777
|
+
*/
|
|
7778
|
+
refresh() {
|
|
7779
|
+
this.cascade();
|
|
7780
|
+
this.scheduleModelReady();
|
|
7781
|
+
}
|
|
7520
7782
|
/** Clean up listeners and timers. */
|
|
7521
7783
|
dispose() {
|
|
7522
7784
|
this.documents.removeListener("documentChanged", this.onDocumentChanged);
|
|
@@ -7534,6 +7796,7 @@ var init_workspace_model = __esm({
|
|
|
7534
7796
|
if (text !== void 0) {
|
|
7535
7797
|
this.passages.rebuild(uri, text);
|
|
7536
7798
|
}
|
|
7799
|
+
this.refreshDiscoveredMacros();
|
|
7537
7800
|
this.cascade();
|
|
7538
7801
|
this.emit("documentChanged", uri);
|
|
7539
7802
|
this.emit("passagesUpdated", uri);
|
|
@@ -7542,6 +7805,7 @@ var init_workspace_model = __esm({
|
|
|
7542
7805
|
/** Handle a document close: remove its passages and cascade. */
|
|
7543
7806
|
handleDocumentClose(uri) {
|
|
7544
7807
|
this.passages.remove(uri);
|
|
7808
|
+
this.refreshDiscoveredMacros();
|
|
7545
7809
|
this.cascade();
|
|
7546
7810
|
this.emit("documentClosed", uri);
|
|
7547
7811
|
this.emit("passagesUpdated", uri);
|
|
@@ -7555,8 +7819,39 @@ var init_workspace_model = __esm({
|
|
|
7555
7819
|
this.passages.rebuild(uri, text);
|
|
7556
7820
|
}
|
|
7557
7821
|
}
|
|
7822
|
+
this.refreshDiscoveredMacros();
|
|
7558
7823
|
this.cascade();
|
|
7559
7824
|
}
|
|
7825
|
+
/**
|
|
7826
|
+
* Re-run static macro discovery over the workspace and replace the
|
|
7827
|
+
* registry's discovered tier (dropping definitions whose source is gone).
|
|
7828
|
+
*
|
|
7829
|
+
* Sources scanned for Story.defineMacro({...}) calls:
|
|
7830
|
+
* - `{do}` blocks in StoryInit passages
|
|
7831
|
+
* - passages tagged `script` (Story JavaScript)
|
|
7832
|
+
* - JavaScript/TypeScript documents in the workspace
|
|
7833
|
+
*/
|
|
7834
|
+
refreshDiscoveredMacros() {
|
|
7835
|
+
const found = [];
|
|
7836
|
+
for (const uri of this.documents.getUris()) {
|
|
7837
|
+
const text = this.documents.getText(uri);
|
|
7838
|
+
if (!text) continue;
|
|
7839
|
+
if (/\.[cm]?[jt]s$/i.test(uri)) {
|
|
7840
|
+
found.push(...discoverMacrosFromSource(text));
|
|
7841
|
+
continue;
|
|
7842
|
+
}
|
|
7843
|
+
let lines;
|
|
7844
|
+
for (const passage of this.passages.getPassagesInDocument(uri)) {
|
|
7845
|
+
const isStoryInit = passage.name === "StoryInit";
|
|
7846
|
+
const isScript = passage.tags?.includes("script") ?? false;
|
|
7847
|
+
if (!isStoryInit && !isScript) continue;
|
|
7848
|
+
lines ??= text.split("\n");
|
|
7849
|
+
const content = lines.slice(passage.headerEnd.end.line + 1, passage.range.end.line + 1).join("\n");
|
|
7850
|
+
found.push(...isScript ? discoverMacrosFromSource(content) : discoverMacrosFromStoryInit(content));
|
|
7851
|
+
}
|
|
7852
|
+
}
|
|
7853
|
+
this.macros.setDiscoveredMacros(found);
|
|
7854
|
+
}
|
|
7560
7855
|
/**
|
|
7561
7856
|
* Cascade: rescan variables and widgets based on current passages.
|
|
7562
7857
|
* Called after any passage index update.
|
|
@@ -7576,8 +7871,10 @@ var init_workspace_model = __esm({
|
|
|
7576
7871
|
}
|
|
7577
7872
|
}
|
|
7578
7873
|
const content = lines.slice(contentStart, contentEnd).join("\n");
|
|
7579
|
-
this.variables.parseStoryVariables(content, contentStart);
|
|
7874
|
+
this.variables.parseStoryVariables(content, contentStart, storyVars.uri);
|
|
7580
7875
|
}
|
|
7876
|
+
} else {
|
|
7877
|
+
this.variables.clearStoryVariables();
|
|
7581
7878
|
}
|
|
7582
7879
|
const storyTransients = this.passages.getStoryTransients();
|
|
7583
7880
|
if (storyTransients) {
|
|
@@ -7593,8 +7890,10 @@ var init_workspace_model = __esm({
|
|
|
7593
7890
|
}
|
|
7594
7891
|
}
|
|
7595
7892
|
const content = lines.slice(contentStart, contentEnd).join("\n");
|
|
7596
|
-
this.variables.parseStoryTransients(content, contentStart);
|
|
7893
|
+
this.variables.parseStoryTransients(content, contentStart, storyTransients.uri);
|
|
7597
7894
|
}
|
|
7895
|
+
} else {
|
|
7896
|
+
this.variables.clearStoryTransients();
|
|
7598
7897
|
}
|
|
7599
7898
|
this.widgets.clearInvocations();
|
|
7600
7899
|
for (const uri of this.documents.getUris()) {
|
|
@@ -8353,6 +8652,30 @@ var init_parameter_validator = __esm({
|
|
|
8353
8652
|
return arg.type === 4 /* Variable */ ? null : new TypeError2("Argument is not a variable");
|
|
8354
8653
|
}
|
|
8355
8654
|
},
|
|
8655
|
+
{
|
|
8656
|
+
// The story variable a form control binds to: `$name` or `"$name"`.
|
|
8657
|
+
// Spindle strips the quotes and the `$` sigil from the first argument
|
|
8658
|
+
// and reads the rest as a dot path into the story variables.
|
|
8659
|
+
name: ["receiver"],
|
|
8660
|
+
validate(arg) {
|
|
8661
|
+
let name;
|
|
8662
|
+
if (arg.type === 0 /* String */) {
|
|
8663
|
+
name = arg.text.slice(1, -1);
|
|
8664
|
+
} else if (arg.type === 8 /* Bareword */) {
|
|
8665
|
+
name = arg.text;
|
|
8666
|
+
}
|
|
8667
|
+
if (name === void 0) {
|
|
8668
|
+
return new TypeError2("Argument is not a variable receiver");
|
|
8669
|
+
}
|
|
8670
|
+
if (/^[_@%]/.test(name)) {
|
|
8671
|
+
return new TypeError2(`Variable receiver must be a story variable ($name), got '${name}'`);
|
|
8672
|
+
}
|
|
8673
|
+
if (!/^\$?[A-Za-z_][\w$]*(?:\.[\w$]+)*$/.test(name)) {
|
|
8674
|
+
return new TypeError2(`Argument is not a valid variable receiver: '${name}'`);
|
|
8675
|
+
}
|
|
8676
|
+
return null;
|
|
8677
|
+
}
|
|
8678
|
+
},
|
|
8356
8679
|
{
|
|
8357
8680
|
name: ["passage"],
|
|
8358
8681
|
validate(arg, _args, _index, state) {
|
|
@@ -8505,58 +8828,175 @@ var init_parameter_validator = __esm({
|
|
|
8505
8828
|
});
|
|
8506
8829
|
|
|
8507
8830
|
// src/core/parsing/link-parser.ts
|
|
8508
|
-
function
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8831
|
+
function skipSelectors(text, i) {
|
|
8832
|
+
while (text[i] === "." || text[i] === "#") {
|
|
8833
|
+
i++;
|
|
8834
|
+
for (; ; ) {
|
|
8835
|
+
if (/[a-zA-Z0-9_-]/.test(text[i] ?? "")) {
|
|
8836
|
+
i++;
|
|
8837
|
+
continue;
|
|
8838
|
+
}
|
|
8839
|
+
const interpolation = /^\{[$_@][\w.]*\}/.exec(text.slice(i));
|
|
8840
|
+
if (!interpolation) break;
|
|
8841
|
+
i += interpolation[0].length;
|
|
8513
8842
|
}
|
|
8514
8843
|
}
|
|
8515
|
-
return
|
|
8844
|
+
return i;
|
|
8516
8845
|
}
|
|
8517
|
-
function
|
|
8518
|
-
let
|
|
8519
|
-
let
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8846
|
+
function locateTarget(inner) {
|
|
8847
|
+
let start = 0;
|
|
8848
|
+
let end = inner.length;
|
|
8849
|
+
const pipeIdx = inner.indexOf("|");
|
|
8850
|
+
const arrowIdx = inner.indexOf("->");
|
|
8851
|
+
const revIdx = inner.indexOf("<-");
|
|
8852
|
+
if (pipeIdx !== -1) {
|
|
8853
|
+
start = pipeIdx + 1;
|
|
8854
|
+
} else if (arrowIdx !== -1) {
|
|
8855
|
+
start = arrowIdx + 2;
|
|
8856
|
+
} else if (revIdx !== -1) {
|
|
8857
|
+
end = revIdx;
|
|
8527
8858
|
}
|
|
8528
|
-
|
|
8859
|
+
while (start < end && /\s/.test(inner[start])) start++;
|
|
8860
|
+
while (end > start && /\s/.test(inner[end - 1])) end--;
|
|
8861
|
+
return { start, end };
|
|
8529
8862
|
}
|
|
8530
8863
|
function parseLinks(text, lineOffset = 0) {
|
|
8531
|
-
const lineStarts =
|
|
8864
|
+
const lineStarts = buildLineStarts(text);
|
|
8532
8865
|
const refs = [];
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
if (
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
const
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8866
|
+
let i = text.indexOf("[[");
|
|
8867
|
+
while (i !== -1) {
|
|
8868
|
+
const linkStart = i;
|
|
8869
|
+
i += 2;
|
|
8870
|
+
if (text[i] === "." || text[i] === "#") {
|
|
8871
|
+
i = skipSelectors(text, i);
|
|
8872
|
+
if (text[i] === " ") i++;
|
|
8873
|
+
}
|
|
8874
|
+
const innerStart = i;
|
|
8875
|
+
let depth = 1;
|
|
8876
|
+
while (i < text.length) {
|
|
8877
|
+
if (text.startsWith("[[", i)) {
|
|
8878
|
+
depth++;
|
|
8879
|
+
i += 2;
|
|
8880
|
+
} else if (text.startsWith("]]", i)) {
|
|
8881
|
+
if (--depth === 0) break;
|
|
8882
|
+
i += 2;
|
|
8883
|
+
} else {
|
|
8884
|
+
i++;
|
|
8885
|
+
}
|
|
8886
|
+
}
|
|
8887
|
+
if (depth !== 0) {
|
|
8888
|
+
i = text.indexOf("[[", linkStart + 2);
|
|
8889
|
+
continue;
|
|
8890
|
+
}
|
|
8891
|
+
const target = locateTarget(text.slice(innerStart, i));
|
|
8892
|
+
if (target.end > target.start) {
|
|
8893
|
+
const nameStart = innerStart + target.start;
|
|
8894
|
+
const nameEnd = innerStart + target.end;
|
|
8895
|
+
const startPos = offsetToPosition(nameStart, lineStarts);
|
|
8896
|
+
const endPos = offsetToPosition(nameEnd, lineStarts);
|
|
8897
|
+
refs.push({
|
|
8898
|
+
name: text.slice(nameStart, nameEnd),
|
|
8899
|
+
range: {
|
|
8900
|
+
start: { line: startPos.line + lineOffset, character: startPos.character },
|
|
8901
|
+
end: { line: endPos.line + lineOffset, character: endPos.character }
|
|
8902
|
+
},
|
|
8903
|
+
source: "link"
|
|
8904
|
+
});
|
|
8905
|
+
}
|
|
8906
|
+
i = text.indexOf("[[", i + 2);
|
|
8907
|
+
}
|
|
8908
|
+
return refs;
|
|
8909
|
+
}
|
|
8910
|
+
function findPassageRefAt(text, position) {
|
|
8911
|
+
const { line, character } = position;
|
|
8912
|
+
return [...parseLinks(text), ...parseMacroPassageRefs(text)].find(({ range: { start, end } }) => (line > start.line || line === start.line && character >= start.character) && (line < end.line || line === end.line && character <= end.character));
|
|
8913
|
+
}
|
|
8914
|
+
function resolveExpressionTarget(args2) {
|
|
8915
|
+
const lead = args2.length - args2.trimStart().length;
|
|
8916
|
+
const expr = args2.trim();
|
|
8917
|
+
if (expr === "") return null;
|
|
8918
|
+
const lexed = lexArguments(expr);
|
|
8919
|
+
if (lexed.length === 1 && lexed[0].start === 0 && lexed[0].end === expr.length && (lexed[0].type === 0 /* String */ || lexed[0].type === 7 /* Expression */)) {
|
|
8920
|
+
const raw = expr.slice(1, -1);
|
|
8921
|
+
if (interpolationRegex.test(raw) || raw.includes("${")) return null;
|
|
8922
|
+
return { name: raw.replace(/\\(.)/g, "$1"), start: lead + 1, end: lead + expr.length - 1 };
|
|
8923
|
+
}
|
|
8924
|
+
if (/[$@%"'`(]/.test(expr) || temporaryRefRegex.test(expr)) return null;
|
|
8925
|
+
if (EXPRESSION_BUILTINS.has(expr)) return null;
|
|
8926
|
+
return { name: expr, start: lead, end: lead + expr.length };
|
|
8927
|
+
}
|
|
8928
|
+
function resolveIncludeTarget(args2) {
|
|
8929
|
+
const inline = /\binline\b/.exec(args2);
|
|
8930
|
+
if (!inline) return resolveExpressionTarget(args2);
|
|
8931
|
+
const cut = inline.index;
|
|
8932
|
+
const width = inline[0].length;
|
|
8933
|
+
const target = resolveExpressionTarget(args2.slice(0, cut) + args2.slice(cut + width));
|
|
8934
|
+
if (!target || target.start < cut && cut < target.end) return null;
|
|
8935
|
+
return {
|
|
8936
|
+
name: target.name,
|
|
8937
|
+
start: target.start < cut ? target.start : target.start + width,
|
|
8938
|
+
end: target.end <= cut ? target.end : target.end + width
|
|
8939
|
+
};
|
|
8940
|
+
}
|
|
8941
|
+
function resolveLinkMacroTarget(args2) {
|
|
8942
|
+
const strings = lexArguments(args2).filter((a) => a.type === 0 /* String */);
|
|
8943
|
+
if (strings.length < 2) return null;
|
|
8944
|
+
const target = strings[1];
|
|
8945
|
+
const name = target.text.slice(1, -1);
|
|
8946
|
+
if (name.includes("\\") || interpolationRegex.test(name)) return null;
|
|
8947
|
+
return { name, start: target.start + 1, end: target.end - 1 };
|
|
8948
|
+
}
|
|
8949
|
+
function parseMacroPassageRefs(text, lineOffset = 0) {
|
|
8950
|
+
const lineStarts = buildLineStarts(text);
|
|
8951
|
+
const refs = [];
|
|
8952
|
+
for (const macro of parseMacros(text)) {
|
|
8953
|
+
if (!macro.open || !macro.rawArgs) continue;
|
|
8954
|
+
const resolve3 = macroTargetResolvers[macro.name.toLowerCase()];
|
|
8955
|
+
if (!resolve3) continue;
|
|
8956
|
+
const argsEnd = lineStarts[macro.range.end.line] + macro.range.end.character - 1;
|
|
8957
|
+
const argsStart = argsEnd - macro.rawArgs.length;
|
|
8958
|
+
const target = resolve3(text.slice(argsStart, argsEnd));
|
|
8959
|
+
if (!target || target.name === "") continue;
|
|
8960
|
+
const startPos = offsetToPosition(argsStart + target.start, lineStarts);
|
|
8961
|
+
const endPos = offsetToPosition(argsStart + target.end, lineStarts);
|
|
8547
8962
|
refs.push({
|
|
8548
|
-
name:
|
|
8549
|
-
range:
|
|
8550
|
-
|
|
8963
|
+
name: target.name,
|
|
8964
|
+
range: {
|
|
8965
|
+
start: { line: startPos.line + lineOffset, character: startPos.character },
|
|
8966
|
+
end: { line: endPos.line + lineOffset, character: endPos.character }
|
|
8967
|
+
},
|
|
8968
|
+
source: "macro"
|
|
8551
8969
|
});
|
|
8552
8970
|
}
|
|
8553
8971
|
return refs;
|
|
8554
8972
|
}
|
|
8555
|
-
var
|
|
8973
|
+
var EXPRESSION_BUILTINS, interpolationRegex, temporaryRefRegex, macroTargetResolvers;
|
|
8556
8974
|
var init_link_parser = __esm({
|
|
8557
8975
|
"src/core/parsing/link-parser.ts"() {
|
|
8558
8976
|
"use strict";
|
|
8559
|
-
|
|
8977
|
+
init_macro_parser();
|
|
8978
|
+
init_argument_lexer();
|
|
8979
|
+
EXPRESSION_BUILTINS = /* @__PURE__ */ new Set([
|
|
8980
|
+
"currentPassage",
|
|
8981
|
+
"previousPassage",
|
|
8982
|
+
"visited",
|
|
8983
|
+
"hasVisited",
|
|
8984
|
+
"hasVisitedAny",
|
|
8985
|
+
"hasVisitedAll",
|
|
8986
|
+
"rendered",
|
|
8987
|
+
"hasRendered",
|
|
8988
|
+
"hasRenderedAny",
|
|
8989
|
+
"hasRenderedAll",
|
|
8990
|
+
"random",
|
|
8991
|
+
"randomInt"
|
|
8992
|
+
]);
|
|
8993
|
+
interpolationRegex = /\{[$_@%][A-Za-z_$]/;
|
|
8994
|
+
temporaryRefRegex = /(?<![.\w$@%])_[A-Za-z_$][\w$]*(?![\w$])/;
|
|
8995
|
+
macroTargetResolvers = {
|
|
8996
|
+
goto: resolveExpressionTarget,
|
|
8997
|
+
include: resolveIncludeTarget,
|
|
8998
|
+
link: resolveLinkMacroTarget
|
|
8999
|
+
};
|
|
8560
9000
|
}
|
|
8561
9001
|
});
|
|
8562
9002
|
|
|
@@ -8569,8 +9009,13 @@ function computeDiagnostics(uri, workspace, options) {
|
|
|
8569
9009
|
const passages = workspace.passages.getPassagesInDocument(uri);
|
|
8570
9010
|
if (passages.length === 0) return [];
|
|
8571
9011
|
const diagnostics = [];
|
|
8572
|
-
const
|
|
8573
|
-
|
|
9012
|
+
const markupText = maskScriptAndStylesheetPassages(text, passages);
|
|
9013
|
+
const macros = parseMacros(markupText);
|
|
9014
|
+
pairMacros(
|
|
9015
|
+
macros,
|
|
9016
|
+
(name) => workspace.macros.isBlock(name),
|
|
9017
|
+
passages.map((p) => p.range.start.line)
|
|
9018
|
+
);
|
|
8574
9019
|
const allPassages = workspace.passages.getAllPassages();
|
|
8575
9020
|
const passageNames = new Set(allPassages.map((p) => p.name));
|
|
8576
9021
|
try {
|
|
@@ -8586,7 +9031,7 @@ function computeDiagnostics(uri, workspace, options) {
|
|
|
8586
9031
|
} catch {
|
|
8587
9032
|
}
|
|
8588
9033
|
try {
|
|
8589
|
-
validateLinks(
|
|
9034
|
+
validateLinks(markupText, passages, passageNames, diagnostics);
|
|
8590
9035
|
} catch {
|
|
8591
9036
|
}
|
|
8592
9037
|
try {
|
|
@@ -8620,6 +9065,18 @@ function computeDiagnostics(uri, workspace, options) {
|
|
|
8620
9065
|
return [];
|
|
8621
9066
|
}
|
|
8622
9067
|
}
|
|
9068
|
+
function maskScriptAndStylesheetPassages(text, passages) {
|
|
9069
|
+
const excluded = passages.filter(isScriptOrStylesheetPassage);
|
|
9070
|
+
if (excluded.length === 0) return text;
|
|
9071
|
+
const lines = text.split("\n");
|
|
9072
|
+
for (const passage of excluded) {
|
|
9073
|
+
const last = Math.min(passage.range.end.line, lines.length - 1);
|
|
9074
|
+
for (let i = passage.range.start.line + 1; i <= last; i++) {
|
|
9075
|
+
lines[i] = lines[i].replace(/[^\r]/g, " ");
|
|
9076
|
+
}
|
|
9077
|
+
}
|
|
9078
|
+
return lines.join("\n");
|
|
9079
|
+
}
|
|
8623
9080
|
function validateMacros(macros, workspace, diagnostics) {
|
|
8624
9081
|
for (let curIndex = 0; curIndex < macros.length; curIndex++) {
|
|
8625
9082
|
const macro = macros[curIndex];
|
|
@@ -8732,8 +9189,13 @@ function validateArguments(macros, workspace, passageNames, diagnostics) {
|
|
|
8732
9189
|
if (info.skipArgs) continue;
|
|
8733
9190
|
if (!info.parameters) continue;
|
|
8734
9191
|
const rawArgs = macro.rawArgs ?? "";
|
|
8735
|
-
const args2 = lexArguments(rawArgs);
|
|
8736
|
-
|
|
9192
|
+
const args2 = macro.name.toLowerCase() === "include" ? includeArguments(rawArgs) : lexArguments(rawArgs);
|
|
9193
|
+
let params;
|
|
9194
|
+
try {
|
|
9195
|
+
params = new Parameters(info.parameters);
|
|
9196
|
+
} catch {
|
|
9197
|
+
continue;
|
|
9198
|
+
}
|
|
8737
9199
|
if (params.isEmpty()) {
|
|
8738
9200
|
if (args2.length > 0) {
|
|
8739
9201
|
diagnostics.push(makeDiag(
|
|
@@ -8773,6 +9235,16 @@ function validateArguments(macros, workspace, passageNames, diagnostics) {
|
|
|
8773
9235
|
}
|
|
8774
9236
|
}
|
|
8775
9237
|
}
|
|
9238
|
+
function includeArguments(rawArgs) {
|
|
9239
|
+
const expr = includeExpression(rawArgs);
|
|
9240
|
+
if (expr === "") return [];
|
|
9241
|
+
const args2 = lexArguments(expr);
|
|
9242
|
+
if (args2.length === 1) return args2;
|
|
9243
|
+
return [{ type: 7 /* Expression */, text: expr, start: 0, end: expr.length }];
|
|
9244
|
+
}
|
|
9245
|
+
function includeExpression(rawArgs) {
|
|
9246
|
+
return rawArgs.replace(/\binline\b/, "").trim();
|
|
9247
|
+
}
|
|
8776
9248
|
function validateVariables(uri, workspace, diagnostics) {
|
|
8777
9249
|
if (!workspace.variables.hasStoryVariables()) {
|
|
8778
9250
|
const undeclared = workspace.variables.getUndeclared(uri);
|
|
@@ -8874,7 +9346,7 @@ function validateArrayMemberAccess(uri, workspace, diagnostics) {
|
|
|
8874
9346
|
));
|
|
8875
9347
|
}
|
|
8876
9348
|
}
|
|
8877
|
-
function
|
|
9349
|
+
function temporaryRefRegex2(name) {
|
|
8878
9350
|
const ident = name ? escapeRegex(name) : "[A-Za-z_$][\\w$]*";
|
|
8879
9351
|
return new RegExp(`(?<![.\\w$@%])_(${ident})(?![\\w$])`, "g");
|
|
8880
9352
|
}
|
|
@@ -8950,7 +9422,7 @@ function setTemporaryTargets(rawArgs) {
|
|
|
8950
9422
|
while ((m = assignRe.exec(code)) !== null) {
|
|
8951
9423
|
const name = m[1];
|
|
8952
9424
|
const rhs = code.slice(m.index + m[0].length, statementEnd(code, m.index + m[0].length));
|
|
8953
|
-
if (
|
|
9425
|
+
if (temporaryRefRegex2(name).test(rhs)) continue;
|
|
8954
9426
|
targets.push({ name, offset: m.index });
|
|
8955
9427
|
}
|
|
8956
9428
|
return targets;
|
|
@@ -9019,7 +9491,7 @@ function validateLoopTemporaries(uri, text, macros, workspace, diagnostics) {
|
|
|
9019
9491
|
}
|
|
9020
9492
|
}
|
|
9021
9493
|
function isReadInBody(body, bodyOffset, name, macroStart, macroEnd) {
|
|
9022
|
-
const re =
|
|
9494
|
+
const re = temporaryRefRegex2(name);
|
|
9023
9495
|
let m;
|
|
9024
9496
|
while ((m = re.exec(body)) !== null) {
|
|
9025
9497
|
const offset = bodyOffset + m.index;
|
|
@@ -9030,16 +9502,16 @@ function isReadInBody(body, bodyOffset, name, macroStart, macroEnd) {
|
|
|
9030
9502
|
}
|
|
9031
9503
|
return false;
|
|
9032
9504
|
}
|
|
9033
|
-
function
|
|
9034
|
-
const expr = rawArgs
|
|
9505
|
+
function resolveIncludeTarget2(rawArgs) {
|
|
9506
|
+
const expr = includeExpression(rawArgs);
|
|
9035
9507
|
if (expr === "") return null;
|
|
9036
9508
|
const literal2 = /^(["'`])((?:\\.|(?!\1)[^\\])*)\1$/s.exec(expr);
|
|
9037
9509
|
if (literal2) {
|
|
9038
9510
|
if (literal2[1] === "`" && literal2[2].includes("${")) return null;
|
|
9039
9511
|
return literal2[2].replace(/\\(.)/g, "$1");
|
|
9040
9512
|
}
|
|
9041
|
-
if (/[$@%"'`(]/.test(expr) ||
|
|
9042
|
-
if (
|
|
9513
|
+
if (/[$@%"'`(]/.test(expr) || temporaryRefRegex2().test(expr)) return null;
|
|
9514
|
+
if (EXPRESSION_BUILTINS2.has(expr)) return null;
|
|
9043
9515
|
return expr;
|
|
9044
9516
|
}
|
|
9045
9517
|
function widgetDefinitionNames(content) {
|
|
@@ -9058,7 +9530,7 @@ function validateWidgetIncludes(text, macros, workspace, diagnostics) {
|
|
|
9058
9530
|
for (const macro of macros) {
|
|
9059
9531
|
if (!macro.open || !macro.rawArgs || macro.name.toLowerCase() !== "include") continue;
|
|
9060
9532
|
lineStarts ??= buildLineStarts(text);
|
|
9061
|
-
const target =
|
|
9533
|
+
const target = resolveIncludeTarget2(sourceArgs(macro, text, lineStarts).args);
|
|
9062
9534
|
if (target === null) continue;
|
|
9063
9535
|
const passage = workspace.passages.getPassage(target);
|
|
9064
9536
|
if (!passage?.tags?.includes("widget")) continue;
|
|
@@ -9084,10 +9556,15 @@ function validateUnusedWidgets(uri, workspace, diagnostics) {
|
|
|
9084
9556
|
}
|
|
9085
9557
|
}
|
|
9086
9558
|
function validateLineLength(text, passages, maxLength, diagnostics) {
|
|
9087
|
-
const
|
|
9088
|
-
const
|
|
9559
|
+
const excludedLines = /* @__PURE__ */ new Set();
|
|
9560
|
+
for (const passage of passages.filter(isScriptOrStylesheetPassage)) {
|
|
9561
|
+
for (let i = passage.range.start.line; i <= passage.range.end.line; i++) {
|
|
9562
|
+
excludedLines.add(i);
|
|
9563
|
+
}
|
|
9564
|
+
}
|
|
9089
9565
|
const lines = text.split("\n");
|
|
9090
9566
|
for (let i = 0; i < lines.length; i++) {
|
|
9567
|
+
if (excludedLines.has(i)) continue;
|
|
9091
9568
|
const line = lines[i];
|
|
9092
9569
|
if (/^::\s+/.test(line)) continue;
|
|
9093
9570
|
if (/^\s*<[a-zA-Z]/.test(line)) continue;
|
|
@@ -9130,7 +9607,7 @@ function toLspDiagnostic(d) {
|
|
|
9130
9607
|
message: d.message
|
|
9131
9608
|
};
|
|
9132
9609
|
}
|
|
9133
|
-
var DEFERRED_CONTAINERS,
|
|
9610
|
+
var DEFERRED_CONTAINERS, EXPRESSION_BUILTINS2, diagnosticsPlugin;
|
|
9134
9611
|
var init_diagnostics = __esm({
|
|
9135
9612
|
"src/plugins/diagnostics.ts"() {
|
|
9136
9613
|
"use strict";
|
|
@@ -9139,8 +9616,9 @@ var init_diagnostics = __esm({
|
|
|
9139
9616
|
init_argument_lexer();
|
|
9140
9617
|
init_parameter_validator();
|
|
9141
9618
|
init_link_parser();
|
|
9619
|
+
init_passage_parser();
|
|
9142
9620
|
DEFERRED_CONTAINERS = /* @__PURE__ */ new Set(["link", "button"]);
|
|
9143
|
-
|
|
9621
|
+
EXPRESSION_BUILTINS2 = /* @__PURE__ */ new Set([
|
|
9144
9622
|
"currentPassage",
|
|
9145
9623
|
"previousPassage",
|
|
9146
9624
|
"visited",
|
|
@@ -9158,22 +9636,22 @@ var init_diagnostics = __esm({
|
|
|
9158
9636
|
id: "diagnostics",
|
|
9159
9637
|
capabilities: {},
|
|
9160
9638
|
initialize(ctx) {
|
|
9161
|
-
const
|
|
9162
|
-
|
|
9163
|
-
const diags = computeDiagnostics(uri, ctx.workspace);
|
|
9164
|
-
ctx.connection.sendDiagnostics({
|
|
9165
|
-
uri,
|
|
9166
|
-
diagnostics: diags.map((d) => toLspDiagnostic(d))
|
|
9167
|
-
});
|
|
9168
|
-
}
|
|
9169
|
-
};
|
|
9170
|
-
ctx.workspace.on("modelReady", publishDiagnostics);
|
|
9171
|
-
ctx.workspace.on("documentChanged", (uri) => {
|
|
9639
|
+
const isEnabled = (d) => ctx.config.diagnostics?.[d.code] !== false;
|
|
9640
|
+
const publishFor = (uri) => {
|
|
9172
9641
|
const diags = computeDiagnostics(uri, ctx.workspace);
|
|
9173
9642
|
ctx.connection.sendDiagnostics({
|
|
9174
9643
|
uri,
|
|
9175
|
-
diagnostics: diags.map((d) => toLspDiagnostic(d))
|
|
9644
|
+
diagnostics: diags.filter(isEnabled).map((d) => toLspDiagnostic(d))
|
|
9176
9645
|
});
|
|
9646
|
+
};
|
|
9647
|
+
ctx.workspace.on("modelReady", () => {
|
|
9648
|
+
for (const uri of ctx.workspace.documents.getUris()) {
|
|
9649
|
+
publishFor(uri);
|
|
9650
|
+
}
|
|
9651
|
+
});
|
|
9652
|
+
ctx.workspace.on("documentChanged", publishFor);
|
|
9653
|
+
ctx.workspace.on("documentClosed", (uri) => {
|
|
9654
|
+
ctx.connection.sendDiagnostics({ uri, diagnostics: [] });
|
|
9177
9655
|
});
|
|
9178
9656
|
}
|
|
9179
9657
|
};
|
|
@@ -9193,9 +9671,9 @@ var require_identity = __commonJS({
|
|
|
9193
9671
|
var NODE_TYPE = Symbol.for("yaml.node.type");
|
|
9194
9672
|
var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
|
|
9195
9673
|
var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
|
|
9196
|
-
var
|
|
9674
|
+
var isMap2 = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
|
|
9197
9675
|
var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR;
|
|
9198
|
-
var
|
|
9676
|
+
var isScalar2 = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR;
|
|
9199
9677
|
var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ;
|
|
9200
9678
|
function isCollection(node) {
|
|
9201
9679
|
if (node && typeof node === "object")
|
|
@@ -9217,7 +9695,7 @@ var require_identity = __commonJS({
|
|
|
9217
9695
|
}
|
|
9218
9696
|
return false;
|
|
9219
9697
|
}
|
|
9220
|
-
var hasAnchor = (node) => (
|
|
9698
|
+
var hasAnchor = (node) => (isScalar2(node) || isCollection(node)) && !!node.anchor;
|
|
9221
9699
|
exports.ALIAS = ALIAS;
|
|
9222
9700
|
exports.DOC = DOC;
|
|
9223
9701
|
exports.MAP = MAP;
|
|
@@ -9229,10 +9707,10 @@ var require_identity = __commonJS({
|
|
|
9229
9707
|
exports.isAlias = isAlias;
|
|
9230
9708
|
exports.isCollection = isCollection;
|
|
9231
9709
|
exports.isDocument = isDocument;
|
|
9232
|
-
exports.isMap =
|
|
9710
|
+
exports.isMap = isMap2;
|
|
9233
9711
|
exports.isNode = isNode;
|
|
9234
9712
|
exports.isPair = isPair;
|
|
9235
|
-
exports.isScalar =
|
|
9713
|
+
exports.isScalar = isScalar2;
|
|
9236
9714
|
exports.isSeq = isSeq;
|
|
9237
9715
|
}
|
|
9238
9716
|
});
|
|
@@ -13334,9 +13812,9 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
13334
13812
|
var blockMsg = "Block collections are not allowed within flow collections";
|
|
13335
13813
|
var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq");
|
|
13336
13814
|
function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) {
|
|
13337
|
-
const
|
|
13338
|
-
const fcName =
|
|
13339
|
-
const NodeClass = tag?.nodeClass ?? (
|
|
13815
|
+
const isMap2 = fc.start.source === "{";
|
|
13816
|
+
const fcName = isMap2 ? "flow map" : "flow sequence";
|
|
13817
|
+
const NodeClass = tag?.nodeClass ?? (isMap2 ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq);
|
|
13340
13818
|
const coll = new NodeClass(ctx.schema);
|
|
13341
13819
|
coll.flow = true;
|
|
13342
13820
|
const atRoot = ctx.atRoot;
|
|
@@ -13372,7 +13850,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
13372
13850
|
offset = props.end;
|
|
13373
13851
|
continue;
|
|
13374
13852
|
}
|
|
13375
|
-
if (!
|
|
13853
|
+
if (!isMap2 && ctx.options.strict && utilContainsNewline.containsNewline(key))
|
|
13376
13854
|
onError(
|
|
13377
13855
|
key,
|
|
13378
13856
|
// checked by containsNewline()
|
|
@@ -13412,7 +13890,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
13412
13890
|
}
|
|
13413
13891
|
}
|
|
13414
13892
|
}
|
|
13415
|
-
if (!
|
|
13893
|
+
if (!isMap2 && !sep2 && !props.found) {
|
|
13416
13894
|
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
|
|
13417
13895
|
coll.items.push(valueNode);
|
|
13418
13896
|
offset = valueNode.range[2];
|
|
@@ -13435,7 +13913,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
13435
13913
|
startOnNewline: false
|
|
13436
13914
|
});
|
|
13437
13915
|
if (valueProps.found) {
|
|
13438
|
-
if (!
|
|
13916
|
+
if (!isMap2 && !props.found && ctx.options.strict) {
|
|
13439
13917
|
if (sep2)
|
|
13440
13918
|
for (const st of sep2) {
|
|
13441
13919
|
if (st === valueProps.found)
|
|
@@ -13467,7 +13945,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
13467
13945
|
const pair = new Pair.Pair(keyNode, valueNode);
|
|
13468
13946
|
if (ctx.options.keepSourceTokens)
|
|
13469
13947
|
pair.srcToken = collItem;
|
|
13470
|
-
if (
|
|
13948
|
+
if (isMap2) {
|
|
13471
13949
|
const map2 = coll;
|
|
13472
13950
|
if (utilMapIncludes.mapIncludes(ctx, map2.items, keyNode))
|
|
13473
13951
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
@@ -13483,7 +13961,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
13483
13961
|
offset = valueNode ? valueNode.range[2] : valueProps.end;
|
|
13484
13962
|
}
|
|
13485
13963
|
}
|
|
13486
|
-
const expectedEnd =
|
|
13964
|
+
const expectedEnd = isMap2 ? "}" : "]";
|
|
13487
13965
|
const [ce, ...ee] = fc.end;
|
|
13488
13966
|
let cePos = offset;
|
|
13489
13967
|
if (ce?.source === expectedEnd)
|
|
@@ -14766,7 +15244,7 @@ var require_cst = __commonJS({
|
|
|
14766
15244
|
var FLOW_END = "";
|
|
14767
15245
|
var SCALAR = "";
|
|
14768
15246
|
var isCollection = (token) => !!token && "items" in token;
|
|
14769
|
-
var
|
|
15247
|
+
var isScalar2 = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar");
|
|
14770
15248
|
function prettyToken(token) {
|
|
14771
15249
|
switch (token) {
|
|
14772
15250
|
case BOM:
|
|
@@ -14850,7 +15328,7 @@ var require_cst = __commonJS({
|
|
|
14850
15328
|
exports.FLOW_END = FLOW_END;
|
|
14851
15329
|
exports.SCALAR = SCALAR;
|
|
14852
15330
|
exports.isCollection = isCollection;
|
|
14853
|
-
exports.isScalar =
|
|
15331
|
+
exports.isScalar = isScalar2;
|
|
14854
15332
|
exports.prettyToken = prettyToken;
|
|
14855
15333
|
exports.tokenType = tokenType;
|
|
14856
15334
|
}
|
|
@@ -16363,7 +16841,7 @@ var require_public_api = __commonJS({
|
|
|
16363
16841
|
return docs;
|
|
16364
16842
|
return Object.assign([], { empty: true }, composer$1.streamInfo());
|
|
16365
16843
|
}
|
|
16366
|
-
function
|
|
16844
|
+
function parseDocument2(source, options = {}) {
|
|
16367
16845
|
const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
|
|
16368
16846
|
const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
|
|
16369
16847
|
const composer$1 = new composer.Composer(options);
|
|
@@ -16389,7 +16867,7 @@ var require_public_api = __commonJS({
|
|
|
16389
16867
|
} else if (options === void 0 && reviver && typeof reviver === "object") {
|
|
16390
16868
|
options = reviver;
|
|
16391
16869
|
}
|
|
16392
|
-
const doc =
|
|
16870
|
+
const doc = parseDocument2(src, options);
|
|
16393
16871
|
if (!doc)
|
|
16394
16872
|
return null;
|
|
16395
16873
|
doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
|
|
@@ -16425,7 +16903,7 @@ var require_public_api = __commonJS({
|
|
|
16425
16903
|
}
|
|
16426
16904
|
exports.parse = parse3;
|
|
16427
16905
|
exports.parseAllDocuments = parseAllDocuments;
|
|
16428
|
-
exports.parseDocument =
|
|
16906
|
+
exports.parseDocument = parseDocument2;
|
|
16429
16907
|
exports.stringify = stringify;
|
|
16430
16908
|
}
|
|
16431
16909
|
});
|
|
@@ -16515,6 +16993,22 @@ function findConfigFile(workspaceRoot) {
|
|
|
16515
16993
|
}
|
|
16516
16994
|
return null;
|
|
16517
16995
|
}
|
|
16996
|
+
function loadConfigFile(configPath) {
|
|
16997
|
+
let content;
|
|
16998
|
+
try {
|
|
16999
|
+
content = readFileSync3(configPath, "utf-8");
|
|
17000
|
+
} catch (err) {
|
|
17001
|
+
const reason = err?.code === "ENOENT" ? "file not found" : err instanceof Error ? err.message : String(err);
|
|
17002
|
+
throw new Error(`Cannot read config file '${configPath}': ${reason}`);
|
|
17003
|
+
}
|
|
17004
|
+
const format = configPath.toLowerCase().endsWith(".json") ? "json" : "yaml";
|
|
17005
|
+
try {
|
|
17006
|
+
return parseConfig(content, format);
|
|
17007
|
+
} catch (err) {
|
|
17008
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
17009
|
+
throw new Error(`Invalid ${format.toUpperCase()} in config file '${configPath}': ${reason}`);
|
|
17010
|
+
}
|
|
17011
|
+
}
|
|
16518
17012
|
function loadConfigFromDisk(workspaceRoot) {
|
|
16519
17013
|
const configPath = findConfigFile(workspaceRoot);
|
|
16520
17014
|
if (!configPath) {
|
|
@@ -16782,6 +17276,15 @@ function filterBySeverity(diagnostics, minSeverity) {
|
|
|
16782
17276
|
async function runCheck(args2) {
|
|
16783
17277
|
const options = parseArgs(args2);
|
|
16784
17278
|
const cwd = process.cwd();
|
|
17279
|
+
let explicitConfig = null;
|
|
17280
|
+
if (options.configPath) {
|
|
17281
|
+
try {
|
|
17282
|
+
explicitConfig = loadConfigFile(resolve(cwd, options.configPath));
|
|
17283
|
+
} catch (err) {
|
|
17284
|
+
console.error(`spindle-lsp check: ${err instanceof Error ? err.message : String(err)}`);
|
|
17285
|
+
return 2;
|
|
17286
|
+
}
|
|
17287
|
+
}
|
|
16785
17288
|
const files = [];
|
|
16786
17289
|
for (const pattern of options.patterns) {
|
|
16787
17290
|
const matches = await glob(pattern, {
|
|
@@ -16802,12 +17305,12 @@ async function runCheck(args2) {
|
|
|
16802
17305
|
}
|
|
16803
17306
|
return 0;
|
|
16804
17307
|
}
|
|
16805
|
-
let
|
|
16806
|
-
if (
|
|
16807
|
-
|
|
17308
|
+
let projectConfig;
|
|
17309
|
+
if (explicitConfig) {
|
|
17310
|
+
projectConfig = explicitConfig;
|
|
16808
17311
|
} else {
|
|
16809
17312
|
const dirs = uniqueFiles.map((f) => resolve(f, ".."));
|
|
16810
|
-
configRoot = dirs.reduce((a, b) => {
|
|
17313
|
+
let configRoot = dirs.reduce((a, b) => {
|
|
16811
17314
|
while (!b.startsWith(a)) a = resolve(a, "..");
|
|
16812
17315
|
return a;
|
|
16813
17316
|
});
|
|
@@ -16821,8 +17324,8 @@ async function runCheck(args2) {
|
|
|
16821
17324
|
if (parent === search) break;
|
|
16822
17325
|
search = parent;
|
|
16823
17326
|
}
|
|
17327
|
+
projectConfig = loadConfigFromDisk(configRoot);
|
|
16824
17328
|
}
|
|
16825
|
-
const projectConfig = loadConfigFromDisk(configRoot);
|
|
16826
17329
|
const workspace = new WorkspaceModel();
|
|
16827
17330
|
const fileContents = /* @__PURE__ */ new Map();
|
|
16828
17331
|
for (const filePath of uniqueFiles) {
|
|
@@ -16835,7 +17338,7 @@ async function runCheck(args2) {
|
|
|
16835
17338
|
}
|
|
16836
17339
|
workspace.initialize(fileContents);
|
|
16837
17340
|
if (Object.keys(projectConfig.macros).length > 0) {
|
|
16838
|
-
workspace.macros.
|
|
17341
|
+
workspace.macros.loadConfig(projectConfig.macros);
|
|
16839
17342
|
}
|
|
16840
17343
|
const diagOptions = {};
|
|
16841
17344
|
if (options.maxLineLength !== null) {
|
|
@@ -16922,7 +17425,7 @@ function splitPassages(text) {
|
|
|
16922
17425
|
function classifyPassage(header) {
|
|
16923
17426
|
const tagMatch = header.match(/\[([^\]]*)\]/g);
|
|
16924
17427
|
if (!tagMatch) return "normal";
|
|
16925
|
-
const tags = tagMatch.
|
|
17428
|
+
const tags = tagMatch.flatMap((t) => t.slice(1, -1).toLowerCase().split(/\s+/));
|
|
16926
17429
|
if (tags.includes("script")) return "script";
|
|
16927
17430
|
if (tags.includes("stylesheet")) return "stylesheet";
|
|
16928
17431
|
return "normal";
|
|
@@ -16941,6 +17444,10 @@ function segmentRegions(body) {
|
|
|
16941
17444
|
if (/^<script(\s|>)/i.test(trimmed)) {
|
|
16942
17445
|
const scriptLines = [line];
|
|
16943
17446
|
i++;
|
|
17447
|
+
if (/<\/script\s*>/i.test(line)) {
|
|
17448
|
+
regions.push({ type: "script", lines: scriptLines });
|
|
17449
|
+
continue;
|
|
17450
|
+
}
|
|
16944
17451
|
while (i < lines.length && !/<\/script>/i.test(lines[i])) {
|
|
16945
17452
|
scriptLines.push(lines[i]);
|
|
16946
17453
|
i++;
|
|
@@ -17165,7 +17672,7 @@ function replaceSvgBlocks(html) {
|
|
|
17165
17672
|
function restoreSvgBlocks(text, tokens) {
|
|
17166
17673
|
let result = text;
|
|
17167
17674
|
for (let i = 0; i < tokens.length; i++) {
|
|
17168
|
-
result = result.replace(`<!--SVG:${i}-->`, tokens[i]);
|
|
17675
|
+
result = result.replace(`<!--SVG:${i}-->`, () => tokens[i]);
|
|
17169
17676
|
}
|
|
17170
17677
|
return result;
|
|
17171
17678
|
}
|
|
@@ -17213,8 +17720,8 @@ function replaceSpindleTokens(html) {
|
|
|
17213
17720
|
function restoreSpindleTokens(text, tokens) {
|
|
17214
17721
|
let result = text;
|
|
17215
17722
|
for (let i = 0; i < tokens.length; i++) {
|
|
17216
|
-
result = result.replace(`<!--SP:${i}-->`, tokens[i]);
|
|
17217
|
-
result = result.replace(`__SP${i}__`, tokens[i]);
|
|
17723
|
+
result = result.replace(`<!--SP:${i}-->`, () => tokens[i]);
|
|
17724
|
+
result = result.replace(`__SP${i}__`, () => tokens[i]);
|
|
17218
17725
|
}
|
|
17219
17726
|
return result;
|
|
17220
17727
|
}
|
|
@@ -17257,6 +17764,7 @@ async function formatDocument(text, options) {
|
|
|
17257
17764
|
const isDedenting = options?.isDedentingSubMacro ?? ((name) => DEFAULT_DEDENTING.has(name.toLowerCase()));
|
|
17258
17765
|
const passages = splitPassages(text);
|
|
17259
17766
|
const resultLines = [];
|
|
17767
|
+
const hardBreakLines = /* @__PURE__ */ new Set();
|
|
17260
17768
|
for (let pi = 0; pi < passages.length; pi++) {
|
|
17261
17769
|
const passage = passages[pi];
|
|
17262
17770
|
if (pi > 0 && passage.header) {
|
|
@@ -17285,6 +17793,10 @@ async function formatDocument(text, options) {
|
|
|
17285
17793
|
const regions = segmentRegions(passage.body);
|
|
17286
17794
|
for (const region of regions) {
|
|
17287
17795
|
if (region.type === "script") {
|
|
17796
|
+
if (region.lines.length === 1) {
|
|
17797
|
+
resultLines.push(region.lines[0]);
|
|
17798
|
+
continue;
|
|
17799
|
+
}
|
|
17288
17800
|
const firstLine = region.lines[0];
|
|
17289
17801
|
const lastLine = region.lines[region.lines.length - 1];
|
|
17290
17802
|
const innerLines = region.lines.slice(1, region.lines.length - 1);
|
|
@@ -17319,49 +17831,92 @@ async function formatDocument(text, options) {
|
|
|
17319
17831
|
if (options?.maxLineLength) {
|
|
17320
17832
|
indented = wrapLines(indented, options.maxLineLength);
|
|
17321
17833
|
}
|
|
17322
|
-
|
|
17834
|
+
for (const line of indented) {
|
|
17835
|
+
if (line.endsWith(" ")) hardBreakLines.add(resultLines.length);
|
|
17836
|
+
resultLines.push(line);
|
|
17837
|
+
}
|
|
17323
17838
|
}
|
|
17324
17839
|
}
|
|
17325
|
-
let output = resultLines.map((l) => l.replace(/\s+$/, "")).join("\n");
|
|
17840
|
+
let output = resultLines.map((l, i) => hardBreakLines.has(i) ? l : l.replace(/\s+$/, "")).join("\n");
|
|
17326
17841
|
output = output.replace(/\n*$/, "\n");
|
|
17327
17842
|
return output;
|
|
17328
17843
|
}
|
|
17329
17844
|
function indentMacros(lines, isBlock, isDedenting) {
|
|
17330
|
-
const
|
|
17331
|
-
|
|
17332
|
-
|
|
17333
|
-
|
|
17334
|
-
|
|
17845
|
+
const bodies = [{ openLine: -1, minCol: Infinity, keepRelative: false }];
|
|
17846
|
+
const stack = [0];
|
|
17847
|
+
const info = [];
|
|
17848
|
+
const openBody = (line) => {
|
|
17849
|
+
bodies.push({ openLine: line, minCol: Infinity, keepRelative: false });
|
|
17850
|
+
stack.push(bodies.length - 1);
|
|
17851
|
+
};
|
|
17852
|
+
const closeBody = () => stack.length > 1 ? stack.pop() : void 0;
|
|
17853
|
+
const addLine = (col, text) => {
|
|
17854
|
+
const body = bodies[stack[stack.length - 1]];
|
|
17855
|
+
body.minCol = Math.min(body.minCol, col);
|
|
17856
|
+
if (LIST_ITEM_REGEX.test(text) || CODE_FENCE_REGEX.test(text)) body.keepRelative = true;
|
|
17857
|
+
info.push({ body: stack[stack.length - 1], col, text });
|
|
17858
|
+
};
|
|
17859
|
+
for (let i = 0; i < lines.length; i++) {
|
|
17860
|
+
const trimmed = lines[i].trim();
|
|
17335
17861
|
if (trimmed === "") {
|
|
17336
|
-
|
|
17862
|
+
info.push(null);
|
|
17337
17863
|
continue;
|
|
17338
17864
|
}
|
|
17865
|
+
const col = indentColumn(lines[i]);
|
|
17339
17866
|
const dedentMatch = trimmed.match(MACRO_OPEN_REGEX);
|
|
17340
17867
|
if (dedentMatch && isDedenting(dedentMatch[1])) {
|
|
17341
|
-
|
|
17342
|
-
|
|
17343
|
-
|
|
17868
|
+
const closed2 = closeBody();
|
|
17869
|
+
if (closed2 === void 0) addLine(col, trimmed);
|
|
17870
|
+
else info.push({ body: stack[stack.length - 1], closes: closed2, col, text: trimmed });
|
|
17871
|
+
openBody(i);
|
|
17344
17872
|
continue;
|
|
17345
17873
|
}
|
|
17346
|
-
const
|
|
17347
|
-
if (
|
|
17348
|
-
|
|
17349
|
-
}
|
|
17350
|
-
result.push(indentLevel > 0 ? " ".repeat(indentLevel) + trimmed : trimmed);
|
|
17874
|
+
const closed = MACRO_CLOSE_REGEX.test(trimmed) ? closeBody() : void 0;
|
|
17875
|
+
if (closed === void 0) addLine(col, trimmed);
|
|
17876
|
+
else info.push({ body: stack[stack.length - 1], closes: closed, col, text: trimmed });
|
|
17351
17877
|
const openMatch = trimmed.match(MACRO_OPEN_REGEX);
|
|
17352
17878
|
if (openMatch && isBlock(openMatch[1])) {
|
|
17353
|
-
|
|
17879
|
+
openBody(i);
|
|
17354
17880
|
}
|
|
17355
17881
|
const closeGlobal = /\{\/[A-Za-z][\w-]*\s*\}/g;
|
|
17356
17882
|
let cm;
|
|
17357
17883
|
while ((cm = closeGlobal.exec(trimmed)) !== null) {
|
|
17358
|
-
if (cm.index > 0)
|
|
17359
|
-
|
|
17360
|
-
|
|
17884
|
+
if (cm.index > 0) closeBody();
|
|
17885
|
+
}
|
|
17886
|
+
}
|
|
17887
|
+
const outCol = [];
|
|
17888
|
+
const result = [];
|
|
17889
|
+
for (let i = 0; i < info.length; i++) {
|
|
17890
|
+
const line = info[i];
|
|
17891
|
+
if (!line) {
|
|
17892
|
+
outCol.push(0);
|
|
17893
|
+
result.push("");
|
|
17894
|
+
continue;
|
|
17895
|
+
}
|
|
17896
|
+
let col;
|
|
17897
|
+
if (line.closes !== void 0) {
|
|
17898
|
+
col = outCol[bodies[line.closes].openLine];
|
|
17899
|
+
} else {
|
|
17900
|
+
const body = bodies[line.body];
|
|
17901
|
+
const isRoot = body.openLine === -1;
|
|
17902
|
+
const base = isRoot ? 0 : outCol[body.openLine] + 2;
|
|
17903
|
+
col = base + (body.keepRelative ? line.col - (isRoot ? 0 : body.minCol) : 0);
|
|
17361
17904
|
}
|
|
17905
|
+
outCol.push(col);
|
|
17906
|
+
const hardBreak = / {2,}$/.test(lines[i]) && info[i + 1] ? " " : "";
|
|
17907
|
+
result.push(" ".repeat(col) + line.text + hardBreak);
|
|
17362
17908
|
}
|
|
17363
17909
|
return result;
|
|
17364
17910
|
}
|
|
17911
|
+
function indentColumn(line) {
|
|
17912
|
+
let col = 0;
|
|
17913
|
+
for (const ch of line) {
|
|
17914
|
+
if (ch === " ") col++;
|
|
17915
|
+
else if (ch === " ") col += 4 - col % 4;
|
|
17916
|
+
else break;
|
|
17917
|
+
}
|
|
17918
|
+
return col;
|
|
17919
|
+
}
|
|
17365
17920
|
async function formatRange(text, _range, options) {
|
|
17366
17921
|
return formatDocument(text, options);
|
|
17367
17922
|
}
|
|
@@ -17383,6 +17938,7 @@ function wrapLines(lines, maxLen) {
|
|
|
17383
17938
|
const indentMatch = line.match(/^(\s*)/);
|
|
17384
17939
|
const indent = indentMatch ? indentMatch[1] : "";
|
|
17385
17940
|
const wrapped = wordWrap(trimmed, maxLen, indent);
|
|
17941
|
+
if (line.endsWith(" ")) wrapped[wrapped.length - 1] += " ";
|
|
17386
17942
|
result.push(...wrapped);
|
|
17387
17943
|
}
|
|
17388
17944
|
return result;
|
|
@@ -17443,7 +17999,7 @@ function normalizePassageHeader(line) {
|
|
|
17443
17999
|
}
|
|
17444
18000
|
return `:: ${name}${suffix}`;
|
|
17445
18001
|
}
|
|
17446
|
-
var PASSAGE_HEADER_REGEX2, MACRO_OPEN_REGEX, MACRO_CLOSE_REGEX, DEFAULT_DEDENTING, formatPlugin;
|
|
18002
|
+
var PASSAGE_HEADER_REGEX2, MACRO_OPEN_REGEX, MACRO_CLOSE_REGEX, DEFAULT_DEDENTING, LIST_ITEM_REGEX, CODE_FENCE_REGEX, formatPlugin;
|
|
17447
18003
|
var init_format = __esm({
|
|
17448
18004
|
"src/plugins/format.ts"() {
|
|
17449
18005
|
"use strict";
|
|
@@ -17455,6 +18011,8 @@ var init_format = __esm({
|
|
|
17455
18011
|
MACRO_OPEN_REGEX = /^\{(?:[#.][a-zA-Z][\w-]*\s*)*([A-Za-z][\w-]*)\b/;
|
|
17456
18012
|
MACRO_CLOSE_REGEX = /^\{\/([A-Za-z][\w-]*)\b/;
|
|
17457
18013
|
DEFAULT_DEDENTING = /* @__PURE__ */ new Set(["else", "elseif", "next", "case", "default"]);
|
|
18014
|
+
LIST_ITEM_REGEX = /^(?:[-+*]|\d{1,9}[.)])(?:[ \t]|$)/;
|
|
18015
|
+
CODE_FENCE_REGEX = /^(?:`{3,}|~{3,})/;
|
|
17458
18016
|
formatPlugin = {
|
|
17459
18017
|
id: "format",
|
|
17460
18018
|
capabilities: {
|
|
@@ -48795,7 +49353,7 @@ function createWorkspace(files) {
|
|
|
48795
49353
|
}
|
|
48796
49354
|
workspace.initialize(fileContents);
|
|
48797
49355
|
if (Object.keys(projectConfig.macros).length > 0) {
|
|
48798
|
-
workspace.macros.
|
|
49356
|
+
workspace.macros.loadConfig(projectConfig.macros);
|
|
48799
49357
|
}
|
|
48800
49358
|
return workspace;
|
|
48801
49359
|
}
|
|
@@ -58061,7 +58619,7 @@ function getCompletions(uri, position, triggerChar, workspace) {
|
|
|
58061
58619
|
if (position.line >= lines.length) return [];
|
|
58062
58620
|
const lineText = lines[position.line].substring(0, position.character);
|
|
58063
58621
|
if (/\{\/[A-Za-z\w-]*$/.test(lineText)) {
|
|
58064
|
-
return getClosingMacroCompletions(text, position, workspace);
|
|
58622
|
+
return getClosingMacroCompletions(uri, text, position, workspace);
|
|
58065
58623
|
}
|
|
58066
58624
|
const transientDotPathMatch = /%([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)?$/.exec(lineText);
|
|
58067
58625
|
if (transientDotPathMatch) {
|
|
@@ -58091,11 +58649,18 @@ function getCompletions(uri, position, triggerChar, workspace) {
|
|
|
58091
58649
|
}
|
|
58092
58650
|
return [];
|
|
58093
58651
|
}
|
|
58094
|
-
function getClosingMacroCompletions(text, position, workspace) {
|
|
58652
|
+
function getClosingMacroCompletions(uri, text, position, workspace) {
|
|
58653
|
+
const passages = workspace.passages.getPassagesInDocument(uri);
|
|
58095
58654
|
const macros = parseMacros(text);
|
|
58096
|
-
pairMacros(
|
|
58655
|
+
pairMacros(
|
|
58656
|
+
macros,
|
|
58657
|
+
(name) => workspace.macros.isBlock(name),
|
|
58658
|
+
passages.map((p) => p.range.start.line)
|
|
58659
|
+
);
|
|
58660
|
+
const passageStart = workspace.passages.getPassageAt(uri, position.line)?.range.start.line ?? 0;
|
|
58097
58661
|
const openStack = [];
|
|
58098
58662
|
for (const macro of macros) {
|
|
58663
|
+
if (macro.range.start.line < passageStart) continue;
|
|
58099
58664
|
if (macro.range.start.line > position.line || macro.range.start.line === position.line && macro.range.start.character >= position.character) {
|
|
58100
58665
|
break;
|
|
58101
58666
|
}
|
|
@@ -58146,7 +58711,7 @@ function getMacroCompletions(workspace) {
|
|
|
58146
58711
|
kind: 3,
|
|
58147
58712
|
// CompletionItemKind.Function
|
|
58148
58713
|
detail: `(widget) ${widget.name}`,
|
|
58149
|
-
documentation: widget.params.length > 0 ? `Parameters: ${widget.params.
|
|
58714
|
+
documentation: widget.params.length > 0 ? `Parameters: ${widget.params.join(", ")}` : void 0
|
|
58150
58715
|
});
|
|
58151
58716
|
}
|
|
58152
58717
|
return items;
|
|
@@ -58421,7 +58986,7 @@ function getWidgetHover(line, position, workspace) {
|
|
|
58421
58986
|
return null;
|
|
58422
58987
|
}
|
|
58423
58988
|
function buildWidgetHover(widget, line, nameStart, nameEnd) {
|
|
58424
|
-
const sig = widget.params.length > 0 ? widget.params.
|
|
58989
|
+
const sig = widget.params.length > 0 ? widget.params.join(", ") : "no parameters";
|
|
58425
58990
|
return {
|
|
58426
58991
|
contents: `**Widget** \`${widget.name}\`
|
|
58427
58992
|
|
|
@@ -58495,7 +59060,7 @@ function getSignatureHelp(uri, position, workspace) {
|
|
|
58495
59060
|
}
|
|
58496
59061
|
const widget = workspace.widgets.getWidget(macroName);
|
|
58497
59062
|
if (widget && widget.params.length > 0) {
|
|
58498
|
-
const paramLabels = widget.params
|
|
59063
|
+
const paramLabels = widget.params;
|
|
58499
59064
|
return {
|
|
58500
59065
|
signatures: [{
|
|
58501
59066
|
label: `{${macroName} ${paramLabels.join(", ")}}`,
|
|
@@ -58551,53 +59116,21 @@ function getDefinition(uri, position, workspace) {
|
|
|
58551
59116
|
const lines = text.split("\n");
|
|
58552
59117
|
if (position.line >= lines.length) return null;
|
|
58553
59118
|
const line = lines[position.line];
|
|
58554
|
-
const
|
|
58555
|
-
if (
|
|
58556
|
-
const macroPassageResult = getMacroPassageDefinition(line, position, workspace);
|
|
58557
|
-
if (macroPassageResult) return macroPassageResult;
|
|
59119
|
+
const passageResult = getPassageRefDefinition(text, position, workspace);
|
|
59120
|
+
if (passageResult) return passageResult;
|
|
58558
59121
|
const widgetResult = getWidgetDefinition(line, position, workspace);
|
|
58559
59122
|
if (widgetResult) return widgetResult;
|
|
58560
59123
|
return null;
|
|
58561
59124
|
}
|
|
58562
|
-
function
|
|
58563
|
-
const
|
|
58564
|
-
|
|
58565
|
-
|
|
58566
|
-
|
|
58567
|
-
|
|
58568
|
-
|
|
58569
|
-
|
|
58570
|
-
|
|
58571
|
-
};
|
|
58572
|
-
}
|
|
58573
|
-
}
|
|
58574
|
-
}
|
|
58575
|
-
return null;
|
|
58576
|
-
}
|
|
58577
|
-
function getMacroPassageDefinition(line, position, workspace) {
|
|
58578
|
-
const patterns = [
|
|
58579
|
-
/\{(?:goto|include)\s+"([^"]+)"\s*\}/gi,
|
|
58580
|
-
/\{(?:link|button)\s+"[^"]*"\s+"([^"]+)"\s*\}/gi
|
|
58581
|
-
];
|
|
58582
|
-
for (const pattern of patterns) {
|
|
58583
|
-
pattern.lastIndex = 0;
|
|
58584
|
-
let match2;
|
|
58585
|
-
while ((match2 = pattern.exec(line)) !== null) {
|
|
58586
|
-
const passageName = match2[1];
|
|
58587
|
-
const nameStart = match2.index + match2[0].indexOf(passageName);
|
|
58588
|
-
const nameEnd = nameStart + passageName.length;
|
|
58589
|
-
if (position.character >= nameStart && position.character <= nameEnd) {
|
|
58590
|
-
const passage = workspace.passages.getPassage(passageName);
|
|
58591
|
-
if (passage) {
|
|
58592
|
-
return {
|
|
58593
|
-
uri: passage.uri,
|
|
58594
|
-
range: passage.headerEnd
|
|
58595
|
-
};
|
|
58596
|
-
}
|
|
58597
|
-
}
|
|
58598
|
-
}
|
|
58599
|
-
}
|
|
58600
|
-
return null;
|
|
59125
|
+
function getPassageRefDefinition(text, position, workspace) {
|
|
59126
|
+
const ref = findPassageRefAt(text, position);
|
|
59127
|
+
if (!ref) return null;
|
|
59128
|
+
const passage = workspace.passages.getPassage(ref.name);
|
|
59129
|
+
if (!passage) return null;
|
|
59130
|
+
return {
|
|
59131
|
+
uri: passage.uri,
|
|
59132
|
+
range: passage.headerEnd
|
|
59133
|
+
};
|
|
58601
59134
|
}
|
|
58602
59135
|
function getWidgetDefinition(line, position, workspace) {
|
|
58603
59136
|
const re = /\{([A-Za-z_$][\w$]*)/g;
|
|
@@ -58697,19 +59230,25 @@ function findReferences(uri, position, workspace, includeDeclaration) {
|
|
|
58697
59230
|
}
|
|
58698
59231
|
}
|
|
58699
59232
|
{
|
|
58700
|
-
const widgetRegex = /\{([A-Za-z_$][\w$]*)/g;
|
|
59233
|
+
const widgetRegex = /\{\/?([A-Za-z_$][\w$]*)/g;
|
|
58701
59234
|
let match2;
|
|
58702
59235
|
while ((match2 = widgetRegex.exec(line)) !== null) {
|
|
58703
59236
|
const name = match2[1];
|
|
58704
|
-
const nameStart = match2.index +
|
|
59237
|
+
const nameStart = match2.index + match2[0].length - name.length;
|
|
58705
59238
|
const nameEnd = nameStart + name.length;
|
|
58706
59239
|
if (position.character >= nameStart && position.character <= nameEnd) {
|
|
58707
|
-
|
|
59240
|
+
const widget = workspace.widgets.getWidget(name);
|
|
59241
|
+
const isClosing = match2[0][1] === "/";
|
|
59242
|
+
if (!workspace.macros.getMacro(name) && widget && (!isClosing || widget.block)) {
|
|
58708
59243
|
return findWidgetReferences(name, workspace, includeDeclaration);
|
|
58709
59244
|
}
|
|
58710
59245
|
}
|
|
58711
59246
|
}
|
|
58712
59247
|
}
|
|
59248
|
+
const passageRef = findPassageRefAt(text, position);
|
|
59249
|
+
if (passageRef) {
|
|
59250
|
+
return findPassageReferences(passageRef.name, workspace, includeDeclaration);
|
|
59251
|
+
}
|
|
58713
59252
|
{
|
|
58714
59253
|
const allPassages = workspace.passages.getAllPassages();
|
|
58715
59254
|
const passageNames = new Set(allPassages.map((p) => p.name));
|
|
@@ -58735,39 +59274,16 @@ function findPassageReferences(passageName, workspace, includeDeclaration) {
|
|
|
58735
59274
|
if (passage) {
|
|
58736
59275
|
locations.push({
|
|
58737
59276
|
uri: passage.uri,
|
|
58738
|
-
range: passage.
|
|
59277
|
+
range: passage.nameRange
|
|
58739
59278
|
});
|
|
58740
59279
|
}
|
|
58741
59280
|
}
|
|
58742
|
-
const gotoRegex = /\{(?:goto|include)\s+"([^"]+)"\s*\}/gi;
|
|
58743
|
-
const linkMacroRegex = /\{(?:link|button)\s+"[^"]*"\s+"([^"]+)"\s*\}/gi;
|
|
58744
59281
|
for (const docUri of workspace.documents.getUris()) {
|
|
58745
59282
|
const docText = workspace.documents.getText(docUri);
|
|
58746
59283
|
if (!docText) continue;
|
|
58747
|
-
const
|
|
58748
|
-
|
|
58749
|
-
|
|
58750
|
-
locations.push({ uri: docUri, range: link.range });
|
|
58751
|
-
}
|
|
58752
|
-
}
|
|
58753
|
-
const lines = docText.split("\n");
|
|
58754
|
-
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
58755
|
-
const line = lines[lineNum];
|
|
58756
|
-
for (const regex of [gotoRegex, linkMacroRegex]) {
|
|
58757
|
-
regex.lastIndex = 0;
|
|
58758
|
-
let match2;
|
|
58759
|
-
while ((match2 = regex.exec(line)) !== null) {
|
|
58760
|
-
if (match2[1] === passageName) {
|
|
58761
|
-
const nameStart = match2.index + match2[0].indexOf(match2[1]);
|
|
58762
|
-
locations.push({
|
|
58763
|
-
uri: docUri,
|
|
58764
|
-
range: {
|
|
58765
|
-
start: { line: lineNum, character: nameStart },
|
|
58766
|
-
end: { line: lineNum, character: nameStart + match2[1].length }
|
|
58767
|
-
}
|
|
58768
|
-
});
|
|
58769
|
-
}
|
|
58770
|
-
}
|
|
59284
|
+
for (const ref of [...parseLinks(docText), ...parseMacroPassageRefs(docText)]) {
|
|
59285
|
+
if (ref.name === passageName) {
|
|
59286
|
+
locations.push({ uri: docUri, range: ref.range });
|
|
58771
59287
|
}
|
|
58772
59288
|
}
|
|
58773
59289
|
}
|
|
@@ -58806,30 +59322,30 @@ function findTransientReferences(varName, workspace, includeDeclaration) {
|
|
|
58806
59322
|
}
|
|
58807
59323
|
function findWidgetReferences(widgetName, workspace, includeDeclaration) {
|
|
58808
59324
|
const locations = [];
|
|
58809
|
-
|
|
58810
|
-
|
|
58811
|
-
|
|
58812
|
-
locations.push({ uri: widget.uri, range: widget.range });
|
|
58813
|
-
}
|
|
59325
|
+
const widget = workspace.widgets.getWidget(widgetName);
|
|
59326
|
+
if (includeDeclaration && widget) {
|
|
59327
|
+
locations.push({ uri: widget.uri, range: widget.range });
|
|
58814
59328
|
}
|
|
58815
|
-
const widgetInvocationRegex = /\{([A-Za-z_$][\w$]*)\b/g;
|
|
59329
|
+
const widgetInvocationRegex = /\{(\/)?([A-Za-z_$][\w$]*)\b/g;
|
|
59330
|
+
const lowerName = widgetName.toLowerCase();
|
|
59331
|
+
const isBlock = widget?.block ?? false;
|
|
58816
59332
|
for (const docUri of workspace.documents.getUris()) {
|
|
58817
59333
|
const docText = workspace.documents.getText(docUri);
|
|
58818
59334
|
if (!docText) continue;
|
|
58819
59335
|
const lines = docText.split("\n");
|
|
58820
59336
|
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
58821
59337
|
const line = lines[lineNum];
|
|
58822
|
-
if (/\{widget\s+"/i.test(line)) continue;
|
|
58823
59338
|
widgetInvocationRegex.lastIndex = 0;
|
|
58824
59339
|
let match2;
|
|
58825
59340
|
while ((match2 = widgetInvocationRegex.exec(line)) !== null) {
|
|
58826
|
-
if (match2[1]
|
|
58827
|
-
|
|
59341
|
+
if (match2[1] && !isBlock) continue;
|
|
59342
|
+
if (match2[2].toLowerCase() === lowerName) {
|
|
59343
|
+
const nameStart = match2.index + 1 + (match2[1] ? 1 : 0);
|
|
58828
59344
|
locations.push({
|
|
58829
59345
|
uri: docUri,
|
|
58830
59346
|
range: {
|
|
58831
59347
|
start: { line: lineNum, character: nameStart },
|
|
58832
|
-
end: { line: lineNum, character: nameStart +
|
|
59348
|
+
end: { line: lineNum, character: nameStart + match2[2].length }
|
|
58833
59349
|
}
|
|
58834
59350
|
});
|
|
58835
59351
|
}
|
|
@@ -58897,10 +59413,13 @@ function computeRename(uri, position, newName, workspace) {
|
|
|
58897
59413
|
}
|
|
58898
59414
|
case "variable": {
|
|
58899
59415
|
const bareName = newName.startsWith("$") ? newName.slice(1) : newName.startsWith("%") ? newName.slice(1) : newName;
|
|
58900
|
-
const
|
|
58901
|
-
const refs = isTransient ? findTransientReferences(symbol2.name, workspace, true) : findVariableReferences(symbol2.name, workspace, true);
|
|
59416
|
+
const refs = symbol2.sigil === "%" ? findTransientReferences(symbol2.name, workspace, true) : findVariableReferences(symbol2.name, workspace, true);
|
|
58902
59417
|
for (const ref of refs) {
|
|
58903
|
-
|
|
59418
|
+
const start = ref.range.start.character + 1;
|
|
59419
|
+
addEdit(ref.uri, {
|
|
59420
|
+
start: { line: ref.range.start.line, character: start },
|
|
59421
|
+
end: { line: ref.range.start.line, character: start + symbol2.name.length }
|
|
59422
|
+
}, bareName);
|
|
58904
59423
|
}
|
|
58905
59424
|
break;
|
|
58906
59425
|
}
|
|
@@ -58911,23 +59430,7 @@ function computeRename(uri, position, newName, workspace) {
|
|
|
58911
59430
|
}
|
|
58912
59431
|
const widget = workspace.widgets.getWidget(symbol2.name);
|
|
58913
59432
|
if (widget) {
|
|
58914
|
-
|
|
58915
|
-
if (docText) {
|
|
58916
|
-
const lines = docText.split("\n");
|
|
58917
|
-
const defLine = lines[widget.range.start.line];
|
|
58918
|
-
if (defLine) {
|
|
58919
|
-
const defSlice = defLine.slice(widget.range.start.character);
|
|
58920
|
-
const nameMatch = /\{widget\s+"([^"]+)"/.exec(defSlice);
|
|
58921
|
-
if (nameMatch) {
|
|
58922
|
-
const nameOffsetInSlice = defSlice.indexOf(nameMatch[1]);
|
|
58923
|
-
const nameStart = widget.range.start.character + nameOffsetInSlice;
|
|
58924
|
-
addEdit(widget.uri, {
|
|
58925
|
-
start: { line: widget.range.start.line, character: nameStart },
|
|
58926
|
-
end: { line: widget.range.start.line, character: nameStart + symbol2.name.length }
|
|
58927
|
-
}, newName);
|
|
58928
|
-
}
|
|
58929
|
-
}
|
|
58930
|
-
}
|
|
59433
|
+
addEdit(widget.uri, widget.nameRange, newName);
|
|
58931
59434
|
}
|
|
58932
59435
|
break;
|
|
58933
59436
|
}
|
|
@@ -58969,6 +59472,7 @@ function resolveSymbolAtCursor(uri, position, workspace) {
|
|
|
58969
59472
|
return {
|
|
58970
59473
|
kind: "variable",
|
|
58971
59474
|
name: match2[1],
|
|
59475
|
+
sigil: "$",
|
|
58972
59476
|
range: {
|
|
58973
59477
|
start: { line: position.line, character: start },
|
|
58974
59478
|
end: { line: position.line, character: end }
|
|
@@ -58987,6 +59491,7 @@ function resolveSymbolAtCursor(uri, position, workspace) {
|
|
|
58987
59491
|
return {
|
|
58988
59492
|
kind: "variable",
|
|
58989
59493
|
name: match2[1],
|
|
59494
|
+
sigil: "%",
|
|
58990
59495
|
range: {
|
|
58991
59496
|
start: { line: position.line, character: start },
|
|
58992
59497
|
end: { line: position.line, character: end }
|
|
@@ -58995,34 +59500,23 @@ function resolveSymbolAtCursor(uri, position, workspace) {
|
|
|
58995
59500
|
}
|
|
58996
59501
|
}
|
|
58997
59502
|
}
|
|
58998
|
-
{
|
|
58999
|
-
const
|
|
59000
|
-
|
|
59001
|
-
|
|
59002
|
-
const name = match2[1];
|
|
59003
|
-
const nameStart = match2.index + match2[0].indexOf(name);
|
|
59004
|
-
const nameEnd = nameStart + name.length;
|
|
59005
|
-
if (position.character >= nameStart && position.character <= nameEnd) {
|
|
59006
|
-
return {
|
|
59007
|
-
kind: "widget",
|
|
59008
|
-
name,
|
|
59009
|
-
range: {
|
|
59010
|
-
start: { line: position.line, character: nameStart },
|
|
59011
|
-
end: { line: position.line, character: nameEnd }
|
|
59012
|
-
}
|
|
59013
|
-
};
|
|
59014
|
-
}
|
|
59503
|
+
for (const widget of workspace.widgets.getAllWidgets()) {
|
|
59504
|
+
const { start, end } = widget.nameRange;
|
|
59505
|
+
if (widget.uri === uri && position.line === start.line && position.character >= start.character && position.character <= end.character) {
|
|
59506
|
+
return { kind: "widget", name: widget.name, range: widget.nameRange };
|
|
59015
59507
|
}
|
|
59016
59508
|
}
|
|
59017
59509
|
{
|
|
59018
|
-
const re = /\{([A-Za-z_$][\w$]*)/g;
|
|
59510
|
+
const re = /\{\/?([A-Za-z_$][\w$]*)/g;
|
|
59019
59511
|
let match2;
|
|
59020
59512
|
while ((match2 = re.exec(line)) !== null) {
|
|
59021
59513
|
const name = match2[1];
|
|
59022
|
-
const nameStart = match2.index +
|
|
59514
|
+
const nameStart = match2.index + match2[0].length - name.length;
|
|
59023
59515
|
const nameEnd = nameStart + name.length;
|
|
59024
59516
|
if (position.character >= nameStart && position.character <= nameEnd) {
|
|
59025
|
-
|
|
59517
|
+
const widget = workspace.widgets.getWidget(name);
|
|
59518
|
+
const isClosing = match2[0][1] === "/";
|
|
59519
|
+
if (!workspace.macros.getMacro(name) && widget && (!isClosing || widget.block)) {
|
|
59026
59520
|
return {
|
|
59027
59521
|
kind: "widget",
|
|
59028
59522
|
name,
|
|
@@ -59116,7 +59610,7 @@ function computeCodeLenses(uri, workspace) {
|
|
|
59116
59610
|
});
|
|
59117
59611
|
continue;
|
|
59118
59612
|
}
|
|
59119
|
-
const widgetMatch = line.match(/\{widget\s+"([
|
|
59613
|
+
const widgetMatch = line.match(/\{widget\s+["']?([^\s"'}]+)/);
|
|
59120
59614
|
if (widgetMatch) {
|
|
59121
59615
|
const widgetName = widgetMatch[1];
|
|
59122
59616
|
const refs = findWidgetReferences(widgetName, workspace, true);
|
|
@@ -59196,34 +59690,24 @@ function computeInlayHints(uri, range2, workspace) {
|
|
|
59196
59690
|
return hints;
|
|
59197
59691
|
}
|
|
59198
59692
|
function addWidgetParamHints(text, range2, workspace, hints) {
|
|
59199
|
-
|
|
59200
|
-
if (allWidgets.length === 0) return;
|
|
59201
|
-
const widgetMap = new Map(allWidgets.map((w) => [w.name, w]));
|
|
59693
|
+
if (workspace.widgets.getAllWidgets().length === 0) return;
|
|
59202
59694
|
const macros = parseMacros(text);
|
|
59695
|
+
const lineStarts = buildLineStarts(text);
|
|
59203
59696
|
for (const macro of macros) {
|
|
59204
59697
|
if (!macro.open) continue;
|
|
59205
59698
|
if (macro.range.start.line < range2.start.line || macro.range.start.line > range2.end.line) continue;
|
|
59206
|
-
const widget =
|
|
59699
|
+
const widget = workspace.widgets.getWidget(macro.name);
|
|
59207
59700
|
if (!widget || widget.params.length === 0) continue;
|
|
59208
59701
|
if (!macro.rawArgs || macro.rawArgs.trim() === "") continue;
|
|
59209
59702
|
const args2 = lexArguments(macro.rawArgs);
|
|
59210
|
-
const
|
|
59211
|
-
const
|
|
59212
|
-
if (!macroLine) continue;
|
|
59213
|
-
const macroText = macroLine.substring(macro.range.start.character, macro.range.end.character);
|
|
59214
|
-
const nameEnd = macroText.indexOf(macro.name) + macro.name.length;
|
|
59215
|
-
const argsText = macroText.substring(nameEnd).replace(/^\s+/, "").replace(/\}$/, "");
|
|
59216
|
-
if (!argsText) continue;
|
|
59703
|
+
const macroEnd = lineStarts[macro.range.end.line] + macro.range.end.character;
|
|
59704
|
+
const argsStart = macroEnd - 1 - macro.rawArgs.length;
|
|
59217
59705
|
for (let i = 0; i < Math.min(args2.length, widget.params.length); i++) {
|
|
59218
|
-
|
|
59219
|
-
|
|
59220
|
-
|
|
59221
|
-
|
|
59222
|
-
|
|
59223
|
-
label: `@${widget.params[i]}:`,
|
|
59224
|
-
kind: "parameter"
|
|
59225
|
-
});
|
|
59226
|
-
}
|
|
59706
|
+
hints.push({
|
|
59707
|
+
position: offsetToPosition(argsStart + args2[i].start, lineStarts),
|
|
59708
|
+
label: `${widget.params[i]}:`,
|
|
59709
|
+
kind: "parameter"
|
|
59710
|
+
});
|
|
59227
59711
|
}
|
|
59228
59712
|
}
|
|
59229
59713
|
}
|
|
@@ -59506,12 +59990,15 @@ var init_semantic_tokens = __esm({
|
|
|
59506
59990
|
});
|
|
59507
59991
|
|
|
59508
59992
|
// src/plugins/code-actions.ts
|
|
59509
|
-
|
|
59993
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
59994
|
+
import { basename, dirname as dirname2, join as join3 } from "node:path";
|
|
59995
|
+
import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL4 } from "node:url";
|
|
59996
|
+
function computeCodeActions(uri, diagnostics, workspace, options = {}) {
|
|
59510
59997
|
const actions = [];
|
|
59511
59998
|
for (const diag of diagnostics) {
|
|
59512
59999
|
switch (diag.code) {
|
|
59513
60000
|
case DiagnosticCode.UndefinedMacro: {
|
|
59514
|
-
const action = fixUndefinedMacro(diag);
|
|
60001
|
+
const action = fixUndefinedMacro(uri, diag, workspace, options);
|
|
59515
60002
|
if (action) actions.push(action);
|
|
59516
60003
|
break;
|
|
59517
60004
|
}
|
|
@@ -59539,27 +60026,141 @@ function computeCodeActions(uri, diagnostics, workspace) {
|
|
|
59539
60026
|
}
|
|
59540
60027
|
return actions;
|
|
59541
60028
|
}
|
|
59542
|
-
function fixUndefinedMacro(diag) {
|
|
60029
|
+
function fixUndefinedMacro(uri, diag, workspace, options) {
|
|
59543
60030
|
const match2 = diag.message.match(/\{(\w[\w-]*)\}/);
|
|
59544
60031
|
if (!match2) return null;
|
|
59545
60032
|
const macroName = match2[1];
|
|
59546
|
-
const
|
|
59547
|
-
|
|
59548
|
-
|
|
59549
|
-
|
|
59550
|
-
|
|
59551
|
-
|
|
59552
|
-
title: `Add '${macroName}' to
|
|
60033
|
+
const root = resolveConfigRoot(uri, options.workspaceRoot);
|
|
60034
|
+
if (!root) return null;
|
|
60035
|
+
const existingPath = findConfigFile(root);
|
|
60036
|
+
const configPath = existingPath ?? join3(root, DEFAULT_CONFIG_FILENAME);
|
|
60037
|
+
const configUri = pathToFileURL4(configPath).toString();
|
|
60038
|
+
const action = {
|
|
60039
|
+
title: `Add '${macroName}' to ${basename(configPath)}`,
|
|
59553
60040
|
kind: "quickfix",
|
|
59554
|
-
diagnosticCodes: [DiagnosticCode.UndefinedMacro]
|
|
59555
|
-
|
|
59556
|
-
|
|
59557
|
-
|
|
59558
|
-
|
|
59559
|
-
|
|
59560
|
-
|
|
59561
|
-
|
|
59562
|
-
|
|
60041
|
+
diagnosticCodes: [DiagnosticCode.UndefinedMacro]
|
|
60042
|
+
};
|
|
60043
|
+
if (!existingPath) {
|
|
60044
|
+
const start = { line: 0, character: 0 };
|
|
60045
|
+
return {
|
|
60046
|
+
...action,
|
|
60047
|
+
createFile: configUri,
|
|
60048
|
+
edits: [{
|
|
60049
|
+
uri: configUri,
|
|
60050
|
+
range: { start, end: start },
|
|
60051
|
+
newText: (0, import_yaml2.stringify)({ macros: { [macroName]: NEW_MACRO_ENTRY } })
|
|
60052
|
+
}]
|
|
60053
|
+
};
|
|
60054
|
+
}
|
|
60055
|
+
let text = workspace.documents.getText(configUri);
|
|
60056
|
+
if (text === void 0) {
|
|
60057
|
+
try {
|
|
60058
|
+
text = readFileSync7(existingPath, "utf-8");
|
|
60059
|
+
} catch {
|
|
60060
|
+
return null;
|
|
60061
|
+
}
|
|
60062
|
+
}
|
|
60063
|
+
const edit = configPath.toLowerCase().endsWith(".json") ? addMacroToJsonConfig(text, macroName) : addMacroToYamlConfig(text, macroName);
|
|
60064
|
+
if (!edit) return null;
|
|
60065
|
+
return { ...action, edits: [{ uri: configUri, ...edit }] };
|
|
60066
|
+
}
|
|
60067
|
+
function resolveConfigRoot(uri, workspaceRoot) {
|
|
60068
|
+
if (workspaceRoot) {
|
|
60069
|
+
return workspaceRoot.startsWith("file:") ? fileURLToPath6(workspaceRoot) : workspaceRoot;
|
|
60070
|
+
}
|
|
60071
|
+
if (!uri.startsWith("file:")) return null;
|
|
60072
|
+
const docDir = dirname2(fileURLToPath6(uri));
|
|
60073
|
+
let search = docDir;
|
|
60074
|
+
for (let i = 0; i < 10; i++) {
|
|
60075
|
+
if (findConfigFile(search)) return search;
|
|
60076
|
+
const parent = dirname2(search);
|
|
60077
|
+
if (parent === search) break;
|
|
60078
|
+
search = parent;
|
|
60079
|
+
}
|
|
60080
|
+
return docDir;
|
|
60081
|
+
}
|
|
60082
|
+
function macrosPath(root) {
|
|
60083
|
+
const legacy = root?.["spindle-0"];
|
|
60084
|
+
return legacy !== null && typeof legacy === "object" ? ["spindle-0", "macros"] : ["macros"];
|
|
60085
|
+
}
|
|
60086
|
+
function definesMacro(macros, macroName) {
|
|
60087
|
+
if (macros === null || typeof macros !== "object") return false;
|
|
60088
|
+
const lower = macroName.toLowerCase();
|
|
60089
|
+
return Object.keys(macros).some((k) => k.toLowerCase() === lower);
|
|
60090
|
+
}
|
|
60091
|
+
function addMacroToYamlConfig(text, macroName) {
|
|
60092
|
+
const doc = (0, import_yaml2.parseDocument)(text);
|
|
60093
|
+
if (doc.errors.length > 0) return null;
|
|
60094
|
+
if (doc.contents !== null && !(0, import_yaml2.isMap)(doc.contents)) return null;
|
|
60095
|
+
const path2 = macrosPath(doc.toJS());
|
|
60096
|
+
const parent = path2.length > 1 ? doc.getIn(path2.slice(0, -1), true) : doc.contents;
|
|
60097
|
+
const macros = doc.getIn(path2, true);
|
|
60098
|
+
if ((0, import_yaml2.isMap)(macros) && definesMacro(macros.toJS(doc), macroName)) return null;
|
|
60099
|
+
if ((0, import_yaml2.isMap)(parent) && (0, import_yaml2.isMap)(macros) && !macros.flow && macros.items.length > 0 && macros.range) {
|
|
60100
|
+
const macrosKey = parent.items.find((p) => (0, import_yaml2.isScalar)(p.key) && p.key.value === "macros")?.key;
|
|
60101
|
+
const firstKey = macros.items[0].key;
|
|
60102
|
+
if ((0, import_yaml2.isScalar)(macrosKey) && macrosKey.range && (0, import_yaml2.isScalar)(firstKey) && firstKey.range) {
|
|
60103
|
+
const indent = columnOf(text, firstKey.range[0]);
|
|
60104
|
+
const step = Math.max(indent - columnOf(text, macrosKey.range[0]), 1);
|
|
60105
|
+
const entry = (0, import_yaml2.stringify)({ [macroName]: NEW_MACRO_ENTRY }, { indent: step }).replace(/^(?=.)/gm, " ".repeat(indent));
|
|
60106
|
+
let offset = macros.range[1];
|
|
60107
|
+
if (offset > 0 && text[offset - 1] !== "\n") {
|
|
60108
|
+
const nl = text.indexOf("\n", offset);
|
|
60109
|
+
offset = nl === -1 ? text.length : nl + 1;
|
|
60110
|
+
}
|
|
60111
|
+
const lead = offset === text.length && text.length > 0 && !text.endsWith("\n") ? "\n" : "";
|
|
60112
|
+
const pos = offsetToPosition2(text, offset);
|
|
60113
|
+
return { range: { start: pos, end: pos }, newText: lead + entry };
|
|
60114
|
+
}
|
|
60115
|
+
}
|
|
60116
|
+
if ((0, import_yaml2.isMap)(macros)) {
|
|
60117
|
+
macros.set(macroName, doc.createNode(NEW_MACRO_ENTRY));
|
|
60118
|
+
} else {
|
|
60119
|
+
doc.setIn(path2, doc.createNode({ [macroName]: NEW_MACRO_ENTRY }));
|
|
60120
|
+
}
|
|
60121
|
+
return minimalEdit(text, doc.toString({ lineWidth: 0 }));
|
|
60122
|
+
}
|
|
60123
|
+
function addMacroToJsonConfig(text, macroName) {
|
|
60124
|
+
let raw;
|
|
60125
|
+
try {
|
|
60126
|
+
raw = text.trim() ? JSON.parse(text) : {};
|
|
60127
|
+
} catch {
|
|
60128
|
+
return null;
|
|
60129
|
+
}
|
|
60130
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
60131
|
+
const path2 = macrosPath(raw);
|
|
60132
|
+
const container = path2.length > 1 ? raw[path2[0]] : raw;
|
|
60133
|
+
const macros = container.macros;
|
|
60134
|
+
if (definesMacro(macros, macroName)) return null;
|
|
60135
|
+
if (macros !== null && typeof macros === "object" && !Array.isArray(macros)) {
|
|
60136
|
+
macros[macroName] = NEW_MACRO_ENTRY;
|
|
60137
|
+
} else {
|
|
60138
|
+
container.macros = { [macroName]: NEW_MACRO_ENTRY };
|
|
60139
|
+
}
|
|
60140
|
+
const indent = text.match(/^[ \t]+(?=")/m)?.[0] ?? " ";
|
|
60141
|
+
const newline = text.endsWith("\n") || !text.trim() ? "\n" : "";
|
|
60142
|
+
return minimalEdit(text, JSON.stringify(raw, null, indent) + newline);
|
|
60143
|
+
}
|
|
60144
|
+
function columnOf(text, offset) {
|
|
60145
|
+
return offset - (text.lastIndexOf("\n", offset - 1) + 1);
|
|
60146
|
+
}
|
|
60147
|
+
function offsetToPosition2(text, offset) {
|
|
60148
|
+
const before = text.slice(0, offset);
|
|
60149
|
+
const lineStart = before.lastIndexOf("\n") + 1;
|
|
60150
|
+
return { line: before.split("\n").length - 1, character: offset - lineStart };
|
|
60151
|
+
}
|
|
60152
|
+
function minimalEdit(oldText, newText) {
|
|
60153
|
+
const max = Math.min(oldText.length, newText.length);
|
|
60154
|
+
let prefix = 0;
|
|
60155
|
+
while (prefix < max && oldText[prefix] === newText[prefix]) prefix++;
|
|
60156
|
+
let suffix = 0;
|
|
60157
|
+
while (suffix < max - prefix && oldText[oldText.length - 1 - suffix] === newText[newText.length - 1 - suffix]) suffix++;
|
|
60158
|
+
return {
|
|
60159
|
+
range: {
|
|
60160
|
+
start: offsetToPosition2(oldText, prefix),
|
|
60161
|
+
end: offsetToPosition2(oldText, oldText.length - suffix)
|
|
60162
|
+
},
|
|
60163
|
+
newText: newText.slice(prefix, newText.length - suffix)
|
|
59563
60164
|
};
|
|
59564
60165
|
}
|
|
59565
60166
|
function fixUndeclaredVariable(diag, workspace) {
|
|
@@ -59669,11 +60270,15 @@ function toLspRange6(r) {
|
|
|
59669
60270
|
end: { line: r.end.line, character: r.end.character }
|
|
59670
60271
|
};
|
|
59671
60272
|
}
|
|
59672
|
-
var codeActionsPlugin;
|
|
60273
|
+
var import_yaml2, DEFAULT_CONFIG_FILENAME, NEW_MACRO_ENTRY, codeActionsPlugin;
|
|
59673
60274
|
var init_code_actions = __esm({
|
|
59674
60275
|
"src/plugins/code-actions.ts"() {
|
|
59675
60276
|
"use strict";
|
|
60277
|
+
import_yaml2 = __toESM(require_dist(), 1);
|
|
59676
60278
|
init_diagnostic_codes();
|
|
60279
|
+
init_config_loader();
|
|
60280
|
+
DEFAULT_CONFIG_FILENAME = "spindle.config.yaml";
|
|
60281
|
+
NEW_MACRO_ENTRY = { description: "" };
|
|
59677
60282
|
codeActionsPlugin = {
|
|
59678
60283
|
id: "code-actions",
|
|
59679
60284
|
capabilities: {
|
|
@@ -59696,7 +60301,8 @@ var init_code_actions = __esm({
|
|
|
59696
60301
|
const actions = computeCodeActions(
|
|
59697
60302
|
params.textDocument.uri,
|
|
59698
60303
|
diagnostics,
|
|
59699
|
-
ctx.workspace
|
|
60304
|
+
ctx.workspace,
|
|
60305
|
+
{ workspaceRoot: ctx.config.workspaceRoot }
|
|
59700
60306
|
);
|
|
59701
60307
|
return actions.map((a) => ({
|
|
59702
60308
|
title: a.title,
|
|
@@ -59704,7 +60310,16 @@ var init_code_actions = __esm({
|
|
|
59704
60310
|
diagnostics: params.context.diagnostics.filter(
|
|
59705
60311
|
(d) => a.diagnosticCodes.includes(String(d.code))
|
|
59706
60312
|
),
|
|
59707
|
-
edit: {
|
|
60313
|
+
edit: a.createFile ? {
|
|
60314
|
+
// Resource operations are only expressible via documentChanges
|
|
60315
|
+
documentChanges: [
|
|
60316
|
+
{ kind: "create", uri: a.createFile, options: { ignoreIfExists: true } },
|
|
60317
|
+
...a.edits.map((e) => ({
|
|
60318
|
+
textDocument: { uri: e.uri, version: null },
|
|
60319
|
+
edits: [{ range: toLspRange6(e.range), newText: e.newText }]
|
|
60320
|
+
}))
|
|
60321
|
+
]
|
|
60322
|
+
} : {
|
|
59708
60323
|
changes: Object.fromEntries(
|
|
59709
60324
|
a.edits.map((e) => [
|
|
59710
60325
|
e.uri,
|
|
@@ -59744,7 +60359,7 @@ function computeDocumentSymbols(uri, workspace) {
|
|
|
59744
60359
|
if (passage.tags?.includes("widget")) {
|
|
59745
60360
|
const contentStart = passage.range.start.line + 1;
|
|
59746
60361
|
const contentEnd = passage.range.end.line + 1;
|
|
59747
|
-
const widgetDefRegex2 = /\{widget\s+"([
|
|
60362
|
+
const widgetDefRegex2 = /\{widget\s+["']?([^\s"'}]+)["']?[^}]*\}/gi;
|
|
59748
60363
|
for (let i = contentStart; i < contentEnd && i < lines.length; i++) {
|
|
59749
60364
|
widgetDefRegex2.lastIndex = 0;
|
|
59750
60365
|
let match2;
|
|
@@ -59849,14 +60464,19 @@ function searchWorkspaceSymbols(query, workspace) {
|
|
|
59849
60464
|
range: widget.range
|
|
59850
60465
|
});
|
|
59851
60466
|
}
|
|
59852
|
-
|
|
59853
|
-
|
|
60467
|
+
const declarations = [
|
|
60468
|
+
...workspace.variables.getDeclared().values(),
|
|
60469
|
+
...workspace.variables.getDeclaredTransient().values()
|
|
60470
|
+
];
|
|
60471
|
+
for (const decl of declarations) {
|
|
60472
|
+
const displayName = decl.sigil + decl.name;
|
|
59854
60473
|
if (lowerQuery && !displayName.toLowerCase().includes(lowerQuery)) continue;
|
|
60474
|
+
if (!decl.declarationUri || !decl.declarationRange) continue;
|
|
59855
60475
|
results.push({
|
|
59856
60476
|
name: displayName,
|
|
59857
60477
|
kind: import_vscode_languageserver3.SymbolKind.Variable,
|
|
59858
|
-
uri: decl.declarationUri
|
|
59859
|
-
range: decl.declarationRange
|
|
60478
|
+
uri: decl.declarationUri,
|
|
60479
|
+
range: decl.declarationRange
|
|
59860
60480
|
});
|
|
59861
60481
|
}
|
|
59862
60482
|
return results;
|
|
@@ -59912,7 +60532,11 @@ function computeFoldingRanges(uri, workspace) {
|
|
|
59912
60532
|
}
|
|
59913
60533
|
}
|
|
59914
60534
|
const macros = parseMacros(text);
|
|
59915
|
-
pairMacros(
|
|
60535
|
+
pairMacros(
|
|
60536
|
+
macros,
|
|
60537
|
+
(name) => workspace.macros.isBlock(name),
|
|
60538
|
+
passages.map((p) => p.range.start.line)
|
|
60539
|
+
);
|
|
59916
60540
|
for (const macro of macros) {
|
|
59917
60541
|
if (macro.open && macro.pair !== -1) {
|
|
59918
60542
|
const closingMacro = macros[macro.pair];
|
|
@@ -60059,17 +60683,22 @@ var server_exports2 = {};
|
|
|
60059
60683
|
__export(server_exports2, {
|
|
60060
60684
|
startServer: () => startServer
|
|
60061
60685
|
});
|
|
60062
|
-
import { fileURLToPath as
|
|
60063
|
-
import { readFileSync as
|
|
60686
|
+
import { fileURLToPath as fileURLToPath7, pathToFileURL as pathToFileURL5 } from "node:url";
|
|
60687
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
60688
|
+
import { basename as basename2 } from "node:path";
|
|
60064
60689
|
function uriToFsPath(uri) {
|
|
60065
60690
|
try {
|
|
60066
|
-
return
|
|
60691
|
+
return fileURLToPath7(uri);
|
|
60067
60692
|
} catch {
|
|
60068
60693
|
return uri;
|
|
60069
60694
|
}
|
|
60070
60695
|
}
|
|
60071
60696
|
function fsPathToUri(fsPath) {
|
|
60072
|
-
return
|
|
60697
|
+
return pathToFileURL5(fsPath).toString();
|
|
60698
|
+
}
|
|
60699
|
+
function isConfigFileUri(uri) {
|
|
60700
|
+
const name = basename2(uriToFsPath(uri));
|
|
60701
|
+
return name.startsWith("spindle.config.") || name.includes("twee-config.");
|
|
60073
60702
|
}
|
|
60074
60703
|
function startServer(_args) {
|
|
60075
60704
|
const connection = (0, import_node.createConnection)(import_node.ProposedFeatures.all);
|
|
@@ -60095,7 +60724,7 @@ function startServer(_args) {
|
|
|
60095
60724
|
plugin.initialize({ connection, workspace, config: config2 });
|
|
60096
60725
|
}
|
|
60097
60726
|
if (Object.keys(projectConfig.macros).length > 0) {
|
|
60098
|
-
workspace.macros.
|
|
60727
|
+
workspace.macros.loadConfig(projectConfig.macros);
|
|
60099
60728
|
console.error("[spindle-lsp] loaded config macros:", Object.keys(projectConfig.macros).length);
|
|
60100
60729
|
}
|
|
60101
60730
|
return {
|
|
@@ -60119,6 +60748,9 @@ function startServer(_args) {
|
|
|
60119
60748
|
try {
|
|
60120
60749
|
const fileContents = await scanWorkspaceFiles(workspaceRoot);
|
|
60121
60750
|
console.error("[spindle-lsp] scanned", fileContents.size, "files from root:", workspaceRoot ?? "undefined");
|
|
60751
|
+
for (const uri of documents.keys()) {
|
|
60752
|
+
fileContents.delete(uri);
|
|
60753
|
+
}
|
|
60122
60754
|
workspace.initialize(fileContents);
|
|
60123
60755
|
console.error("[spindle-lsp] workspace initialized, macros:", workspace.macros.getAllMacros().length);
|
|
60124
60756
|
} catch (err) {
|
|
@@ -60158,23 +60790,40 @@ function startServer(_args) {
|
|
|
60158
60790
|
if (workspace) {
|
|
60159
60791
|
try {
|
|
60160
60792
|
const fsPath = uriToFsPath(textDocument.uri);
|
|
60161
|
-
const text =
|
|
60793
|
+
const text = readFileSync8(fsPath, "utf-8");
|
|
60162
60794
|
workspace.documents.update(textDocument.uri, text);
|
|
60163
60795
|
} catch {
|
|
60164
60796
|
workspace.documents.close(textDocument.uri);
|
|
60165
60797
|
}
|
|
60166
60798
|
}
|
|
60167
60799
|
});
|
|
60800
|
+
function reloadProjectConfig() {
|
|
60801
|
+
if (!workspaceRoot) return;
|
|
60802
|
+
try {
|
|
60803
|
+
const projectConfig = loadConfigFromDisk(workspaceRoot);
|
|
60804
|
+
workspace.macros.loadConfig(projectConfig.macros);
|
|
60805
|
+
console.error("[spindle-lsp] reloaded config macros:", Object.keys(projectConfig.macros).length);
|
|
60806
|
+
} catch (err) {
|
|
60807
|
+
console.error("[spindle-lsp] config reload failed:", err);
|
|
60808
|
+
return;
|
|
60809
|
+
}
|
|
60810
|
+
workspace.refresh();
|
|
60811
|
+
}
|
|
60168
60812
|
connection.onDidChangeWatchedFiles(({ changes }) => {
|
|
60169
60813
|
if (!workspace) return;
|
|
60814
|
+
let configChanged = false;
|
|
60170
60815
|
for (const change of changes) {
|
|
60816
|
+
if (isConfigFileUri(change.uri)) {
|
|
60817
|
+
configChanged = true;
|
|
60818
|
+
continue;
|
|
60819
|
+
}
|
|
60820
|
+
if (documents.has(change.uri)) continue;
|
|
60171
60821
|
if (change.type === import_node.FileChangeType.Deleted) {
|
|
60172
60822
|
workspace.documents.close(change.uri);
|
|
60173
|
-
documents.delete(change.uri);
|
|
60174
60823
|
} else {
|
|
60175
60824
|
try {
|
|
60176
60825
|
const fsPath = uriToFsPath(change.uri);
|
|
60177
|
-
const text =
|
|
60826
|
+
const text = readFileSync8(fsPath, "utf-8");
|
|
60178
60827
|
if (workspace.documents.has(change.uri)) {
|
|
60179
60828
|
workspace.documents.update(change.uri, text);
|
|
60180
60829
|
} else {
|
|
@@ -60184,6 +60833,9 @@ function startServer(_args) {
|
|
|
60184
60833
|
}
|
|
60185
60834
|
}
|
|
60186
60835
|
}
|
|
60836
|
+
if (configChanged) {
|
|
60837
|
+
reloadProjectConfig();
|
|
60838
|
+
}
|
|
60187
60839
|
});
|
|
60188
60840
|
connection.onShutdown(() => {
|
|
60189
60841
|
for (const plugin of activePlugins) {
|
|
@@ -60204,7 +60856,7 @@ async function scanWorkspaceFiles(root) {
|
|
|
60204
60856
|
});
|
|
60205
60857
|
for (const filePath of files) {
|
|
60206
60858
|
try {
|
|
60207
|
-
const text =
|
|
60859
|
+
const text = readFileSync8(filePath, "utf-8");
|
|
60208
60860
|
const uri = fsPathToUri(filePath);
|
|
60209
60861
|
contents.set(uri, text);
|
|
60210
60862
|
} catch {
|
|
@@ -60231,7 +60883,7 @@ var init_server4 = __esm({
|
|
|
60231
60883
|
|
|
60232
60884
|
// src/bin.ts
|
|
60233
60885
|
var args = process.argv.slice(2);
|
|
60234
|
-
var VERSION = "0.9.
|
|
60886
|
+
var VERSION = "0.9.1";
|
|
60235
60887
|
var HELP = `spindle-lsp v${VERSION}
|
|
60236
60888
|
Language Server Protocol server for Spindle story format
|
|
60237
60889
|
|