@rohal12/spindle-lsp 0.6.0 → 0.7.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 +465 -34
- package/dist/bin.js.map +3 -3
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -210,6 +210,10 @@ var init_passage_index = __esm({
|
|
|
210
210
|
getStoryVariables() {
|
|
211
211
|
return this.getPassage("StoryVariables");
|
|
212
212
|
}
|
|
213
|
+
/** Get the StoryTransients passage, if any. */
|
|
214
|
+
getStoryTransients() {
|
|
215
|
+
return this.getPassage("StoryTransients");
|
|
216
|
+
}
|
|
213
217
|
/** Get the StoryInit passage, if any. */
|
|
214
218
|
getStoryInit() {
|
|
215
219
|
return this.getPassage("StoryInit");
|
|
@@ -412,14 +416,16 @@ var init_macro_registry = __esm({
|
|
|
412
416
|
});
|
|
413
417
|
|
|
414
418
|
// src/core/workspace/variable-tracker.ts
|
|
415
|
-
var varRefRegex, EXCLUDED_PASSAGES, CLEAN_PATTERNS, VariableTracker;
|
|
419
|
+
var varRefRegex, transientRefRegex, EXCLUDED_PASSAGES, CLEAN_PATTERNS, VariableTracker;
|
|
416
420
|
var init_variable_tracker = __esm({
|
|
417
421
|
"src/core/workspace/variable-tracker.ts"() {
|
|
418
422
|
"use strict";
|
|
419
423
|
init_passage_parser();
|
|
420
424
|
varRefRegex = /\$([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
425
|
+
transientRefRegex = /(?<!\w)%([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
421
426
|
EXCLUDED_PASSAGES = /* @__PURE__ */ new Set([
|
|
422
427
|
"StoryVariables",
|
|
428
|
+
"StoryTransients",
|
|
423
429
|
"StoryInit",
|
|
424
430
|
"StoryData",
|
|
425
431
|
"StoryScript",
|
|
@@ -442,8 +448,12 @@ var init_variable_tracker = __esm({
|
|
|
442
448
|
VariableTracker = class {
|
|
443
449
|
declared = /* @__PURE__ */ new Map();
|
|
444
450
|
_hasStoryVariables = false;
|
|
451
|
+
declaredTransient = /* @__PURE__ */ new Map();
|
|
452
|
+
_hasStoryTransients = false;
|
|
445
453
|
/** Per-URI list of variable usages. */
|
|
446
454
|
usagesByUri = /* @__PURE__ */ new Map();
|
|
455
|
+
/** Per-URI list of transient variable usages. */
|
|
456
|
+
transientUsagesByUri = /* @__PURE__ */ new Map();
|
|
447
457
|
/**
|
|
448
458
|
* Parse the StoryVariables passage content for declarations.
|
|
449
459
|
* Each line like `$name = value` becomes a declaration.
|
|
@@ -474,14 +484,46 @@ var init_variable_tracker = __esm({
|
|
|
474
484
|
this.declared.set(name, decl);
|
|
475
485
|
}
|
|
476
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Parse the StoryTransients passage content for declarations.
|
|
489
|
+
* Each line like `%name = value` becomes a declaration.
|
|
490
|
+
*/
|
|
491
|
+
parseStoryTransients(content) {
|
|
492
|
+
this.declaredTransient.clear();
|
|
493
|
+
this._hasStoryTransients = true;
|
|
494
|
+
const lines = content.split("\n");
|
|
495
|
+
for (const line of lines) {
|
|
496
|
+
const trimmed = line.trim();
|
|
497
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("<!--")) continue;
|
|
498
|
+
const match = /^%([A-Za-z_$][\w$]*)\s*=\s*(.*)$/.exec(trimmed);
|
|
499
|
+
if (!match) continue;
|
|
500
|
+
const name = match[1];
|
|
501
|
+
const expr = match[2].trim();
|
|
502
|
+
const decl = { name, sigil: "%" };
|
|
503
|
+
if (expr.startsWith("{")) {
|
|
504
|
+
const fieldRegex = /(\w+)\s*:/g;
|
|
505
|
+
let fieldMatch;
|
|
506
|
+
const fields = [];
|
|
507
|
+
while ((fieldMatch = fieldRegex.exec(expr)) !== null) {
|
|
508
|
+
fields.push(fieldMatch[1]);
|
|
509
|
+
}
|
|
510
|
+
if (fields.length > 0) {
|
|
511
|
+
decl.fields = fields;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
this.declaredTransient.set(name, decl);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
477
517
|
/**
|
|
478
518
|
* Scan a document for variable usages.
|
|
479
519
|
* Identifies passages in the document and scans non-special ones.
|
|
480
520
|
*/
|
|
481
521
|
scanDocument(uri, text, _macros) {
|
|
482
522
|
this.usagesByUri.delete(uri);
|
|
523
|
+
this.transientUsagesByUri.delete(uri);
|
|
483
524
|
const lines = text.split("\n");
|
|
484
525
|
const usages = [];
|
|
526
|
+
const transientUsages = [];
|
|
485
527
|
const passageBoundaries = [];
|
|
486
528
|
for (let i = 0; i < lines.length; i++) {
|
|
487
529
|
const header = parsePassageHeader(lines[i], i);
|
|
@@ -523,10 +565,32 @@ var init_variable_tracker = __esm({
|
|
|
523
565
|
};
|
|
524
566
|
usages.push({ uri, baseName, fullName, range });
|
|
525
567
|
}
|
|
568
|
+
const tre = new RegExp(transientRefRegex.source, "g");
|
|
569
|
+
let tmatch;
|
|
570
|
+
while ((tmatch = tre.exec(cleaned)) !== null) {
|
|
571
|
+
const fullName = tmatch[1];
|
|
572
|
+
const baseName = fullName.split(".")[0];
|
|
573
|
+
const charOffset = tmatch.index;
|
|
574
|
+
let localLine = 0;
|
|
575
|
+
for (let i = 0; i < lineOffsets.length; i++) {
|
|
576
|
+
if (lineOffsets[i] > charOffset) break;
|
|
577
|
+
localLine = i;
|
|
578
|
+
}
|
|
579
|
+
const character = charOffset - lineOffsets[localLine];
|
|
580
|
+
const absoluteLine = contentStartLine + localLine;
|
|
581
|
+
const range = {
|
|
582
|
+
start: { line: absoluteLine, character },
|
|
583
|
+
end: { line: absoluteLine, character: character + tmatch[0].length }
|
|
584
|
+
};
|
|
585
|
+
transientUsages.push({ uri, baseName, fullName, range });
|
|
586
|
+
}
|
|
526
587
|
}
|
|
527
588
|
if (usages.length > 0) {
|
|
528
589
|
this.usagesByUri.set(uri, usages);
|
|
529
590
|
}
|
|
591
|
+
if (transientUsages.length > 0) {
|
|
592
|
+
this.transientUsagesByUri.set(uri, transientUsages);
|
|
593
|
+
}
|
|
530
594
|
}
|
|
531
595
|
/** Get all declared variables. */
|
|
532
596
|
getDeclared() {
|
|
@@ -562,6 +626,40 @@ var init_variable_tracker = __esm({
|
|
|
562
626
|
hasStoryVariables() {
|
|
563
627
|
return this._hasStoryVariables;
|
|
564
628
|
}
|
|
629
|
+
/** Get all declared transient variables. */
|
|
630
|
+
getDeclaredTransient() {
|
|
631
|
+
return this.declaredTransient;
|
|
632
|
+
}
|
|
633
|
+
/** Get all usages of a transient variable by base name. */
|
|
634
|
+
getTransientUsages(name) {
|
|
635
|
+
const results = [];
|
|
636
|
+
for (const usages of this.transientUsagesByUri.values()) {
|
|
637
|
+
for (const u of usages) {
|
|
638
|
+
if (u.baseName === name) {
|
|
639
|
+
results.push({ uri: u.uri, range: u.range });
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return results;
|
|
644
|
+
}
|
|
645
|
+
/** Get undeclared transient variable usages in a specific document. */
|
|
646
|
+
getUndeclaredTransient(uri) {
|
|
647
|
+
const usages = this.transientUsagesByUri.get(uri);
|
|
648
|
+
if (!usages) return [];
|
|
649
|
+
const results = [];
|
|
650
|
+
const seen = /* @__PURE__ */ new Set();
|
|
651
|
+
for (const u of usages) {
|
|
652
|
+
if (!this.declaredTransient.has(u.baseName) && !seen.has(u.baseName)) {
|
|
653
|
+
seen.add(u.baseName);
|
|
654
|
+
results.push({ name: u.baseName, range: u.range });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return results;
|
|
658
|
+
}
|
|
659
|
+
/** Whether a StoryTransients passage has been parsed. */
|
|
660
|
+
hasStoryTransients() {
|
|
661
|
+
return this._hasStoryTransients;
|
|
662
|
+
}
|
|
565
663
|
};
|
|
566
664
|
}
|
|
567
665
|
});
|
|
@@ -721,7 +819,7 @@ var variableInterpolationRegex, macroRegex;
|
|
|
721
819
|
var init_macro_parser = __esm({
|
|
722
820
|
"src/core/parsing/macro-parser.ts"() {
|
|
723
821
|
"use strict";
|
|
724
|
-
variableInterpolationRegex = /(?<!\\)\{([$_
|
|
822
|
+
variableInterpolationRegex = /(?<!\\)\{([$_@%][A-Za-z_$][\w$.]*)\}/g;
|
|
725
823
|
macroRegex = /(?<!\\)\{(\/)?(?:((?:[#.][a-zA-Z][\w-]*\s*)*)([A-Za-z][\w-]*))(?:\s+((?:(?:`(?:\\.|[^`\\])*?`)|(?:"(?:\\.|[^"\\])*?")|(?:'(?:\\.|[^'\\])*?')|(?:\[(?:[<>]?[Ii][Mm][Gg])?\[[^\r\n]*?\]\]+)|[^}]|(?:}))*?))?\}/gmi;
|
|
726
824
|
}
|
|
727
825
|
});
|
|
@@ -799,12 +897,12 @@ var init_macro_supplements = __esm({
|
|
|
799
897
|
},
|
|
800
898
|
set: {
|
|
801
899
|
name: "set",
|
|
802
|
-
description: "Sets variables based on the given expression.\n\nUsage: `{set $var to value}`\n\nSupports `$var` (story), `_var` (temporary),
|
|
900
|
+
description: "Sets variables based on the given expression.\n\nUsage: `{set $var to value}`\n\nSupports `$var` (story), `_var` (temporary), `@var` (local), and `%var` (transient) variables.",
|
|
803
901
|
parameters: ["...text"]
|
|
804
902
|
},
|
|
805
903
|
unset: {
|
|
806
904
|
name: "unset",
|
|
807
|
-
description: "Unsets one or more variables.\n\nUsage: `{unset $var}`",
|
|
905
|
+
description: "Unsets one or more variables.\n\nUsage: `{unset $var}`\n\nSupports `$var` (story), `_var` (temporary), `@var` (local), and `%var` (transient) variables.",
|
|
808
906
|
parameters: ["...text"]
|
|
809
907
|
},
|
|
810
908
|
computed: {
|
|
@@ -1143,6 +1241,23 @@ var init_workspace_model = __esm({
|
|
|
1143
1241
|
this.variables.parseStoryVariables(content);
|
|
1144
1242
|
}
|
|
1145
1243
|
}
|
|
1244
|
+
const storyTransients = this.passages.getStoryTransients();
|
|
1245
|
+
if (storyTransients) {
|
|
1246
|
+
const text = this.documents.getText(storyTransients.uri);
|
|
1247
|
+
if (text) {
|
|
1248
|
+
const lines = text.split("\n");
|
|
1249
|
+
const contentStart = storyTransients.headerEnd.end.line + 1;
|
|
1250
|
+
let contentEnd = lines.length;
|
|
1251
|
+
for (let i = contentStart; i < lines.length; i++) {
|
|
1252
|
+
if (/^::\s+/.test(lines[i])) {
|
|
1253
|
+
contentEnd = i;
|
|
1254
|
+
break;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
const content = lines.slice(contentStart, contentEnd).join("\n");
|
|
1258
|
+
this.variables.parseStoryTransients(content);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1146
1261
|
for (const uri of this.documents.getUris()) {
|
|
1147
1262
|
const text = this.documents.getText(uri);
|
|
1148
1263
|
if (text) {
|
|
@@ -1190,6 +1305,7 @@ var init_diagnostic_codes = __esm({
|
|
|
1190
1305
|
UndeclaredVariable: "SP200",
|
|
1191
1306
|
UndeclaredField: "SP201",
|
|
1192
1307
|
NoStoryVariables: "SP202",
|
|
1308
|
+
UndeclaredTransient: "SP203",
|
|
1193
1309
|
BrokenPassageLink: "SP300",
|
|
1194
1310
|
WidgetArgCountMismatch: "SP301",
|
|
1195
1311
|
DeadEndPassage: "SP400",
|
|
@@ -1211,6 +1327,7 @@ var init_diagnostic_codes = __esm({
|
|
|
1211
1327
|
SP200: "warning",
|
|
1212
1328
|
SP201: "warning",
|
|
1213
1329
|
SP202: "info",
|
|
1330
|
+
SP203: "warning",
|
|
1214
1331
|
SP300: "warning",
|
|
1215
1332
|
SP301: "warning",
|
|
1216
1333
|
SP400: "hint",
|
|
@@ -1411,7 +1528,7 @@ var varTestRegexp, EOF, Lexer, notSpaceRegex, spaceRegex;
|
|
|
1411
1528
|
var init_argument_lexer = __esm({
|
|
1412
1529
|
"src/core/parsing/argument-lexer.ts"() {
|
|
1413
1530
|
"use strict";
|
|
1414
|
-
varTestRegexp = /^[$_
|
|
1531
|
+
varTestRegexp = /^[$_@%][$A-Z_a-z][$0-9A-Z_.a-z]*/;
|
|
1415
1532
|
EOF = -1;
|
|
1416
1533
|
Lexer = class {
|
|
1417
1534
|
source;
|
|
@@ -2292,7 +2409,7 @@ function validateArguments(macros, workspace, passageNames, diagnostics) {
|
|
|
2292
2409
|
}
|
|
2293
2410
|
function validateVariables(uri, workspace, diagnostics) {
|
|
2294
2411
|
if (!workspace.variables.hasStoryVariables()) {
|
|
2295
|
-
const
|
|
2412
|
+
const undeclared = workspace.variables.getUndeclared(uri);
|
|
2296
2413
|
const text = workspace.documents.getText(uri);
|
|
2297
2414
|
if (text && /\$[A-Za-z_$]/.test(text)) {
|
|
2298
2415
|
const passages = workspace.passages.getPassagesInDocument(uri);
|
|
@@ -2308,15 +2425,25 @@ function validateVariables(uri, workspace, diagnostics) {
|
|
|
2308
2425
|
));
|
|
2309
2426
|
}
|
|
2310
2427
|
}
|
|
2311
|
-
|
|
2428
|
+
} else {
|
|
2429
|
+
const undeclared = workspace.variables.getUndeclared(uri);
|
|
2430
|
+
for (const u of undeclared) {
|
|
2431
|
+
diagnostics.push(makeDiag(
|
|
2432
|
+
u.range,
|
|
2433
|
+
DiagnosticCode.UndeclaredVariable,
|
|
2434
|
+
`Variable '$${u.name}' is not declared in StoryVariables`
|
|
2435
|
+
));
|
|
2436
|
+
}
|
|
2312
2437
|
}
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2438
|
+
if (workspace.variables.hasStoryTransients()) {
|
|
2439
|
+
const undeclaredTransient = workspace.variables.getUndeclaredTransient(uri);
|
|
2440
|
+
for (const u of undeclaredTransient) {
|
|
2441
|
+
diagnostics.push(makeDiag(
|
|
2442
|
+
u.range,
|
|
2443
|
+
DiagnosticCode.UndeclaredTransient,
|
|
2444
|
+
`Transient variable '%${u.name}' is not declared in StoryTransients`
|
|
2445
|
+
));
|
|
2446
|
+
}
|
|
2320
2447
|
}
|
|
2321
2448
|
}
|
|
2322
2449
|
function validateLinks(text, passages, passageNames, diagnostics) {
|
|
@@ -3045,6 +3172,50 @@ var init_prettier_bridge = __esm({
|
|
|
3045
3172
|
});
|
|
3046
3173
|
|
|
3047
3174
|
// src/plugins/format/placeholders.ts
|
|
3175
|
+
function scanSpindleTokens(text) {
|
|
3176
|
+
const matches = [];
|
|
3177
|
+
let i = 0;
|
|
3178
|
+
while (i < text.length) {
|
|
3179
|
+
if (text[i] === "[" && text[i + 1] === "[") {
|
|
3180
|
+
const closeIdx = text.indexOf("]]", i + 2);
|
|
3181
|
+
if (closeIdx !== -1) {
|
|
3182
|
+
const end = closeIdx + 2;
|
|
3183
|
+
matches.push({ start: i, end, token: text.slice(i, end) });
|
|
3184
|
+
i = end;
|
|
3185
|
+
continue;
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
if (text[i] === "{" && !(i > 0 && text[i - 1] === "\\")) {
|
|
3189
|
+
const next = i + 1 < text.length ? text[i + 1] : "";
|
|
3190
|
+
let isToken = false;
|
|
3191
|
+
if (next === "/") {
|
|
3192
|
+
isToken = i + 2 < text.length && /[A-Za-z]/.test(text[i + 2]);
|
|
3193
|
+
} else if (next === "#" || next === ".") {
|
|
3194
|
+
isToken = i + 2 < text.length && /[a-zA-Z]/.test(text[i + 2]);
|
|
3195
|
+
} else if (/[A-Za-z]/.test(next)) {
|
|
3196
|
+
isToken = true;
|
|
3197
|
+
} else if (/[$_@%]/.test(next)) {
|
|
3198
|
+
isToken = i + 2 < text.length && /[a-zA-Z]/.test(text[i + 2]);
|
|
3199
|
+
}
|
|
3200
|
+
if (isToken) {
|
|
3201
|
+
let depth = 1;
|
|
3202
|
+
let j = i + 1;
|
|
3203
|
+
while (j < text.length && depth > 0) {
|
|
3204
|
+
if (text[j] === "{") depth++;
|
|
3205
|
+
else if (text[j] === "}") depth--;
|
|
3206
|
+
j++;
|
|
3207
|
+
}
|
|
3208
|
+
if (depth === 0) {
|
|
3209
|
+
matches.push({ start: i, end: j, token: text.slice(i, j) });
|
|
3210
|
+
i = j;
|
|
3211
|
+
continue;
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
i++;
|
|
3216
|
+
}
|
|
3217
|
+
return matches;
|
|
3218
|
+
}
|
|
3048
3219
|
function isInsideAttribute(text, pos) {
|
|
3049
3220
|
let inQuote = false;
|
|
3050
3221
|
let quoteChar = "";
|
|
@@ -3081,13 +3252,15 @@ function replaceSpindleTokens(html) {
|
|
|
3081
3252
|
const resultLines = [];
|
|
3082
3253
|
for (const line of lines) {
|
|
3083
3254
|
const trimmed = line.trim();
|
|
3084
|
-
|
|
3085
|
-
if (
|
|
3255
|
+
const found = scanSpindleTokens(trimmed);
|
|
3256
|
+
if (found.length === 0) {
|
|
3086
3257
|
resultLines.push(line);
|
|
3087
3258
|
continue;
|
|
3088
3259
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3260
|
+
let withoutSpindle = trimmed;
|
|
3261
|
+
for (let fi = found.length - 1; fi >= 0; fi--) {
|
|
3262
|
+
withoutSpindle = withoutSpindle.slice(0, found[fi].start) + withoutSpindle.slice(found[fi].end);
|
|
3263
|
+
}
|
|
3091
3264
|
const hasHtml = HTML_TAG_REGEX.test(withoutSpindle);
|
|
3092
3265
|
if (!hasHtml) {
|
|
3093
3266
|
const idx = tokens.length;
|
|
@@ -3095,16 +3268,20 @@ function replaceSpindleTokens(html) {
|
|
|
3095
3268
|
const indent = line.match(/^(\s*)/)?.[1] ?? "";
|
|
3096
3269
|
resultLines.push(`${indent}<!--SP:${idx}-->`);
|
|
3097
3270
|
} else {
|
|
3098
|
-
|
|
3099
|
-
const
|
|
3100
|
-
|
|
3271
|
+
const indent = line.match(/^(\s*)/)?.[1] ?? "";
|
|
3272
|
+
const indentLen = indent.length;
|
|
3273
|
+
const assignments = found.map((t) => {
|
|
3274
|
+
const lineStart = t.start + indentLen;
|
|
3101
3275
|
const idx = tokens.length;
|
|
3102
|
-
tokens.push(
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
}
|
|
3106
|
-
return `<!--SP:${idx}-->`;
|
|
3276
|
+
tokens.push(t.token);
|
|
3277
|
+
const placeholder = isInsideAttribute(line, lineStart) ? `__SP${idx}__` : `<!--SP:${idx}-->`;
|
|
3278
|
+
return { start: lineStart, end: t.end + indentLen, placeholder };
|
|
3107
3279
|
});
|
|
3280
|
+
let replaced = line;
|
|
3281
|
+
for (let ai = assignments.length - 1; ai >= 0; ai--) {
|
|
3282
|
+
const a = assignments[ai];
|
|
3283
|
+
replaced = replaced.slice(0, a.start) + a.placeholder + replaced.slice(a.end);
|
|
3284
|
+
}
|
|
3108
3285
|
resultLines.push(replaced);
|
|
3109
3286
|
}
|
|
3110
3287
|
}
|
|
@@ -3118,11 +3295,10 @@ function restoreSpindleTokens(text, tokens) {
|
|
|
3118
3295
|
}
|
|
3119
3296
|
return result;
|
|
3120
3297
|
}
|
|
3121
|
-
var
|
|
3298
|
+
var HTML_TAG_REGEX;
|
|
3122
3299
|
var init_placeholders = __esm({
|
|
3123
3300
|
"src/plugins/format/placeholders.ts"() {
|
|
3124
3301
|
"use strict";
|
|
3125
|
-
SPINDLE_TOKEN_REGEX = /(?:\{\/[A-Za-z][\w-]*\s*\}|\{(?:[#.][a-zA-Z][\w-]*\s*)*[A-Za-z][\w-]*(?:\s+(?:[^{}]|\{[^{}]*\})*)?\}|\{[$_@][a-zA-Z][\w.]*\}|\[\[(?:[^\]]*)\]\])/g;
|
|
3126
3302
|
HTML_TAG_REGEX = /<\/?[a-zA-Z][\w-]*[\s>/]/;
|
|
3127
3303
|
}
|
|
3128
3304
|
});
|
|
@@ -3216,7 +3392,10 @@ async function formatDocument(text, options) {
|
|
|
3216
3392
|
}
|
|
3217
3393
|
continue;
|
|
3218
3394
|
}
|
|
3219
|
-
|
|
3395
|
+
let indented = indentMacros(region.lines, isBlock, isDedenting);
|
|
3396
|
+
if (options?.maxLineLength) {
|
|
3397
|
+
indented = wrapLines(indented, options.maxLineLength);
|
|
3398
|
+
}
|
|
3220
3399
|
resultLines.push(...indented);
|
|
3221
3400
|
}
|
|
3222
3401
|
}
|
|
@@ -3263,6 +3442,65 @@ function indentMacros(lines, isBlock, isDedenting) {
|
|
|
3263
3442
|
async function formatRange(text, _range, options) {
|
|
3264
3443
|
return formatDocument(text, options);
|
|
3265
3444
|
}
|
|
3445
|
+
function wrapLines(lines, maxLen) {
|
|
3446
|
+
const result = [];
|
|
3447
|
+
for (const line of lines) {
|
|
3448
|
+
if (line.length <= maxLen) {
|
|
3449
|
+
result.push(line);
|
|
3450
|
+
continue;
|
|
3451
|
+
}
|
|
3452
|
+
const trimmed = line.trim();
|
|
3453
|
+
if (trimmed === "" || /^::/.test(trimmed) || /^\{[\/#.]/.test(trimmed) || // closing/opening-only macro lines
|
|
3454
|
+
/^\[\[/.test(trimmed) || // links
|
|
3455
|
+
/^\{choice\b/.test(trimmed) || // choice macros
|
|
3456
|
+
/^\{choices\b/.test(trimmed)) {
|
|
3457
|
+
result.push(line);
|
|
3458
|
+
continue;
|
|
3459
|
+
}
|
|
3460
|
+
const indentMatch = line.match(/^(\s*)/);
|
|
3461
|
+
const indent = indentMatch ? indentMatch[1] : "";
|
|
3462
|
+
const wrapped = wordWrap(trimmed, maxLen, indent);
|
|
3463
|
+
result.push(...wrapped);
|
|
3464
|
+
}
|
|
3465
|
+
return result;
|
|
3466
|
+
}
|
|
3467
|
+
function wordWrap(text, maxLen, indent) {
|
|
3468
|
+
const lines = [];
|
|
3469
|
+
let remaining = text;
|
|
3470
|
+
while (remaining.length > 0) {
|
|
3471
|
+
const currentMax = lines.length === 0 ? maxLen - indent.length : maxLen - indent.length;
|
|
3472
|
+
if (remaining.length <= currentMax) {
|
|
3473
|
+
lines.push(indent + remaining);
|
|
3474
|
+
break;
|
|
3475
|
+
}
|
|
3476
|
+
let breakIdx = -1;
|
|
3477
|
+
let inMacro = 0;
|
|
3478
|
+
for (let i = 0; i < remaining.length && i <= currentMax; i++) {
|
|
3479
|
+
if (remaining[i] === "{") inMacro++;
|
|
3480
|
+
else if (remaining[i] === "}") inMacro = Math.max(0, inMacro - 1);
|
|
3481
|
+
else if (remaining[i] === " " && inMacro === 0 && i > 0) {
|
|
3482
|
+
breakIdx = i;
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
if (breakIdx === -1) {
|
|
3486
|
+
for (let i = currentMax + 1; i < remaining.length; i++) {
|
|
3487
|
+
if (remaining[i] === "{") inMacro++;
|
|
3488
|
+
else if (remaining[i] === "}") inMacro = Math.max(0, inMacro - 1);
|
|
3489
|
+
else if (remaining[i] === " " && inMacro === 0) {
|
|
3490
|
+
breakIdx = i;
|
|
3491
|
+
break;
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
if (breakIdx === -1) {
|
|
3496
|
+
lines.push(indent + remaining);
|
|
3497
|
+
break;
|
|
3498
|
+
}
|
|
3499
|
+
lines.push(indent + remaining.substring(0, breakIdx));
|
|
3500
|
+
remaining = remaining.substring(breakIdx + 1);
|
|
3501
|
+
}
|
|
3502
|
+
return lines;
|
|
3503
|
+
}
|
|
3266
3504
|
function normalizePassageHeader(line) {
|
|
3267
3505
|
const headerMatch = line.match(/^::\s+(.*)/);
|
|
3268
3506
|
if (!headerMatch) return line;
|
|
@@ -3352,13 +3590,28 @@ import { glob as glob2 } from "glob";
|
|
|
3352
3590
|
function parseArgs2(args2) {
|
|
3353
3591
|
const options = {
|
|
3354
3592
|
check: false,
|
|
3593
|
+
maxLineLength: null,
|
|
3355
3594
|
patterns: []
|
|
3356
3595
|
};
|
|
3357
|
-
|
|
3596
|
+
let i = 0;
|
|
3597
|
+
while (i < args2.length) {
|
|
3598
|
+
const arg = args2[i];
|
|
3358
3599
|
if (arg === "--check") {
|
|
3359
3600
|
options.check = true;
|
|
3601
|
+
i++;
|
|
3602
|
+
} else if (arg === "--max-line-length" && i + 1 < args2.length) {
|
|
3603
|
+
const n = parseInt(args2[i + 1], 10);
|
|
3604
|
+
if (!isNaN(n) && n > 0) options.maxLineLength = n;
|
|
3605
|
+
i += 2;
|
|
3606
|
+
} else if (arg.startsWith("--max-line-length=")) {
|
|
3607
|
+
const n = parseInt(arg.slice("--max-line-length=".length), 10);
|
|
3608
|
+
if (!isNaN(n) && n > 0) options.maxLineLength = n;
|
|
3609
|
+
i++;
|
|
3360
3610
|
} else if (!arg.startsWith("--")) {
|
|
3361
3611
|
options.patterns.push(arg);
|
|
3612
|
+
i++;
|
|
3613
|
+
} else {
|
|
3614
|
+
i++;
|
|
3362
3615
|
}
|
|
3363
3616
|
}
|
|
3364
3617
|
if (options.patterns.length === 0) {
|
|
@@ -3383,11 +3636,15 @@ async function runFormat(args2) {
|
|
|
3383
3636
|
console.log("No files found");
|
|
3384
3637
|
return 0;
|
|
3385
3638
|
}
|
|
3639
|
+
const formatOpts = {};
|
|
3640
|
+
if (options.maxLineLength !== null) {
|
|
3641
|
+
formatOpts.maxLineLength = options.maxLineLength;
|
|
3642
|
+
}
|
|
3386
3643
|
const unformatted = [];
|
|
3387
3644
|
for (const filePath of uniqueFiles) {
|
|
3388
3645
|
try {
|
|
3389
3646
|
const text = readFileSync5(filePath, "utf-8");
|
|
3390
|
-
const formatted = await formatDocument(text);
|
|
3647
|
+
const formatted = await formatDocument(text, formatOpts);
|
|
3391
3648
|
if (formatted !== text) {
|
|
3392
3649
|
if (options.check) {
|
|
3393
3650
|
const relative = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
|
|
@@ -3663,6 +3920,10 @@ function getCompletions(uri, position, triggerChar, workspace) {
|
|
|
3663
3920
|
if (/\{\/[A-Za-z\w-]*$/.test(lineText)) {
|
|
3664
3921
|
return getClosingMacroCompletions(text, position, workspace);
|
|
3665
3922
|
}
|
|
3923
|
+
const transientDotPathMatch = /%([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)?$/.exec(lineText);
|
|
3924
|
+
if (transientDotPathMatch) {
|
|
3925
|
+
return getTransientDotPathCompletions(transientDotPathMatch[1], workspace);
|
|
3926
|
+
}
|
|
3666
3927
|
const dotPathMatch = /\$([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)?$/.exec(lineText);
|
|
3667
3928
|
if (dotPathMatch) {
|
|
3668
3929
|
return getDotPathCompletions(dotPathMatch[1], workspace);
|
|
@@ -3676,6 +3937,9 @@ function getCompletions(uri, position, triggerChar, workspace) {
|
|
|
3676
3937
|
if (/@[A-Za-z_$]?[\w$]*$/.test(lineText)) {
|
|
3677
3938
|
return getLocalVariableCompletions(text);
|
|
3678
3939
|
}
|
|
3940
|
+
if (/%[A-Za-z_$]?[\w$]*$/.test(lineText) && !/%[A-Za-z_$][\w$]*\./.test(lineText)) {
|
|
3941
|
+
return getTransientVariableCompletions(workspace);
|
|
3942
|
+
}
|
|
3679
3943
|
if (/\[\[[^\]]*$/.test(lineText)) {
|
|
3680
3944
|
return getPassageNameCompletions(workspace);
|
|
3681
3945
|
}
|
|
@@ -3803,6 +4067,33 @@ function getDotPathCompletions(varName, workspace) {
|
|
|
3803
4067
|
detail: `field of $${varName}`
|
|
3804
4068
|
}));
|
|
3805
4069
|
}
|
|
4070
|
+
function getTransientVariableCompletions(workspace) {
|
|
4071
|
+
const declared = workspace.variables.getDeclaredTransient();
|
|
4072
|
+
if (declared.size === 0) return [];
|
|
4073
|
+
const items = [];
|
|
4074
|
+
for (const [name, decl] of declared) {
|
|
4075
|
+
items.push({
|
|
4076
|
+
label: `%${name}`,
|
|
4077
|
+
kind: 6,
|
|
4078
|
+
// CompletionItemKind.Variable
|
|
4079
|
+
detail: "transient variable",
|
|
4080
|
+
insertText: name,
|
|
4081
|
+
documentation: decl.fields && decl.fields.length > 0 ? `Fields: ${decl.fields.join(", ")}` : void 0
|
|
4082
|
+
});
|
|
4083
|
+
}
|
|
4084
|
+
return items;
|
|
4085
|
+
}
|
|
4086
|
+
function getTransientDotPathCompletions(varName, workspace) {
|
|
4087
|
+
const declared = workspace.variables.getDeclaredTransient();
|
|
4088
|
+
const decl = declared.get(varName);
|
|
4089
|
+
if (!decl || !decl.fields || decl.fields.length === 0) return [];
|
|
4090
|
+
return decl.fields.map((field) => ({
|
|
4091
|
+
label: field,
|
|
4092
|
+
kind: 5,
|
|
4093
|
+
// CompletionItemKind.Field
|
|
4094
|
+
detail: `field of %${varName}`
|
|
4095
|
+
}));
|
|
4096
|
+
}
|
|
3806
4097
|
function getPassageNameCompletions(workspace) {
|
|
3807
4098
|
const passages = workspace.passages.getAllPassages();
|
|
3808
4099
|
if (passages.length === 0) return [];
|
|
@@ -3822,7 +4113,7 @@ var init_completions = __esm({
|
|
|
3822
4113
|
id: "completions",
|
|
3823
4114
|
capabilities: {
|
|
3824
4115
|
completionProvider: {
|
|
3825
|
-
triggerCharacters: ["{", "$", "_", "@", "[", "."]
|
|
4116
|
+
triggerCharacters: ["{", "$", "_", "@", "%", "[", "."]
|
|
3826
4117
|
}
|
|
3827
4118
|
},
|
|
3828
4119
|
initialize(ctx) {
|
|
@@ -3946,6 +4237,28 @@ Fields: ${decl.fields.map((f) => `\`${f}\``).join(", ")}` : "";
|
|
|
3946
4237
|
}
|
|
3947
4238
|
}
|
|
3948
4239
|
}
|
|
4240
|
+
{
|
|
4241
|
+
const re = /(?<!\w)%([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
4242
|
+
let match;
|
|
4243
|
+
while ((match = re.exec(line)) !== null) {
|
|
4244
|
+
const start = match.index;
|
|
4245
|
+
const end = start + match[0].length;
|
|
4246
|
+
if (position.character >= start && position.character <= end) {
|
|
4247
|
+
const baseName = match[1].split(".")[0];
|
|
4248
|
+
const decl = workspace.variables.getDeclaredTransient().get(baseName);
|
|
4249
|
+
const typeInfo = decl?.fields && decl.fields.length > 0 ? `
|
|
4250
|
+
|
|
4251
|
+
Fields: ${decl.fields.map((f) => `\`${f}\``).join(", ")}` : "";
|
|
4252
|
+
return {
|
|
4253
|
+
contents: `**Transient variable** \`%${match[1]}\`${typeInfo}`,
|
|
4254
|
+
range: {
|
|
4255
|
+
start: { line: position.line, character: start },
|
|
4256
|
+
end: { line: position.line, character: end }
|
|
4257
|
+
}
|
|
4258
|
+
};
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
3949
4262
|
return null;
|
|
3950
4263
|
}
|
|
3951
4264
|
function getWidgetHover(line, position, workspace) {
|
|
@@ -4228,6 +4541,18 @@ function findReferences(uri, position, workspace, includeDeclaration) {
|
|
|
4228
4541
|
}
|
|
4229
4542
|
}
|
|
4230
4543
|
}
|
|
4544
|
+
{
|
|
4545
|
+
const transRegex = /(?<!\w)%([A-Za-z_$][\w$]*)/g;
|
|
4546
|
+
let match;
|
|
4547
|
+
while ((match = transRegex.exec(line)) !== null) {
|
|
4548
|
+
const start = match.index;
|
|
4549
|
+
const end = start + match[0].length;
|
|
4550
|
+
if (position.character >= start && position.character <= end) {
|
|
4551
|
+
const varName = match[1];
|
|
4552
|
+
return findTransientReferences(varName, workspace, includeDeclaration);
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4231
4556
|
{
|
|
4232
4557
|
const widgetRegex = /\{([A-Za-z_$][\w$]*)/g;
|
|
4233
4558
|
let match;
|
|
@@ -4322,6 +4647,20 @@ function findVariableReferences(varName, workspace, includeDeclaration) {
|
|
|
4322
4647
|
}
|
|
4323
4648
|
return locations;
|
|
4324
4649
|
}
|
|
4650
|
+
function findTransientReferences(varName, workspace, includeDeclaration) {
|
|
4651
|
+
const results = [];
|
|
4652
|
+
if (includeDeclaration) {
|
|
4653
|
+
const decl = workspace.variables.getDeclaredTransient().get(varName);
|
|
4654
|
+
if (decl?.declarationUri && decl.declarationRange) {
|
|
4655
|
+
results.push({ uri: decl.declarationUri, range: decl.declarationRange });
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
const usages = workspace.variables.getTransientUsages(varName);
|
|
4659
|
+
for (const u of usages) {
|
|
4660
|
+
results.push({ uri: u.uri, range: u.range });
|
|
4661
|
+
}
|
|
4662
|
+
return results;
|
|
4663
|
+
}
|
|
4325
4664
|
function findWidgetReferences(widgetName, workspace, includeDeclaration) {
|
|
4326
4665
|
const locations = [];
|
|
4327
4666
|
if (includeDeclaration) {
|
|
@@ -4414,8 +4753,9 @@ function computeRename(uri, position, newName, workspace) {
|
|
|
4414
4753
|
break;
|
|
4415
4754
|
}
|
|
4416
4755
|
case "variable": {
|
|
4417
|
-
const bareName = newName.startsWith("$") ? newName.slice(1) : newName;
|
|
4418
|
-
const
|
|
4756
|
+
const bareName = newName.startsWith("$") ? newName.slice(1) : newName.startsWith("%") ? newName.slice(1) : newName;
|
|
4757
|
+
const isTransient = workspace.variables.getDeclaredTransient().has(symbol.name);
|
|
4758
|
+
const refs = isTransient ? findTransientReferences(symbol.name, workspace, true) : findVariableReferences(symbol.name, workspace, true);
|
|
4419
4759
|
for (const ref of refs) {
|
|
4420
4760
|
addEdit(ref.uri, ref.range, bareName);
|
|
4421
4761
|
}
|
|
@@ -4494,6 +4834,24 @@ function resolveSymbolAtCursor(uri, position, workspace) {
|
|
|
4494
4834
|
}
|
|
4495
4835
|
}
|
|
4496
4836
|
}
|
|
4837
|
+
{
|
|
4838
|
+
const transRegex = /(?<!\w)%([A-Za-z_$][\w$]*)/g;
|
|
4839
|
+
let match;
|
|
4840
|
+
while ((match = transRegex.exec(line)) !== null) {
|
|
4841
|
+
const start = match.index;
|
|
4842
|
+
const end = start + match[0].length;
|
|
4843
|
+
if (position.character >= start && position.character <= end) {
|
|
4844
|
+
return {
|
|
4845
|
+
kind: "variable",
|
|
4846
|
+
name: match[1],
|
|
4847
|
+
range: {
|
|
4848
|
+
start: { line: position.line, character: start },
|
|
4849
|
+
end: { line: position.line, character: end }
|
|
4850
|
+
}
|
|
4851
|
+
};
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4497
4855
|
{
|
|
4498
4856
|
const widgetDefRegex2 = /\{widget\s+"([^"]+)"/gi;
|
|
4499
4857
|
let match;
|
|
@@ -4691,6 +5049,7 @@ function computeInlayHints(uri, range, workspace) {
|
|
|
4691
5049
|
const hints = [];
|
|
4692
5050
|
addWidgetParamHints(text, range, workspace, hints);
|
|
4693
5051
|
addVariableTypeHints(uri, text, range, workspace, hints);
|
|
5052
|
+
addTransientTypeHints(uri, text, range, workspace, hints);
|
|
4694
5053
|
return hints;
|
|
4695
5054
|
}
|
|
4696
5055
|
function addWidgetParamHints(text, range, workspace, hints) {
|
|
@@ -4747,6 +5106,28 @@ function addVariableTypeHints(uri, text, range, workspace, hints) {
|
|
|
4747
5106
|
});
|
|
4748
5107
|
}
|
|
4749
5108
|
}
|
|
5109
|
+
function addTransientTypeHints(uri, text, range, workspace, hints) {
|
|
5110
|
+
const storyTransientsPassage = workspace.passages.getStoryTransients();
|
|
5111
|
+
if (!storyTransientsPassage) return;
|
|
5112
|
+
if (storyTransientsPassage.uri !== uri) return;
|
|
5113
|
+
const lines = text.split("\n");
|
|
5114
|
+
const startLine = Math.max(range.start.line, 0);
|
|
5115
|
+
const endLine = Math.min(range.end.line, lines.length - 1);
|
|
5116
|
+
for (let lineNum = startLine; lineNum <= endLine; lineNum++) {
|
|
5117
|
+
const line = lines[lineNum];
|
|
5118
|
+
const declMatch = line.match(/^%([A-Za-z_$][\w$]*)\s*=\s*(.+)$/);
|
|
5119
|
+
if (!declMatch) continue;
|
|
5120
|
+
const valueStr = declMatch[2].trim();
|
|
5121
|
+
const inferredType = inferType(valueStr);
|
|
5122
|
+
if (!inferredType) continue;
|
|
5123
|
+
const eqIdx = line.indexOf("=");
|
|
5124
|
+
hints.push({
|
|
5125
|
+
position: { line: lineNum, character: eqIdx },
|
|
5126
|
+
label: `: ${inferredType}`,
|
|
5127
|
+
kind: "type"
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
4750
5131
|
function inferType(value) {
|
|
4751
5132
|
if (/^\d+(\.\d+)?$/.test(value)) return "number";
|
|
4752
5133
|
if (/^"[^"]*"$/.test(value) || /^'[^']*'$/.test(value)) return "string";
|
|
@@ -4851,6 +5232,7 @@ function computeSemanticTokensAbsolute(uri, workspace) {
|
|
|
4851
5232
|
const storyVarRegex = /(?<!\w)\$([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
4852
5233
|
const tempVarRegex = /(?<!\w)_([A-Za-z_$][\w$]*)/g;
|
|
4853
5234
|
const localVarRegex = /(?<!\w)@([A-Za-z_$][\w$]*)/g;
|
|
5235
|
+
const transientVarRegex = /(?<!\w)%([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
4854
5236
|
const sugarKeywordRegex = /\b(to|is|isnot|eq|neq|gt|gte|lt|lte|and|or|not|def|ndef)\b/g;
|
|
4855
5237
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
4856
5238
|
if (headerLines.has(lineIndex)) continue;
|
|
@@ -4886,6 +5268,16 @@ function computeSemanticTokensAbsolute(uri, workspace) {
|
|
|
4886
5268
|
tokenModifiers: encodeModifiers(["readonly"])
|
|
4887
5269
|
});
|
|
4888
5270
|
}
|
|
5271
|
+
transientVarRegex.lastIndex = 0;
|
|
5272
|
+
while ((m = transientVarRegex.exec(line)) !== null) {
|
|
5273
|
+
tokens.push({
|
|
5274
|
+
line: lineIndex,
|
|
5275
|
+
startChar: m.index,
|
|
5276
|
+
length: m[0].length,
|
|
5277
|
+
tokenType: encodeType("variable"),
|
|
5278
|
+
tokenModifiers: encodeModifiers(["defaultLibrary"])
|
|
5279
|
+
});
|
|
5280
|
+
}
|
|
4889
5281
|
sugarKeywordRegex.lastIndex = 0;
|
|
4890
5282
|
while ((m = sugarKeywordRegex.exec(line)) !== null) {
|
|
4891
5283
|
tokens.push({
|
|
@@ -4990,6 +5382,11 @@ function computeCodeActions(uri, diagnostics, workspace) {
|
|
|
4990
5382
|
if (action) actions.push(action);
|
|
4991
5383
|
break;
|
|
4992
5384
|
}
|
|
5385
|
+
case DiagnosticCode.UndeclaredTransient: {
|
|
5386
|
+
const action = fixUndeclaredTransient(diag, workspace);
|
|
5387
|
+
if (action) actions.push(action);
|
|
5388
|
+
break;
|
|
5389
|
+
}
|
|
4993
5390
|
}
|
|
4994
5391
|
}
|
|
4995
5392
|
return actions;
|
|
@@ -5072,6 +5469,40 @@ function fixNoStoryVariables(uri, workspace) {
|
|
|
5072
5469
|
}]
|
|
5073
5470
|
};
|
|
5074
5471
|
}
|
|
5472
|
+
function fixUndeclaredTransient(diag, workspace) {
|
|
5473
|
+
const match = diag.message.match(/'%(\w+)'/);
|
|
5474
|
+
if (!match) return null;
|
|
5475
|
+
const varName = match[1];
|
|
5476
|
+
const storyTransients = workspace.passages.getStoryTransients();
|
|
5477
|
+
if (!storyTransients) return null;
|
|
5478
|
+
const storyTransientsUri = storyTransients.uri;
|
|
5479
|
+
const text = workspace.documents.getText(storyTransientsUri);
|
|
5480
|
+
if (text === void 0) return null;
|
|
5481
|
+
const lines = text.split("\n");
|
|
5482
|
+
const contentStart = storyTransients.headerEnd.end.line + 1;
|
|
5483
|
+
let contentEnd = lines.length;
|
|
5484
|
+
for (let i = contentStart; i < lines.length; i++) {
|
|
5485
|
+
if (/^::\s+/.test(lines[i])) {
|
|
5486
|
+
contentEnd = i;
|
|
5487
|
+
break;
|
|
5488
|
+
}
|
|
5489
|
+
}
|
|
5490
|
+
const insertLine = contentEnd;
|
|
5491
|
+
return {
|
|
5492
|
+
title: `Declare '%${varName}' in StoryTransients`,
|
|
5493
|
+
kind: "quickfix",
|
|
5494
|
+
diagnosticCodes: [DiagnosticCode.UndeclaredTransient],
|
|
5495
|
+
edits: [{
|
|
5496
|
+
uri: storyTransientsUri,
|
|
5497
|
+
range: {
|
|
5498
|
+
start: { line: insertLine, character: 0 },
|
|
5499
|
+
end: { line: insertLine, character: 0 }
|
|
5500
|
+
},
|
|
5501
|
+
newText: `%${varName} = null
|
|
5502
|
+
`
|
|
5503
|
+
}]
|
|
5504
|
+
};
|
|
5505
|
+
}
|
|
5075
5506
|
function toLspRange6(r) {
|
|
5076
5507
|
return {
|
|
5077
5508
|
start: { line: r.start.line, character: r.start.character },
|