@rohal12/spindle-lsp 0.10.0 → 0.11.0
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 +514 -240
- package/dist/bin.js.map +4 -4
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -6694,7 +6694,7 @@ var init_macro_registry = __esm({
|
|
|
6694
6694
|
}
|
|
6695
6695
|
});
|
|
6696
6696
|
|
|
6697
|
-
// src/core/parsing/
|
|
6697
|
+
// src/core/parsing/code-scanner.ts
|
|
6698
6698
|
function buildCodeScanner(input, from) {
|
|
6699
6699
|
const n = input.length;
|
|
6700
6700
|
const close = new Int32Array(n + 2).fill(-1);
|
|
@@ -6745,6 +6745,293 @@ function buildCodeScanner(input, from) {
|
|
|
6745
6745
|
function createCodeScanner(input) {
|
|
6746
6746
|
return buildCodeScanner(input, 0);
|
|
6747
6747
|
}
|
|
6748
|
+
var NON_STRING_QUOTE_PREFIX;
|
|
6749
|
+
var init_code_scanner = __esm({
|
|
6750
|
+
"src/core/parsing/code-scanner.ts"() {
|
|
6751
|
+
"use strict";
|
|
6752
|
+
NON_STRING_QUOTE_PREFIX = /[\p{L}\p{N}_\\]/u;
|
|
6753
|
+
}
|
|
6754
|
+
});
|
|
6755
|
+
|
|
6756
|
+
// src/core/parsing/html-scanner.ts
|
|
6757
|
+
function plainBraceMatches(input) {
|
|
6758
|
+
const match2 = new Int32Array(input.length).fill(-1);
|
|
6759
|
+
const open = [];
|
|
6760
|
+
for (let i = 0; i < input.length; i++) {
|
|
6761
|
+
if (input[i] === "{") open.push(i);
|
|
6762
|
+
else if (input[i] === "}" && open.length > 0) match2[open.pop()] = i;
|
|
6763
|
+
}
|
|
6764
|
+
return match2;
|
|
6765
|
+
}
|
|
6766
|
+
function scanHtmlTags(text) {
|
|
6767
|
+
const n = text.length;
|
|
6768
|
+
const tags = [];
|
|
6769
|
+
const macros = [];
|
|
6770
|
+
const code = createCodeScanner(text);
|
|
6771
|
+
const plain = plainBraceMatches(text);
|
|
6772
|
+
const rawBodies = [];
|
|
6773
|
+
let doCloses;
|
|
6774
|
+
let nextDoClose = 0;
|
|
6775
|
+
const rawBody = (end) => {
|
|
6776
|
+
doCloses ??= [...text.matchAll(/\{\/do\s*\}/gi)].map((match2) => match2.index);
|
|
6777
|
+
while (nextDoClose < doCloses.length && doCloses[nextDoClose] < end) nextDoClose++;
|
|
6778
|
+
if (nextDoClose < doCloses.length) rawBodies.push([end, doCloses[nextDoClose]]);
|
|
6779
|
+
};
|
|
6780
|
+
const rawBodyLimit = (limit2) => {
|
|
6781
|
+
if (rawBodies.length === 0) return limit2;
|
|
6782
|
+
const macroStarts = new Set(macros);
|
|
6783
|
+
let t = 0;
|
|
6784
|
+
for (const [start, close] of rawBodies) {
|
|
6785
|
+
if (start >= limit2) break;
|
|
6786
|
+
if (close < limit2 && !macroStarts.has(close)) {
|
|
6787
|
+
limit2 = start;
|
|
6788
|
+
break;
|
|
6789
|
+
}
|
|
6790
|
+
while (t < tags.length && tags[t].start < start) t++;
|
|
6791
|
+
if (t < tags.length && tags[t].start < Math.min(close, limit2)) limit2 = tags[t].start;
|
|
6792
|
+
}
|
|
6793
|
+
return limit2;
|
|
6794
|
+
};
|
|
6795
|
+
const braceEnd = (open) => {
|
|
6796
|
+
const close = code.closeBrace(open + 1);
|
|
6797
|
+
if (close !== plain[open]) return GIVE_UP;
|
|
6798
|
+
return close === -1 ? open + 1 : close + 1;
|
|
6799
|
+
};
|
|
6800
|
+
const displayEnd = (open, sigil) => {
|
|
6801
|
+
let j = sigil + 1;
|
|
6802
|
+
while (j < n && DISPLAY_NAME_CHAR.test(text[j])) j++;
|
|
6803
|
+
return text[j] === "}" ? j + 1 : braceEnd(open);
|
|
6804
|
+
};
|
|
6805
|
+
const afterBrace = (i2) => {
|
|
6806
|
+
let at = i2 + 1;
|
|
6807
|
+
const selectors = text[at] === "." || text[at] === "#";
|
|
6808
|
+
if (selectors) {
|
|
6809
|
+
at = selectorsEnd(text, at);
|
|
6810
|
+
if (text[at] === " ") at++;
|
|
6811
|
+
}
|
|
6812
|
+
const c3 = text[at];
|
|
6813
|
+
if (c3 === "$" || c3 === "_" || c3 === "@" || c3 === "%") return displayEnd(i2, at);
|
|
6814
|
+
if (c3 === void 0) return i2 + 1;
|
|
6815
|
+
if (LETTER.test(c3) || c3 === "/" && !selectors) {
|
|
6816
|
+
const end = braceEnd(i2);
|
|
6817
|
+
if (end > i2 + 1) {
|
|
6818
|
+
macros.push(i2);
|
|
6819
|
+
if (/^do$/i.test(text.slice(at, end - 1).trim().split(/\s/)[0])) rawBody(end);
|
|
6820
|
+
}
|
|
6821
|
+
return end;
|
|
6822
|
+
}
|
|
6823
|
+
return i2 + 1;
|
|
6824
|
+
};
|
|
6825
|
+
const readTag = (i2) => {
|
|
6826
|
+
let j = i2 + 1;
|
|
6827
|
+
const isClose = text[j] === "/";
|
|
6828
|
+
if (isClose) j++;
|
|
6829
|
+
const nameStart = j;
|
|
6830
|
+
while (j < n && TAG_NAME_CHAR.test(text[j])) j++;
|
|
6831
|
+
const name = text.slice(nameStart, j);
|
|
6832
|
+
if (!name || !LETTER.test(name[0])) return i2 + 1;
|
|
6833
|
+
const isVoid = VOID_ELEMENTS.has(name.toLowerCase());
|
|
6834
|
+
if (isClose) {
|
|
6835
|
+
while (j < n && WHITESPACE.test(text[j])) j++;
|
|
6836
|
+
if (text[j] !== ">") return i2 + 1;
|
|
6837
|
+
if (!isVoid) tags.push({ name, kind: "close", start: i2, end: j + 1 });
|
|
6838
|
+
return j + 1;
|
|
6839
|
+
}
|
|
6840
|
+
const attrs = readAttributes(j);
|
|
6841
|
+
if (attrs.end === GIVE_UP) return GIVE_UP;
|
|
6842
|
+
j = attrs.end;
|
|
6843
|
+
let selfClosing = isVoid;
|
|
6844
|
+
if (text[j] === "/") {
|
|
6845
|
+
selfClosing = true;
|
|
6846
|
+
j++;
|
|
6847
|
+
}
|
|
6848
|
+
if (text[j] === ">") {
|
|
6849
|
+
tags.push({ name, kind: selfClosing ? "void" : "open", start: i2, end: j + 1, values: attrs.values });
|
|
6850
|
+
return j + 1;
|
|
6851
|
+
}
|
|
6852
|
+
return attrs.count > 0 ? GIVE_UP : i2 + 1;
|
|
6853
|
+
};
|
|
6854
|
+
const readAttributes = (j) => {
|
|
6855
|
+
let count = 0;
|
|
6856
|
+
const values = [];
|
|
6857
|
+
while (j < n) {
|
|
6858
|
+
while (j < n && WHITESPACE.test(text[j])) j++;
|
|
6859
|
+
if (j >= n || text[j] === ">" || text[j] === "/" && text[j + 1] === ">") break;
|
|
6860
|
+
const nameStart = j;
|
|
6861
|
+
while (j < n && ATTR_NAME_CHAR.test(text[j])) j++;
|
|
6862
|
+
if (j === nameStart) break;
|
|
6863
|
+
count++;
|
|
6864
|
+
if (text[j] !== "=") continue;
|
|
6865
|
+
j++;
|
|
6866
|
+
const quote = text[j];
|
|
6867
|
+
if (quote === '"' || quote === "'") {
|
|
6868
|
+
const close = quotedValueEnd(j + 1, quote);
|
|
6869
|
+
if (close === GIVE_UP) return { end: GIVE_UP, count, values };
|
|
6870
|
+
values.push([j + 1, close]);
|
|
6871
|
+
j = close < n ? close + 1 : close;
|
|
6872
|
+
} else {
|
|
6873
|
+
const start = j;
|
|
6874
|
+
while (j < n && !WHITESPACE.test(text[j]) && text[j] !== ">") j++;
|
|
6875
|
+
values.push([start, j]);
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6878
|
+
return { end: j, count, values };
|
|
6879
|
+
};
|
|
6880
|
+
const quotedValueEnd = (from, quote) => {
|
|
6881
|
+
let depth = 0;
|
|
6882
|
+
let sawBrace = false;
|
|
6883
|
+
let k = from;
|
|
6884
|
+
for (; k < n; k++) {
|
|
6885
|
+
const c3 = text[k];
|
|
6886
|
+
if (c3 === "{") {
|
|
6887
|
+
depth++;
|
|
6888
|
+
sawBrace = true;
|
|
6889
|
+
} else if (c3 === "}") {
|
|
6890
|
+
depth--;
|
|
6891
|
+
sawBrace = true;
|
|
6892
|
+
} else if (c3 === quote && depth <= 0) {
|
|
6893
|
+
break;
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
if (!sawBrace) return k;
|
|
6897
|
+
let d = from;
|
|
6898
|
+
while (d < n) {
|
|
6899
|
+
if (text[d] === "{") {
|
|
6900
|
+
const close = code.closeBrace(d + 1);
|
|
6901
|
+
if (close !== -1) {
|
|
6902
|
+
d = close + 1;
|
|
6903
|
+
continue;
|
|
6904
|
+
}
|
|
6905
|
+
} else if (text[d] === quote) {
|
|
6906
|
+
break;
|
|
6907
|
+
}
|
|
6908
|
+
d++;
|
|
6909
|
+
}
|
|
6910
|
+
return d === k ? k : GIVE_UP;
|
|
6911
|
+
};
|
|
6912
|
+
let i = 0;
|
|
6913
|
+
let stoppedAt = -1;
|
|
6914
|
+
while (i < n) {
|
|
6915
|
+
const c3 = text[i];
|
|
6916
|
+
let next;
|
|
6917
|
+
if (c3 === "\\") {
|
|
6918
|
+
let k = i + 1;
|
|
6919
|
+
while (text[k] === "\\") k++;
|
|
6920
|
+
if (text[k] === "{" || text[k] === "}") next = (k - i) % 2 === 1 ? k + 1 : GIVE_UP;
|
|
6921
|
+
else next = k;
|
|
6922
|
+
} else if (c3 === "[" && text[i + 1] === "[") {
|
|
6923
|
+
next = linkEnd(text, i);
|
|
6924
|
+
} else if (c3 === "{") {
|
|
6925
|
+
next = afterBrace(i);
|
|
6926
|
+
} else if (c3 === "<") {
|
|
6927
|
+
next = readTag(i);
|
|
6928
|
+
} else {
|
|
6929
|
+
next = i + 1;
|
|
6930
|
+
}
|
|
6931
|
+
if (next === GIVE_UP) {
|
|
6932
|
+
stoppedAt = i;
|
|
6933
|
+
break;
|
|
6934
|
+
}
|
|
6935
|
+
i = next;
|
|
6936
|
+
}
|
|
6937
|
+
const limit = rawBodyLimit(stoppedAt === -1 ? n : stoppedAt);
|
|
6938
|
+
if (limit === stoppedAt || limit === n) return { tags, stoppedAt, macros };
|
|
6939
|
+
return {
|
|
6940
|
+
tags: tags.filter((tag) => tag.start < limit),
|
|
6941
|
+
stoppedAt: limit,
|
|
6942
|
+
macros: macros.filter((at) => at < limit)
|
|
6943
|
+
};
|
|
6944
|
+
}
|
|
6945
|
+
function selectorsEnd(text, i) {
|
|
6946
|
+
const n = text.length;
|
|
6947
|
+
while (i < n && (text[i] === "." || text[i] === "#")) {
|
|
6948
|
+
i++;
|
|
6949
|
+
while (i < n) {
|
|
6950
|
+
if (SELECTOR_NAME_CHAR.test(text[i])) {
|
|
6951
|
+
i++;
|
|
6952
|
+
} else if (text[i] === "{" && (text[i + 1] === "$" || text[i + 1] === "_" || text[i + 1] === "@")) {
|
|
6953
|
+
let j = i + 2;
|
|
6954
|
+
while (j < n && DISPLAY_NAME_CHAR.test(text[j])) j++;
|
|
6955
|
+
if (text[j] !== "}") break;
|
|
6956
|
+
i = j + 1;
|
|
6957
|
+
} else {
|
|
6958
|
+
break;
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
return i;
|
|
6963
|
+
}
|
|
6964
|
+
function linkEnd(text, i) {
|
|
6965
|
+
const n = text.length;
|
|
6966
|
+
let depth = 1;
|
|
6967
|
+
let j = i + 2;
|
|
6968
|
+
while (j < n) {
|
|
6969
|
+
if (text[j] === "[" && text[j + 1] === "[") {
|
|
6970
|
+
depth++;
|
|
6971
|
+
j += 2;
|
|
6972
|
+
} else if (text[j] === "]" && text[j + 1] === "]") {
|
|
6973
|
+
if (--depth === 0) return j + 2;
|
|
6974
|
+
j += 2;
|
|
6975
|
+
} else {
|
|
6976
|
+
j++;
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
return GIVE_UP;
|
|
6980
|
+
}
|
|
6981
|
+
function attributeValueSpans(text) {
|
|
6982
|
+
const spans = [];
|
|
6983
|
+
if (!text.includes("<")) return spans;
|
|
6984
|
+
const headers = [...text.matchAll(/^::.*$/gm)];
|
|
6985
|
+
const scan = (from2, to) => {
|
|
6986
|
+
const content = text.slice(from2, to);
|
|
6987
|
+
if (!content.includes("<")) return;
|
|
6988
|
+
for (const tag of scanHtmlTags(content).tags) {
|
|
6989
|
+
for (const [start, end] of tag.values ?? []) spans.push([from2 + start, from2 + end]);
|
|
6990
|
+
}
|
|
6991
|
+
};
|
|
6992
|
+
let from = 0;
|
|
6993
|
+
for (const header of headers) {
|
|
6994
|
+
scan(from, header.index);
|
|
6995
|
+
from = header.index + header[0].length;
|
|
6996
|
+
}
|
|
6997
|
+
scan(from, text.length);
|
|
6998
|
+
return spans;
|
|
6999
|
+
}
|
|
7000
|
+
function inAttributeValue(text, offset) {
|
|
7001
|
+
return attributeValueSpans(text).some(([start, end]) => start <= offset && offset < end);
|
|
7002
|
+
}
|
|
7003
|
+
var VOID_ELEMENTS, TAG_NAME_CHAR, LETTER, ATTR_NAME_CHAR, WHITESPACE, SELECTOR_NAME_CHAR, DISPLAY_NAME_CHAR, GIVE_UP;
|
|
7004
|
+
var init_html_scanner = __esm({
|
|
7005
|
+
"src/core/parsing/html-scanner.ts"() {
|
|
7006
|
+
"use strict";
|
|
7007
|
+
init_code_scanner();
|
|
7008
|
+
VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
7009
|
+
"area",
|
|
7010
|
+
"base",
|
|
7011
|
+
"br",
|
|
7012
|
+
"col",
|
|
7013
|
+
"embed",
|
|
7014
|
+
"hr",
|
|
7015
|
+
"img",
|
|
7016
|
+
"input",
|
|
7017
|
+
"link",
|
|
7018
|
+
"meta",
|
|
7019
|
+
"param",
|
|
7020
|
+
"source",
|
|
7021
|
+
"track",
|
|
7022
|
+
"wbr"
|
|
7023
|
+
]);
|
|
7024
|
+
TAG_NAME_CHAR = /[a-zA-Z0-9-]/;
|
|
7025
|
+
LETTER = /[a-zA-Z]/;
|
|
7026
|
+
ATTR_NAME_CHAR = /[a-zA-Z0-9_\-:@]/;
|
|
7027
|
+
WHITESPACE = /[^\S ]/;
|
|
7028
|
+
SELECTOR_NAME_CHAR = /[a-zA-Z0-9_-]/;
|
|
7029
|
+
DISPLAY_NAME_CHAR = /[\w.]/;
|
|
7030
|
+
GIVE_UP = -2;
|
|
7031
|
+
}
|
|
7032
|
+
});
|
|
7033
|
+
|
|
7034
|
+
// src/core/parsing/macro-parser.ts
|
|
6748
7035
|
function buildLineStarts(text) {
|
|
6749
7036
|
const starts = [0];
|
|
6750
7037
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -6775,10 +7062,20 @@ function parseMacros(text) {
|
|
|
6775
7062
|
const scanner = createCodeScanner(cleaned);
|
|
6776
7063
|
const macros = [];
|
|
6777
7064
|
let id = 0;
|
|
7065
|
+
const attributeValues = attributeValueSpans(text);
|
|
7066
|
+
let value = 0;
|
|
7067
|
+
const inAttributeValue2 = (offset) => {
|
|
7068
|
+
while (value < attributeValues.length && attributeValues[value][1] <= offset) value++;
|
|
7069
|
+
return value < attributeValues.length && attributeValues[value][0] <= offset;
|
|
7070
|
+
};
|
|
6778
7071
|
macroHeadRegex.lastIndex = 0;
|
|
6779
7072
|
let match2;
|
|
6780
7073
|
while ((match2 = macroHeadRegex.exec(text)) !== null) {
|
|
6781
7074
|
const matchStart = match2.index;
|
|
7075
|
+
if (inAttributeValue2(matchStart)) {
|
|
7076
|
+
macroHeadRegex.lastIndex = matchStart + 1;
|
|
7077
|
+
continue;
|
|
7078
|
+
}
|
|
6782
7079
|
const closeIdx = scanner.closeBrace(matchStart + 1);
|
|
6783
7080
|
if (closeIdx === -1) {
|
|
6784
7081
|
macroHeadRegex.lastIndex = matchStart + 1;
|
|
@@ -6838,17 +7135,19 @@ function pairMacros(macros, isBlock, passageStartLines = []) {
|
|
|
6838
7135
|
}
|
|
6839
7136
|
}
|
|
6840
7137
|
}
|
|
6841
|
-
var variableInterpolationRegex, SELECTOR_PATTERN, macroHeadRegex
|
|
7138
|
+
var variableInterpolationRegex, SELECTOR_PATTERN, macroHeadRegex;
|
|
6842
7139
|
var init_macro_parser = __esm({
|
|
6843
7140
|
"src/core/parsing/macro-parser.ts"() {
|
|
6844
7141
|
"use strict";
|
|
7142
|
+
init_code_scanner();
|
|
7143
|
+
init_html_scanner();
|
|
7144
|
+
init_code_scanner();
|
|
6845
7145
|
variableInterpolationRegex = /(?<!\\)\{([$_@%][A-Za-z_$][\w$.]*)\}/g;
|
|
6846
7146
|
SELECTOR_PATTERN = String.raw`(?:[.#](?:[\w-]|\{[$_@][\w.]*\})*)+`;
|
|
6847
7147
|
macroHeadRegex = new RegExp(
|
|
6848
7148
|
String.raw`(?<!\\)\{(?:(\/)|(${SELECTOR_PATTERN}) )?([A-Za-z][\w-]*)(?=[\s}]|\{[$_@%][A-Za-z_$][\w$.]*\})`,
|
|
6849
7149
|
"gi"
|
|
6850
7150
|
);
|
|
6851
|
-
NON_STRING_QUOTE_PREFIX = /[\p{L}\p{N}_\\]/u;
|
|
6852
7151
|
}
|
|
6853
7152
|
});
|
|
6854
7153
|
|
|
@@ -16107,6 +16406,7 @@ var init_diagnostic_codes = __esm({
|
|
|
16107
16406
|
UndefinedMacro: "SP100",
|
|
16108
16407
|
MalformedContainer: "SP101",
|
|
16109
16408
|
MalformedElement: "SP102",
|
|
16409
|
+
UnevaluatedAttributeBlock: "SP103",
|
|
16110
16410
|
IllegalClosingTag: "SP104",
|
|
16111
16411
|
InvalidChildren: "SP107",
|
|
16112
16412
|
ExpectedNoArguments: "SP108",
|
|
@@ -16136,6 +16436,7 @@ var init_diagnostic_codes = __esm({
|
|
|
16136
16436
|
SP100: "warning",
|
|
16137
16437
|
SP101: "error",
|
|
16138
16438
|
SP102: "error",
|
|
16439
|
+
SP103: "warning",
|
|
16139
16440
|
SP104: "error",
|
|
16140
16441
|
SP107: "error",
|
|
16141
16442
|
SP108: "error",
|
|
@@ -16434,255 +16735,159 @@ var init_argument_lexer = __esm({
|
|
|
16434
16735
|
}
|
|
16435
16736
|
});
|
|
16436
16737
|
|
|
16437
|
-
// src/core/parsing/
|
|
16438
|
-
function
|
|
16439
|
-
const
|
|
16440
|
-
|
|
16441
|
-
|
|
16442
|
-
|
|
16443
|
-
|
|
16444
|
-
|
|
16445
|
-
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
|
|
16450
|
-
const macros = [];
|
|
16451
|
-
const code = createCodeScanner(text);
|
|
16452
|
-
const plain = plainBraceMatches(text);
|
|
16453
|
-
const rawBodies = [];
|
|
16454
|
-
let doCloses;
|
|
16455
|
-
let nextDoClose = 0;
|
|
16456
|
-
const rawBody = (end) => {
|
|
16457
|
-
doCloses ??= [...text.matchAll(/\{\/do\s*\}/gi)].map((match2) => match2.index);
|
|
16458
|
-
while (nextDoClose < doCloses.length && doCloses[nextDoClose] < end) nextDoClose++;
|
|
16459
|
-
if (nextDoClose < doCloses.length) rawBodies.push([end, doCloses[nextDoClose]]);
|
|
16460
|
-
};
|
|
16461
|
-
const rawBodyLimit = (limit2) => {
|
|
16462
|
-
if (rawBodies.length === 0) return limit2;
|
|
16463
|
-
const macroStarts = new Set(macros);
|
|
16464
|
-
let t = 0;
|
|
16465
|
-
for (const [start, close] of rawBodies) {
|
|
16466
|
-
if (start >= limit2) break;
|
|
16467
|
-
if (close < limit2 && !macroStarts.has(close)) {
|
|
16468
|
-
limit2 = start;
|
|
16469
|
-
break;
|
|
16470
|
-
}
|
|
16471
|
-
while (t < tags.length && tags[t].start < start) t++;
|
|
16472
|
-
if (t < tags.length && tags[t].start < Math.min(close, limit2)) limit2 = tags[t].start;
|
|
16473
|
-
}
|
|
16474
|
-
return limit2;
|
|
16475
|
-
};
|
|
16476
|
-
const braceEnd = (open) => {
|
|
16477
|
-
const close = code.closeBrace(open + 1);
|
|
16478
|
-
if (close !== plain[open]) return GIVE_UP;
|
|
16479
|
-
return close === -1 ? open + 1 : close + 1;
|
|
16480
|
-
};
|
|
16481
|
-
const displayEnd = (open, sigil) => {
|
|
16482
|
-
let j = sigil + 1;
|
|
16483
|
-
while (j < n && DISPLAY_NAME_CHAR.test(text[j])) j++;
|
|
16484
|
-
return text[j] === "}" ? j + 1 : braceEnd(open);
|
|
16485
|
-
};
|
|
16486
|
-
const afterBrace = (i2) => {
|
|
16487
|
-
let at = i2 + 1;
|
|
16488
|
-
const selectors = text[at] === "." || text[at] === "#";
|
|
16489
|
-
if (selectors) {
|
|
16490
|
-
at = selectorsEnd(text, at);
|
|
16491
|
-
if (text[at] === " ") at++;
|
|
16492
|
-
}
|
|
16493
|
-
const c3 = text[at];
|
|
16494
|
-
if (c3 === "$" || c3 === "_" || c3 === "@" || c3 === "%") return displayEnd(i2, at);
|
|
16495
|
-
if (c3 === void 0) return i2 + 1;
|
|
16496
|
-
if (LETTER.test(c3) || c3 === "/" && !selectors) {
|
|
16497
|
-
const end = braceEnd(i2);
|
|
16498
|
-
if (end > i2 + 1) {
|
|
16499
|
-
macros.push(i2);
|
|
16500
|
-
if (/^do$/i.test(text.slice(at, end - 1).trim().split(/\s/)[0])) rawBody(end);
|
|
16501
|
-
}
|
|
16502
|
-
return end;
|
|
16503
|
-
}
|
|
16504
|
-
return i2 + 1;
|
|
16505
|
-
};
|
|
16506
|
-
const readTag = (i2) => {
|
|
16507
|
-
let j = i2 + 1;
|
|
16508
|
-
const isClose = text[j] === "/";
|
|
16509
|
-
if (isClose) j++;
|
|
16510
|
-
const nameStart = j;
|
|
16511
|
-
while (j < n && TAG_NAME_CHAR.test(text[j])) j++;
|
|
16512
|
-
const name = text.slice(nameStart, j);
|
|
16513
|
-
if (!name || !LETTER.test(name[0])) return i2 + 1;
|
|
16514
|
-
const isVoid = VOID_ELEMENTS.has(name.toLowerCase());
|
|
16515
|
-
if (isClose) {
|
|
16516
|
-
while (j < n && WHITESPACE.test(text[j])) j++;
|
|
16517
|
-
if (text[j] !== ">") return i2 + 1;
|
|
16518
|
-
if (!isVoid) tags.push({ name, kind: "close", start: i2, end: j + 1 });
|
|
16519
|
-
return j + 1;
|
|
16520
|
-
}
|
|
16521
|
-
const attrs = readAttributes(j);
|
|
16522
|
-
if (attrs.end === GIVE_UP) return GIVE_UP;
|
|
16523
|
-
j = attrs.end;
|
|
16524
|
-
let selfClosing = isVoid;
|
|
16525
|
-
if (text[j] === "/") {
|
|
16526
|
-
selfClosing = true;
|
|
16527
|
-
j++;
|
|
16528
|
-
}
|
|
16529
|
-
if (text[j] === ">") {
|
|
16530
|
-
tags.push({ name, kind: selfClosing ? "void" : "open", start: i2, end: j + 1 });
|
|
16531
|
-
return j + 1;
|
|
16738
|
+
// src/core/parsing/attribute-blocks.ts
|
|
16739
|
+
function findUnevaluatedBlocks(value, lookup) {
|
|
16740
|
+
const blocks = [];
|
|
16741
|
+
if (!value.includes("{")) return blocks;
|
|
16742
|
+
const code = createCodeScanner(value);
|
|
16743
|
+
let i = 0;
|
|
16744
|
+
while (i < value.length) {
|
|
16745
|
+
i = value.indexOf("{", i);
|
|
16746
|
+
if (i === -1) break;
|
|
16747
|
+
const block = readBlock(value, code, i);
|
|
16748
|
+
if (block.kind === "skip") {
|
|
16749
|
+
i = block.next;
|
|
16750
|
+
continue;
|
|
16532
16751
|
}
|
|
16533
|
-
|
|
16534
|
-
|
|
16535
|
-
|
|
16536
|
-
|
|
16537
|
-
|
|
16538
|
-
|
|
16539
|
-
|
|
16540
|
-
const nameStart = j;
|
|
16541
|
-
while (j < n && ATTR_NAME_CHAR.test(text[j])) j++;
|
|
16542
|
-
if (j === nameStart) break;
|
|
16543
|
-
count++;
|
|
16544
|
-
if (text[j] !== "=") continue;
|
|
16545
|
-
j++;
|
|
16546
|
-
const quote = text[j];
|
|
16547
|
-
if (quote === '"' || quote === "'") {
|
|
16548
|
-
const close = quotedValueEnd(j + 1, quote);
|
|
16549
|
-
if (close === GIVE_UP) return { end: GIVE_UP, count };
|
|
16550
|
-
j = close < n ? close + 1 : close;
|
|
16551
|
-
} else {
|
|
16552
|
-
while (j < n && !WHITESPACE.test(text[j]) && text[j] !== ">") j++;
|
|
16752
|
+
const inner = value.slice(i + 1, block.close);
|
|
16753
|
+
const head = MACRO_HEAD.exec(inner);
|
|
16754
|
+
if (head && lookup.isMacro(head[2])) {
|
|
16755
|
+
const closing = head[1] === "/";
|
|
16756
|
+
let end = block.close + 1;
|
|
16757
|
+
if (!closing && lookup.isContainer(head[2])) {
|
|
16758
|
+
end = containerEnd(value, code, end, head[2]) ?? end;
|
|
16553
16759
|
}
|
|
16760
|
+
blocks.push({ start: i, end, kind: "macro", macro: closing ? `/${head[2]}` : head[2] });
|
|
16761
|
+
i = end;
|
|
16762
|
+
} else if (usesVariable(inner)) {
|
|
16763
|
+
blocks.push({ start: i, end: block.close + 1, kind: "expression" });
|
|
16764
|
+
i = block.close + 1;
|
|
16765
|
+
} else {
|
|
16766
|
+
i++;
|
|
16554
16767
|
}
|
|
16555
|
-
|
|
16556
|
-
|
|
16557
|
-
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
|
|
16561
|
-
|
|
16562
|
-
|
|
16563
|
-
|
|
16564
|
-
|
|
16565
|
-
|
|
16566
|
-
|
|
16567
|
-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16768
|
+
}
|
|
16769
|
+
return blocks;
|
|
16770
|
+
}
|
|
16771
|
+
function readBlock(value, code, i) {
|
|
16772
|
+
if (i > 0 && value[i - 1] === "\\") return { kind: "skip", next: i + 1 };
|
|
16773
|
+
if (SIGILS.has(value[i + 1])) {
|
|
16774
|
+
const end = Math.max(plainClose(value, i + 1), code.closeBrace(i + 1));
|
|
16775
|
+
return { kind: "skip", next: end === -1 ? i + 1 : end + 1 };
|
|
16776
|
+
}
|
|
16777
|
+
const close = code.closeBrace(i + 1);
|
|
16778
|
+
return close === -1 ? { kind: "skip", next: i + 1 } : { kind: "text", close };
|
|
16779
|
+
}
|
|
16780
|
+
function plainClose(value, i) {
|
|
16781
|
+
let depth = 1;
|
|
16782
|
+
for (; i < value.length; i++) {
|
|
16783
|
+
if (value[i] === "{") depth++;
|
|
16784
|
+
else if (value[i] === "}" && --depth === 0) return i;
|
|
16785
|
+
}
|
|
16786
|
+
return -1;
|
|
16787
|
+
}
|
|
16788
|
+
function containerEnd(value, code, from, name) {
|
|
16789
|
+
const lower = name.toLowerCase();
|
|
16790
|
+
let depth = 1;
|
|
16791
|
+
let i = from;
|
|
16792
|
+
while (i < value.length) {
|
|
16793
|
+
i = value.indexOf("{", i);
|
|
16794
|
+
if (i === -1) break;
|
|
16795
|
+
const block = readBlock(value, code, i);
|
|
16796
|
+
if (block.kind === "skip") {
|
|
16797
|
+
i = block.next;
|
|
16798
|
+
continue;
|
|
16572
16799
|
}
|
|
16573
|
-
|
|
16574
|
-
|
|
16575
|
-
|
|
16576
|
-
if (
|
|
16577
|
-
|
|
16578
|
-
|
|
16579
|
-
|
|
16580
|
-
continue;
|
|
16581
|
-
}
|
|
16582
|
-
} else if (text[d] === quote) {
|
|
16583
|
-
break;
|
|
16584
|
-
}
|
|
16585
|
-
d++;
|
|
16800
|
+
const head = MACRO_HEAD.exec(value.slice(i + 1, block.close));
|
|
16801
|
+
if (head && head[2].toLowerCase() === lower) {
|
|
16802
|
+
depth += head[1] === "/" ? -1 : 1;
|
|
16803
|
+
if (depth === 0) return block.close + 1;
|
|
16804
|
+
i = block.close + 1;
|
|
16805
|
+
} else {
|
|
16806
|
+
i++;
|
|
16586
16807
|
}
|
|
16587
|
-
|
|
16588
|
-
|
|
16808
|
+
}
|
|
16809
|
+
return void 0;
|
|
16810
|
+
}
|
|
16811
|
+
function usesVariable(inner) {
|
|
16812
|
+
const code = createCodeScanner(inner);
|
|
16813
|
+
let masked = "";
|
|
16589
16814
|
let i = 0;
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
|
|
16593
|
-
|
|
16594
|
-
|
|
16595
|
-
|
|
16596
|
-
while (text[k] === "\\") k++;
|
|
16597
|
-
if (text[k] === "{" || text[k] === "}") next = (k - i) % 2 === 1 ? k + 1 : GIVE_UP;
|
|
16598
|
-
else next = k;
|
|
16599
|
-
} else if (c3 === "[" && text[i + 1] === "[") {
|
|
16600
|
-
next = linkEnd(text, i);
|
|
16601
|
-
} else if (c3 === "{") {
|
|
16602
|
-
next = afterBrace(i);
|
|
16603
|
-
} else if (c3 === "<") {
|
|
16604
|
-
next = readTag(i);
|
|
16605
|
-
} else {
|
|
16606
|
-
next = i + 1;
|
|
16815
|
+
while (i < inner.length) {
|
|
16816
|
+
const literal2 = code.literalEnd(i);
|
|
16817
|
+
if (literal2 !== -1) {
|
|
16818
|
+
masked += " ".repeat(literal2 - i);
|
|
16819
|
+
i = literal2;
|
|
16820
|
+
continue;
|
|
16607
16821
|
}
|
|
16608
|
-
if (
|
|
16609
|
-
|
|
16610
|
-
|
|
16822
|
+
if (inner[i] === "{" && SIGILS.has(inner[i + 1])) {
|
|
16823
|
+
const close = code.closeBrace(i + 1);
|
|
16824
|
+
if (close !== -1) {
|
|
16825
|
+
masked += " ".repeat(close + 1 - i);
|
|
16826
|
+
i = close + 1;
|
|
16827
|
+
continue;
|
|
16828
|
+
}
|
|
16611
16829
|
}
|
|
16612
|
-
|
|
16830
|
+
masked += inner[i];
|
|
16831
|
+
i++;
|
|
16613
16832
|
}
|
|
16614
|
-
|
|
16615
|
-
if (limit === stoppedAt || limit === n) return { tags, stoppedAt, macros };
|
|
16616
|
-
return {
|
|
16617
|
-
tags: tags.filter((tag) => tag.start < limit),
|
|
16618
|
-
stoppedAt: limit,
|
|
16619
|
-
macros: macros.filter((at) => at < limit)
|
|
16620
|
-
};
|
|
16833
|
+
return VARIABLE_REFERENCE.test(masked);
|
|
16621
16834
|
}
|
|
16622
|
-
function
|
|
16623
|
-
const
|
|
16624
|
-
|
|
16625
|
-
|
|
16626
|
-
|
|
16627
|
-
|
|
16628
|
-
|
|
16629
|
-
|
|
16630
|
-
|
|
16631
|
-
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
}
|
|
16835
|
+
function conditionalExpression(source) {
|
|
16836
|
+
const match2 = IF_ELSE.exec(source);
|
|
16837
|
+
if (!match2) return null;
|
|
16838
|
+
const condition = match2[1].trim();
|
|
16839
|
+
const [whenTrue, whenFalse = ""] = [match2[2], match2[3]];
|
|
16840
|
+
if (!/^[$_@%]\w/.test(condition)) return null;
|
|
16841
|
+
if (/[`\r\n]/.test(condition) || /[\r\n]/.test(whenTrue + whenFalse)) return null;
|
|
16842
|
+
const code = createCodeScanner(condition);
|
|
16843
|
+
let operators = "";
|
|
16844
|
+
for (let i = 0; i < condition.length; ) {
|
|
16845
|
+
const literal2 = code.literalEnd(i);
|
|
16846
|
+
if (literal2 !== -1) {
|
|
16847
|
+
i = literal2;
|
|
16848
|
+
continue;
|
|
16637
16849
|
}
|
|
16850
|
+
operators += condition[i];
|
|
16851
|
+
i++;
|
|
16638
16852
|
}
|
|
16639
|
-
return
|
|
16853
|
+
if (/(?:<<|>>)=/.test(operators)) return null;
|
|
16854
|
+
const rest = operators.replace(/\?\?|\?\./g, "").replace(/[!=]==?|[<>]=/g, "");
|
|
16855
|
+
if (/[?=,;]/.test(rest)) return null;
|
|
16856
|
+
const quote = (s) => `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
16857
|
+
return `{${condition} ? ${quote(whenTrue)} : ${quote(whenFalse)}}`;
|
|
16640
16858
|
}
|
|
16641
|
-
function
|
|
16642
|
-
const
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16650
|
-
|
|
16651
|
-
|
|
16652
|
-
|
|
16653
|
-
j++;
|
|
16859
|
+
function printExpression(source) {
|
|
16860
|
+
const match2 = /^\{print\s+([^{}\r\n]*)\}$/.exec(source);
|
|
16861
|
+
if (!match2) return null;
|
|
16862
|
+
const expr = match2[1].trim();
|
|
16863
|
+
if (!/^[$_@%]\w/.test(expr)) return null;
|
|
16864
|
+
const code = createCodeScanner(expr);
|
|
16865
|
+
let depth = 0;
|
|
16866
|
+
for (let i = 0; i < expr.length; ) {
|
|
16867
|
+
const literal2 = code.literalEnd(i);
|
|
16868
|
+
if (literal2 !== -1) {
|
|
16869
|
+
i = literal2;
|
|
16870
|
+
continue;
|
|
16654
16871
|
}
|
|
16872
|
+
const c3 = expr[i];
|
|
16873
|
+
if (c3 === "(" || c3 === "[") depth++;
|
|
16874
|
+
else if (c3 === ")" || c3 === "]") depth--;
|
|
16875
|
+
else if (c3 === ";" || c3 === "," && depth <= 0) return null;
|
|
16876
|
+
i++;
|
|
16655
16877
|
}
|
|
16656
|
-
return
|
|
16878
|
+
return SIMPLE_PATH.test(expr) && expr.includes(".") ? `{${expr} ?? ''}` : `{${expr}}`;
|
|
16657
16879
|
}
|
|
16658
|
-
var
|
|
16659
|
-
var
|
|
16660
|
-
"src/core/parsing/
|
|
16880
|
+
var SIGILS, MACRO_HEAD, VARIABLE_REFERENCE, IF_ELSE, SIMPLE_PATH;
|
|
16881
|
+
var init_attribute_blocks = __esm({
|
|
16882
|
+
"src/core/parsing/attribute-blocks.ts"() {
|
|
16661
16883
|
"use strict";
|
|
16884
|
+
init_code_scanner();
|
|
16662
16885
|
init_macro_parser();
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
"embed",
|
|
16669
|
-
"hr",
|
|
16670
|
-
"img",
|
|
16671
|
-
"input",
|
|
16672
|
-
"link",
|
|
16673
|
-
"meta",
|
|
16674
|
-
"param",
|
|
16675
|
-
"source",
|
|
16676
|
-
"track",
|
|
16677
|
-
"wbr"
|
|
16678
|
-
]);
|
|
16679
|
-
TAG_NAME_CHAR = /[a-zA-Z0-9-]/;
|
|
16680
|
-
LETTER = /[a-zA-Z]/;
|
|
16681
|
-
ATTR_NAME_CHAR = /[a-zA-Z0-9_\-:@]/;
|
|
16682
|
-
WHITESPACE = /[^\S ]/;
|
|
16683
|
-
SELECTOR_NAME_CHAR = /[a-zA-Z0-9_-]/;
|
|
16684
|
-
DISPLAY_NAME_CHAR = /[\w.]/;
|
|
16685
|
-
GIVE_UP = -2;
|
|
16886
|
+
SIGILS = /* @__PURE__ */ new Set(["$", "_", "@", "%"]);
|
|
16887
|
+
MACRO_HEAD = new RegExp(String.raw`^(?:(\/)|${SELECTOR_PATTERN} )?([A-Za-z][\w-]*)(?=\s|$|\{[$_@%])`);
|
|
16888
|
+
VARIABLE_REFERENCE = /(?:^|[^\w$.@%])[$_@%][A-Za-z_$]/;
|
|
16889
|
+
IF_ELSE = /^\{if\s+([^{}]*)\}([^{}]*?)(?:\{else\s*\}([^{}]*))?\{\/if\s*\}$/;
|
|
16890
|
+
SIMPLE_PATH = /^[$_@%][\w.]+$/;
|
|
16686
16891
|
}
|
|
16687
16892
|
});
|
|
16688
16893
|
|
|
@@ -17544,6 +17749,10 @@ function computeDiagnostics(uri, workspace, options) {
|
|
|
17544
17749
|
diagnostics.push(...replay.errors);
|
|
17545
17750
|
} catch {
|
|
17546
17751
|
}
|
|
17752
|
+
try {
|
|
17753
|
+
validateAttributeBlocks(markupText, passages, workspace, diagnostics);
|
|
17754
|
+
} catch {
|
|
17755
|
+
}
|
|
17547
17756
|
try {
|
|
17548
17757
|
validateMacros(macros, elements, workspace, diagnostics);
|
|
17549
17758
|
} catch {
|
|
@@ -17771,6 +17980,40 @@ function replayPassage(macros, from, to, scan, base, lineStarts, workspace, repl
|
|
|
17771
17980
|
if ("element" in entry) report2(tagRange(entry.element), `unclosed <${entry.element.name}>`);
|
|
17772
17981
|
}
|
|
17773
17982
|
}
|
|
17983
|
+
function validateAttributeBlocks(text, passages, workspace, diagnostics) {
|
|
17984
|
+
if (!text.includes("<")) return;
|
|
17985
|
+
const lineStarts = buildLineStarts(text);
|
|
17986
|
+
const lineOffset = (line) => lineStarts[line] ?? text.length;
|
|
17987
|
+
const lookup = {
|
|
17988
|
+
isMacro: (name) => workspace.macros.getMacro(name) !== void 0 || workspace.widgets.getWidget(name) !== void 0,
|
|
17989
|
+
isContainer: (name) => workspace.isContainer(name)
|
|
17990
|
+
};
|
|
17991
|
+
for (const passage of passages) {
|
|
17992
|
+
if (!isMarkupPassage(passage)) continue;
|
|
17993
|
+
const base = lineOffset(passage.range.start.line + 1);
|
|
17994
|
+
const content = text.slice(base, lineOffset(passage.range.end.line + 1));
|
|
17995
|
+
if (!content.includes("<") || !content.includes("{")) continue;
|
|
17996
|
+
let rawText;
|
|
17997
|
+
for (const tag of scanHtmlTags(content).tags) {
|
|
17998
|
+
const name = tag.name.toLowerCase();
|
|
17999
|
+
if (rawText !== void 0) {
|
|
18000
|
+
if (tag.kind === "close" && name === rawText) rawText = void 0;
|
|
18001
|
+
continue;
|
|
18002
|
+
}
|
|
18003
|
+
for (const [start, end] of tag.values ?? []) {
|
|
18004
|
+
for (const block of findUnevaluatedBlocks(content.slice(start, end), lookup)) {
|
|
18005
|
+
const range2 = {
|
|
18006
|
+
start: offsetToPosition(base + start + block.start, lineStarts),
|
|
18007
|
+
end: offsetToPosition(base + start + block.end, lineStarts)
|
|
18008
|
+
};
|
|
18009
|
+
const message = block.kind === "macro" ? MACRO_IN_ATTRIBUTE(block.macro) : EXPRESSION_IN_ATTRIBUTE;
|
|
18010
|
+
diagnostics.push(makeDiag(range2, DiagnosticCode.UnevaluatedAttributeBlock, message));
|
|
18011
|
+
}
|
|
18012
|
+
}
|
|
18013
|
+
if (tag.kind === "open" && RAW_TEXT_ELEMENTS.has(name)) rawText = name;
|
|
18014
|
+
}
|
|
18015
|
+
}
|
|
18016
|
+
}
|
|
17774
18017
|
function isInsideParent(macros, index, parents, workspace) {
|
|
17775
18018
|
const parentSet = new Set(parents.map((p) => p.toLowerCase()));
|
|
17776
18019
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -18264,7 +18507,7 @@ function toLspDiagnostic(d) {
|
|
|
18264
18507
|
message: d.message
|
|
18265
18508
|
};
|
|
18266
18509
|
}
|
|
18267
|
-
var DIRECT_CHILD_MACROS, NON_MARKUP_PASSAGES, WILL_NOT_START, DEFERRED_CONTAINERS, EXPRESSION_BUILTINS2, diagnosticsPlugin;
|
|
18510
|
+
var DIRECT_CHILD_MACROS, NON_MARKUP_PASSAGES, RAW_TEXT_ELEMENTS, MACRO_IN_ATTRIBUTE, EXPRESSION_IN_ATTRIBUTE, WILL_NOT_START, DEFERRED_CONTAINERS, EXPRESSION_BUILTINS2, diagnosticsPlugin;
|
|
18268
18511
|
var init_diagnostics = __esm({
|
|
18269
18512
|
"src/plugins/diagnostics.ts"() {
|
|
18270
18513
|
"use strict";
|
|
@@ -18272,6 +18515,7 @@ var init_diagnostics = __esm({
|
|
|
18272
18515
|
init_macro_parser();
|
|
18273
18516
|
init_argument_lexer();
|
|
18274
18517
|
init_html_scanner();
|
|
18518
|
+
init_attribute_blocks();
|
|
18275
18519
|
init_widget_arguments();
|
|
18276
18520
|
init_parameter_validator();
|
|
18277
18521
|
init_link_parser();
|
|
@@ -18285,6 +18529,9 @@ var init_diagnostics = __esm({
|
|
|
18285
18529
|
"StoryTransients",
|
|
18286
18530
|
"SaveTitle"
|
|
18287
18531
|
]);
|
|
18532
|
+
RAW_TEXT_ELEMENTS = /* @__PURE__ */ new Set(["script", "style"]);
|
|
18533
|
+
MACRO_IN_ATTRIBUTE = (macro) => `Macros are not evaluated inside HTML attributes, so Spindle outputs this {${macro}} as text. Use an expression such as {$x ? 'a' : 'b'}`;
|
|
18534
|
+
EXPRESSION_IN_ATTRIBUTE = "Spindle evaluates {\u2026} in an HTML attribute only when $, _, @ or % follows the brace, so it outputs this block as text. Start the expression with a variable, as in {$x ? 'a' : 'b'}";
|
|
18288
18535
|
WILL_NOT_START = "Spindle will not start the story.";
|
|
18289
18536
|
DEFERRED_CONTAINERS = /* @__PURE__ */ new Set(["link", "button"]);
|
|
18290
18537
|
EXPRESSION_BUILTINS2 = /* @__PURE__ */ new Set([
|
|
@@ -60030,8 +60277,9 @@ function getCompletions(uri, position, triggerChar, workspace) {
|
|
|
60030
60277
|
const lines = text.split("\n");
|
|
60031
60278
|
if (position.line >= lines.length) return [];
|
|
60032
60279
|
const lineText = lines[position.line].substring(0, position.character);
|
|
60280
|
+
const inAttribute = () => inAttributeValue(text, (buildLineStarts(text)[position.line] ?? 0) + position.character - 1);
|
|
60033
60281
|
if (/\{\/[A-Za-z\w-]*$/.test(lineText)) {
|
|
60034
|
-
return getClosingMacroCompletions(uri, text, position, workspace);
|
|
60282
|
+
return inAttribute() ? [] : getClosingMacroCompletions(uri, text, position, workspace);
|
|
60035
60283
|
}
|
|
60036
60284
|
const transientDotPathMatch = /%([\w$]+)\.([A-Za-z_$][\w$]*)?$/.exec(lineText);
|
|
60037
60285
|
if (transientDotPathMatch) {
|
|
@@ -60057,7 +60305,7 @@ function getCompletions(uri, position, triggerChar, workspace) {
|
|
|
60057
60305
|
return getPassageNameCompletions(workspace);
|
|
60058
60306
|
}
|
|
60059
60307
|
if (/(?:^|[^\\])\{[A-Za-z\w-]*$/.test(lineText)) {
|
|
60060
|
-
return getMacroCompletions(workspace);
|
|
60308
|
+
return inAttribute() ? [] : getMacroCompletions(workspace);
|
|
60061
60309
|
}
|
|
60062
60310
|
return [];
|
|
60063
60311
|
}
|
|
@@ -60229,6 +60477,7 @@ var init_completions = __esm({
|
|
|
60229
60477
|
"src/plugins/completions.ts"() {
|
|
60230
60478
|
"use strict";
|
|
60231
60479
|
init_macro_parser();
|
|
60480
|
+
init_html_scanner();
|
|
60232
60481
|
completionsPlugin = {
|
|
60233
60482
|
id: "completions",
|
|
60234
60483
|
capabilities: {
|
|
@@ -60467,11 +60716,13 @@ function getHoverInfo(uri, position, workspace) {
|
|
|
60467
60716
|
const lines = text.split("\n");
|
|
60468
60717
|
if (position.line >= lines.length) return null;
|
|
60469
60718
|
const line = lines[position.line];
|
|
60470
|
-
const
|
|
60719
|
+
const offset = (buildLineStarts(text)[position.line] ?? 0) + position.character;
|
|
60720
|
+
const inAttribute = inAttributeValue(text, offset);
|
|
60721
|
+
const macroResult = inAttribute ? null : getMacroHover(line, position, workspace);
|
|
60471
60722
|
if (macroResult) return macroResult;
|
|
60472
60723
|
const varResult = getVariableHover(uri, line, position, workspace);
|
|
60473
60724
|
if (varResult) return varResult;
|
|
60474
|
-
const widgetResult = getWidgetHover(line, position, workspace);
|
|
60725
|
+
const widgetResult = inAttribute ? null : getWidgetHover(line, position, workspace);
|
|
60475
60726
|
if (widgetResult) return widgetResult;
|
|
60476
60727
|
return null;
|
|
60477
60728
|
}
|
|
@@ -60633,6 +60884,7 @@ var init_hover = __esm({
|
|
|
60633
60884
|
"use strict";
|
|
60634
60885
|
init_macro_parser();
|
|
60635
60886
|
init_references();
|
|
60887
|
+
init_html_scanner();
|
|
60636
60888
|
hoverPlugin = {
|
|
60637
60889
|
id: "hover",
|
|
60638
60890
|
capabilities: {
|
|
@@ -61457,6 +61709,11 @@ function computeCodeActions(uri, diagnostics, workspace, options = {}) {
|
|
|
61457
61709
|
if (action) actions.push(action);
|
|
61458
61710
|
break;
|
|
61459
61711
|
}
|
|
61712
|
+
case DiagnosticCode.UnevaluatedAttributeBlock: {
|
|
61713
|
+
const action = fixAttributeMacro(uri, diag, workspace);
|
|
61714
|
+
if (action) actions.push(action);
|
|
61715
|
+
break;
|
|
61716
|
+
}
|
|
61460
61717
|
}
|
|
61461
61718
|
}
|
|
61462
61719
|
return actions;
|
|
@@ -61699,6 +61956,21 @@ function fixNullVariableValue(uri, diag) {
|
|
|
61699
61956
|
}]
|
|
61700
61957
|
};
|
|
61701
61958
|
}
|
|
61959
|
+
function fixAttributeMacro(uri, diag, workspace) {
|
|
61960
|
+
const text = workspace.documents.getText(uri);
|
|
61961
|
+
if (text === void 0) return null;
|
|
61962
|
+
const lineStarts = buildLineStarts(text);
|
|
61963
|
+
const offset = (p) => (lineStarts[p.line] ?? text.length) + p.character;
|
|
61964
|
+
const source = text.slice(offset(diag.range.start), offset(diag.range.end));
|
|
61965
|
+
const expression = conditionalExpression(source) ?? printExpression(source);
|
|
61966
|
+
if (!expression) return null;
|
|
61967
|
+
return {
|
|
61968
|
+
title: `Rewrite as ${expression}`,
|
|
61969
|
+
kind: "quickfix",
|
|
61970
|
+
diagnosticCodes: [DiagnosticCode.UnevaluatedAttributeBlock],
|
|
61971
|
+
edits: [{ uri, range: diag.range, newText: expression }]
|
|
61972
|
+
};
|
|
61973
|
+
}
|
|
61702
61974
|
function toLspRange6(r) {
|
|
61703
61975
|
return {
|
|
61704
61976
|
start: { line: r.start.line, character: r.start.character },
|
|
@@ -61713,6 +61985,8 @@ var init_code_actions = __esm({
|
|
|
61713
61985
|
init_diagnostic_codes();
|
|
61714
61986
|
init_config_loader();
|
|
61715
61987
|
init_macro_sources();
|
|
61988
|
+
init_attribute_blocks();
|
|
61989
|
+
init_macro_parser();
|
|
61716
61990
|
DEFAULT_CONFIG_FILENAME = "spindle.config.yaml";
|
|
61717
61991
|
NEW_MACRO_ENTRY = { description: "" };
|
|
61718
61992
|
codeActionsPlugin = {
|
|
@@ -62346,7 +62620,7 @@ var init_server4 = __esm({
|
|
|
62346
62620
|
|
|
62347
62621
|
// src/bin.ts
|
|
62348
62622
|
var args = process.argv.slice(2);
|
|
62349
|
-
var VERSION = "0.
|
|
62623
|
+
var VERSION = "0.11.0";
|
|
62350
62624
|
var HELP = `spindle-lsp v${VERSION}
|
|
62351
62625
|
Language Server Protocol server for Spindle story format
|
|
62352
62626
|
|