@reteps/tree-sitter-htmlmustache 0.2.0 → 0.3.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/cli/out/main.js +269 -122
- package/package.json +1 -1
- package/src/scanner.c +1 -40
- package/tree-sitter-htmlmustache.wasm +0 -0
package/cli/out/main.js
CHANGED
|
@@ -1715,16 +1715,39 @@ function loadConfigFileForPath(filePath) {
|
|
|
1715
1715
|
}
|
|
1716
1716
|
}
|
|
1717
1717
|
|
|
1718
|
-
// lsp/server/src/
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1718
|
+
// lsp/server/src/nodeHelpers.ts
|
|
1719
|
+
var MUSTACHE_SECTION_TYPES = /* @__PURE__ */ new Set([
|
|
1720
|
+
"mustache_section",
|
|
1721
|
+
"mustache_inverted_section"
|
|
1722
|
+
]);
|
|
1723
|
+
var RAW_CONTENT_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
1724
|
+
"html_script_element",
|
|
1725
|
+
"html_style_element",
|
|
1726
|
+
"html_raw_element"
|
|
1727
|
+
]);
|
|
1728
|
+
var HTML_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
1729
|
+
"html_element",
|
|
1730
|
+
"html_script_element",
|
|
1731
|
+
"html_style_element",
|
|
1732
|
+
"html_raw_element"
|
|
1733
|
+
]);
|
|
1734
|
+
function isMustacheSection(node) {
|
|
1735
|
+
return MUSTACHE_SECTION_TYPES.has(node.type);
|
|
1724
1736
|
}
|
|
1725
|
-
function
|
|
1726
|
-
|
|
1727
|
-
|
|
1737
|
+
function isRawContentElement(node) {
|
|
1738
|
+
return RAW_CONTENT_ELEMENT_TYPES.has(node.type);
|
|
1739
|
+
}
|
|
1740
|
+
function isHtmlElementType(node) {
|
|
1741
|
+
return HTML_ELEMENT_TYPES.has(node.type);
|
|
1742
|
+
}
|
|
1743
|
+
function getTagName(node) {
|
|
1744
|
+
for (const child of node.children) {
|
|
1745
|
+
if (child.type === "html_start_tag" || child.type === "html_self_closing_tag") {
|
|
1746
|
+
const tagNameNode = child.children.find((c) => c.type === "html_tag_name");
|
|
1747
|
+
if (tagNameNode) return tagNameNode.text;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return null;
|
|
1728
1751
|
}
|
|
1729
1752
|
function getSectionName(node) {
|
|
1730
1753
|
const beginNode = node.children.find(
|
|
@@ -1734,6 +1757,18 @@ function getSectionName(node) {
|
|
|
1734
1757
|
const tagNameNode = beginNode.children.find((c) => c.type === "mustache_tag_name");
|
|
1735
1758
|
return tagNameNode?.text ?? null;
|
|
1736
1759
|
}
|
|
1760
|
+
function getErroneousEndTagName(node) {
|
|
1761
|
+
const nameNode = node.children.find((c) => c.type === "html_erroneous_end_tag_name");
|
|
1762
|
+
return nameNode?.text?.toLowerCase() ?? null;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
// lsp/server/src/htmlBalanceChecker.ts
|
|
1766
|
+
function getTagNameLower(element) {
|
|
1767
|
+
return getTagName(element)?.toLowerCase() ?? null;
|
|
1768
|
+
}
|
|
1769
|
+
function getErroneousEndTagNameLower(node) {
|
|
1770
|
+
return getErroneousEndTagName(node)?.toLowerCase() ?? null;
|
|
1771
|
+
}
|
|
1737
1772
|
function hasForcedEndTag(element) {
|
|
1738
1773
|
return element.children.some((c) => c.type === "html_forced_end_tag");
|
|
1739
1774
|
}
|
|
@@ -1750,7 +1785,7 @@ function extractFromNode(node) {
|
|
|
1750
1785
|
(c) => c.type !== "html_start_tag" && c.type !== "html_end_tag" && c.type !== "html_forced_end_tag"
|
|
1751
1786
|
);
|
|
1752
1787
|
if (hasForcedEndTag(node)) {
|
|
1753
|
-
const tagName =
|
|
1788
|
+
const tagName = getTagNameLower(node);
|
|
1754
1789
|
const items = [];
|
|
1755
1790
|
if (tagName) {
|
|
1756
1791
|
const startTag = node.children.find((c) => c.type === "html_start_tag");
|
|
@@ -1765,7 +1800,7 @@ function extractFromNode(node) {
|
|
|
1765
1800
|
return [];
|
|
1766
1801
|
}
|
|
1767
1802
|
if (node.type === "html_erroneous_end_tag") {
|
|
1768
|
-
const tagName =
|
|
1803
|
+
const tagName = getErroneousEndTagNameLower(node);
|
|
1769
1804
|
if (tagName) {
|
|
1770
1805
|
return [{ type: "close", tagName, node }];
|
|
1771
1806
|
}
|
|
@@ -1967,7 +2002,7 @@ function checkUnclosedTags(rootNode) {
|
|
|
1967
2002
|
const hasEndTag = node.children.some((c) => c.type === "html_end_tag");
|
|
1968
2003
|
const hasForcedEnd = node.children.some((c) => c.type === "html_forced_end_tag");
|
|
1969
2004
|
if (!hasEndTag && !hasForcedEnd) {
|
|
1970
|
-
const tagName =
|
|
2005
|
+
const tagName = getTagNameLower(node);
|
|
1971
2006
|
if (tagName && !VOID_ELEMENTS.has(tagName) && !OPTIONAL_END_TAG_ELEMENTS.has(tagName)) {
|
|
1972
2007
|
const startTag = node.children.find((c) => c.type === "html_start_tag");
|
|
1973
2008
|
errors.push({
|
|
@@ -2017,7 +2052,7 @@ function checkHtmlBalance(rootNode) {
|
|
|
2017
2052
|
function checkNestedSameNameSections(rootNode) {
|
|
2018
2053
|
const errors = [];
|
|
2019
2054
|
function visit(node, ancestors) {
|
|
2020
|
-
if (node
|
|
2055
|
+
if (isMustacheSection(node)) {
|
|
2021
2056
|
const name = getSectionName(node);
|
|
2022
2057
|
if (name) {
|
|
2023
2058
|
if (ancestors.has(name)) {
|
|
@@ -2077,7 +2112,7 @@ function checkConsecutiveSameNameSections(rootNode, sourceText) {
|
|
|
2077
2112
|
for (let i = 0; i < children.length - 1; i++) {
|
|
2078
2113
|
const current = children[i];
|
|
2079
2114
|
const next = children[i + 1];
|
|
2080
|
-
if (current
|
|
2115
|
+
if (!isMustacheSection(current) || current.type !== next.type) {
|
|
2081
2116
|
continue;
|
|
2082
2117
|
}
|
|
2083
2118
|
const currentName = getSectionName(current);
|
|
@@ -2113,8 +2148,152 @@ function checkConsecutiveSameNameSections(rootNode, sourceText) {
|
|
|
2113
2148
|
visit(rootNode);
|
|
2114
2149
|
return errors;
|
|
2115
2150
|
}
|
|
2151
|
+
var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
|
|
2152
|
+
"area",
|
|
2153
|
+
"base",
|
|
2154
|
+
"basefont",
|
|
2155
|
+
"bgsound",
|
|
2156
|
+
"br",
|
|
2157
|
+
"col",
|
|
2158
|
+
"command",
|
|
2159
|
+
"embed",
|
|
2160
|
+
"frame",
|
|
2161
|
+
"hr",
|
|
2162
|
+
"image",
|
|
2163
|
+
"img",
|
|
2164
|
+
"input",
|
|
2165
|
+
"isindex",
|
|
2166
|
+
"keygen",
|
|
2167
|
+
"link",
|
|
2168
|
+
"menuitem",
|
|
2169
|
+
"meta",
|
|
2170
|
+
"nextid",
|
|
2171
|
+
"param",
|
|
2172
|
+
"source",
|
|
2173
|
+
"track",
|
|
2174
|
+
"wbr"
|
|
2175
|
+
]);
|
|
2176
|
+
function checkSelfClosingNonVoidTags(rootNode) {
|
|
2177
|
+
const errors = [];
|
|
2178
|
+
function visit(node) {
|
|
2179
|
+
if (node.type === "html_self_closing_tag") {
|
|
2180
|
+
const tagNameNode = node.children.find((c) => c.type === "html_tag_name");
|
|
2181
|
+
const tagName = tagNameNode?.text.toLowerCase();
|
|
2182
|
+
if (tagName && !VOID_ELEMENTS2.has(tagName)) {
|
|
2183
|
+
errors.push({
|
|
2184
|
+
node,
|
|
2185
|
+
message: `Self-closing non-void element: <${tagNameNode.text}/>`,
|
|
2186
|
+
fix: [{
|
|
2187
|
+
startIndex: node.startIndex,
|
|
2188
|
+
endIndex: node.endIndex,
|
|
2189
|
+
newText: node.text.replace(/\s*\/>$/, ">") + `</${tagNameNode.text}>`
|
|
2190
|
+
}],
|
|
2191
|
+
fixDescription: "Replace self-closing syntax with explicit close tag"
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
for (const child of node.children) {
|
|
2197
|
+
visit(child);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
visit(rootNode);
|
|
2201
|
+
return errors;
|
|
2202
|
+
}
|
|
2203
|
+
function areMutuallyExclusive(a, b) {
|
|
2204
|
+
for (const ac of a) {
|
|
2205
|
+
for (const bc of b) {
|
|
2206
|
+
if (ac.name === bc.name && ac.inverted !== bc.inverted) {
|
|
2207
|
+
return true;
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
return false;
|
|
2212
|
+
}
|
|
2213
|
+
function formatConditionClause(a, b) {
|
|
2214
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2215
|
+
for (const c of [...a, ...b]) {
|
|
2216
|
+
if (!seen.has(c.name)) {
|
|
2217
|
+
seen.set(c.name, c.inverted);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
if (seen.size === 0) return "";
|
|
2221
|
+
const parts = [];
|
|
2222
|
+
for (const [name, inverted] of seen) {
|
|
2223
|
+
parts.push(`${name} is ${inverted ? "falsy" : "truthy"}`);
|
|
2224
|
+
}
|
|
2225
|
+
return ` (when ${parts.join(", ")})`;
|
|
2226
|
+
}
|
|
2227
|
+
function collectAttributes(node, conditions, out) {
|
|
2228
|
+
for (const child of node.children) {
|
|
2229
|
+
if (child.type === "html_attribute") {
|
|
2230
|
+
const nameNode = child.children.find((c) => c.type === "html_attribute_name");
|
|
2231
|
+
if (nameNode) {
|
|
2232
|
+
out.push({ nameNode, conditions: [...conditions] });
|
|
2233
|
+
}
|
|
2234
|
+
} else if (child.type === "mustache_attribute") {
|
|
2235
|
+
const section = child.children.find((c) => isMustacheSection(c));
|
|
2236
|
+
if (section) {
|
|
2237
|
+
const name = getSectionName(section);
|
|
2238
|
+
if (name) {
|
|
2239
|
+
const inverted = section.type === "mustache_inverted_section";
|
|
2240
|
+
collectAttributes(section, [...conditions, { name, inverted }], out);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
function checkDuplicateAttributes(rootNode) {
|
|
2247
|
+
const errors = [];
|
|
2248
|
+
function visit(node) {
|
|
2249
|
+
if (node.type === "html_start_tag" || node.type === "html_self_closing_tag") {
|
|
2250
|
+
const occurrences = [];
|
|
2251
|
+
collectAttributes(node, [], occurrences);
|
|
2252
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2253
|
+
for (const occ of occurrences) {
|
|
2254
|
+
const key = occ.nameNode.text.toLowerCase();
|
|
2255
|
+
let group2 = groups.get(key);
|
|
2256
|
+
if (!group2) {
|
|
2257
|
+
group2 = [];
|
|
2258
|
+
groups.set(key, group2);
|
|
2259
|
+
}
|
|
2260
|
+
group2.push(occ);
|
|
2261
|
+
}
|
|
2262
|
+
for (const [, group2] of groups) {
|
|
2263
|
+
if (group2.length < 2) continue;
|
|
2264
|
+
for (let i = 1; i < group2.length; i++) {
|
|
2265
|
+
let conflictIdx = -1;
|
|
2266
|
+
for (let j = 0; j < i; j++) {
|
|
2267
|
+
if (!areMutuallyExclusive(group2[i].conditions, group2[j].conditions)) {
|
|
2268
|
+
conflictIdx = j;
|
|
2269
|
+
break;
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
if (conflictIdx >= 0) {
|
|
2273
|
+
const clause = formatConditionClause(group2[conflictIdx].conditions, group2[i].conditions);
|
|
2274
|
+
errors.push({
|
|
2275
|
+
node: group2[i].nameNode,
|
|
2276
|
+
message: `Duplicate attribute "${group2[i].nameNode.text}"${clause}`
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
for (const child of node.children) {
|
|
2284
|
+
visit(child);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
visit(rootNode);
|
|
2288
|
+
return errors;
|
|
2289
|
+
}
|
|
2116
2290
|
|
|
2117
|
-
//
|
|
2291
|
+
// lsp/server/src/collectErrors.ts
|
|
2292
|
+
var ERROR_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
2293
|
+
"ERROR",
|
|
2294
|
+
"mustache_erroneous_section_end",
|
|
2295
|
+
"mustache_erroneous_inverted_section_end"
|
|
2296
|
+
]);
|
|
2118
2297
|
function errorMessageForNode(nodeType, node) {
|
|
2119
2298
|
if (nodeType === "mustache_erroneous_section_end" || nodeType === "mustache_erroneous_inverted_section_end") {
|
|
2120
2299
|
const tagNameNode = node.children.find((c) => c.type === "mustache_erroneous_tag_name");
|
|
@@ -2125,12 +2304,7 @@ function errorMessageForNode(nodeType, node) {
|
|
|
2125
2304
|
}
|
|
2126
2305
|
return `Missing ${nodeType}`;
|
|
2127
2306
|
}
|
|
2128
|
-
|
|
2129
|
-
"ERROR",
|
|
2130
|
-
"mustache_erroneous_section_end",
|
|
2131
|
-
"mustache_erroneous_inverted_section_end"
|
|
2132
|
-
]);
|
|
2133
|
-
function collectErrors(tree, file) {
|
|
2307
|
+
function collectErrors(tree) {
|
|
2134
2308
|
const errors = [];
|
|
2135
2309
|
const cursor = tree.walk();
|
|
2136
2310
|
function visit() {
|
|
@@ -2138,13 +2312,8 @@ function collectErrors(tree, file) {
|
|
|
2138
2312
|
const nodeType = cursor.nodeType;
|
|
2139
2313
|
if (ERROR_NODE_TYPES.has(nodeType) || cursor.nodeIsMissing) {
|
|
2140
2314
|
errors.push({
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
column: node.startPosition.column + 1,
|
|
2144
|
-
endLine: node.endPosition.row + 1,
|
|
2145
|
-
endColumn: node.endPosition.column + 1,
|
|
2146
|
-
message: errorMessageForNode(nodeType, node),
|
|
2147
|
-
nodeText: node.text
|
|
2315
|
+
node,
|
|
2316
|
+
message: errorMessageForNode(nodeType, node)
|
|
2148
2317
|
});
|
|
2149
2318
|
if (nodeType === "ERROR") return;
|
|
2150
2319
|
}
|
|
@@ -2156,46 +2325,26 @@ function collectErrors(tree, file) {
|
|
|
2156
2325
|
}
|
|
2157
2326
|
}
|
|
2158
2327
|
visit();
|
|
2159
|
-
const
|
|
2160
|
-
const balanceErrors = checkHtmlBalance(rootNode);
|
|
2328
|
+
const balanceErrors = checkHtmlBalance(tree.rootNode);
|
|
2161
2329
|
for (const error of balanceErrors) {
|
|
2162
|
-
errors.push({
|
|
2163
|
-
file,
|
|
2164
|
-
line: error.node.startPosition.row + 1,
|
|
2165
|
-
column: error.node.startPosition.column + 1,
|
|
2166
|
-
endLine: error.node.endPosition.row + 1,
|
|
2167
|
-
endColumn: error.node.endPosition.column + 1,
|
|
2168
|
-
message: error.message,
|
|
2169
|
-
nodeText: error.node.text
|
|
2170
|
-
});
|
|
2330
|
+
errors.push({ node: error.node, message: error.message });
|
|
2171
2331
|
}
|
|
2172
|
-
const unclosedErrors = checkUnclosedTags(rootNode);
|
|
2332
|
+
const unclosedErrors = checkUnclosedTags(tree.rootNode);
|
|
2173
2333
|
for (const error of unclosedErrors) {
|
|
2174
|
-
errors.push({
|
|
2175
|
-
file,
|
|
2176
|
-
line: error.node.startPosition.row + 1,
|
|
2177
|
-
column: error.node.startPosition.column + 1,
|
|
2178
|
-
endLine: error.node.endPosition.row + 1,
|
|
2179
|
-
endColumn: error.node.endPosition.column + 1,
|
|
2180
|
-
message: error.message,
|
|
2181
|
-
nodeText: error.node.text
|
|
2182
|
-
});
|
|
2334
|
+
errors.push({ node: error.node, message: error.message });
|
|
2183
2335
|
}
|
|
2184
|
-
const sourceText = rootNode.text;
|
|
2336
|
+
const sourceText = tree.rootNode.text;
|
|
2185
2337
|
const mustacheChecks = [
|
|
2186
|
-
...checkNestedSameNameSections(rootNode),
|
|
2187
|
-
...checkUnquotedMustacheAttributes(rootNode),
|
|
2188
|
-
...checkConsecutiveSameNameSections(rootNode, sourceText)
|
|
2338
|
+
...checkNestedSameNameSections(tree.rootNode),
|
|
2339
|
+
...checkUnquotedMustacheAttributes(tree.rootNode),
|
|
2340
|
+
...checkConsecutiveSameNameSections(tree.rootNode, sourceText),
|
|
2341
|
+
...checkSelfClosingNonVoidTags(tree.rootNode),
|
|
2342
|
+
...checkDuplicateAttributes(tree.rootNode)
|
|
2189
2343
|
];
|
|
2190
2344
|
for (const error of mustacheChecks) {
|
|
2191
2345
|
errors.push({
|
|
2192
|
-
|
|
2193
|
-
line: error.node.startPosition.row + 1,
|
|
2194
|
-
column: error.node.startPosition.column + 1,
|
|
2195
|
-
endLine: error.node.endPosition.row + 1,
|
|
2196
|
-
endColumn: error.node.endPosition.column + 1,
|
|
2346
|
+
node: error.node,
|
|
2197
2347
|
message: error.message,
|
|
2198
|
-
nodeText: error.node.text,
|
|
2199
2348
|
severity: error.severity,
|
|
2200
2349
|
fix: error.fix,
|
|
2201
2350
|
fixDescription: error.fixDescription
|
|
@@ -2203,6 +2352,23 @@ function collectErrors(tree, file) {
|
|
|
2203
2352
|
}
|
|
2204
2353
|
return errors;
|
|
2205
2354
|
}
|
|
2355
|
+
|
|
2356
|
+
// cli/src/check.ts
|
|
2357
|
+
function collectErrors2(tree, file) {
|
|
2358
|
+
const errors = collectErrors(tree);
|
|
2359
|
+
return errors.map((error) => ({
|
|
2360
|
+
file,
|
|
2361
|
+
line: error.node.startPosition.row + 1,
|
|
2362
|
+
column: error.node.startPosition.column + 1,
|
|
2363
|
+
endLine: error.node.endPosition.row + 1,
|
|
2364
|
+
endColumn: error.node.endPosition.column + 1,
|
|
2365
|
+
message: error.message,
|
|
2366
|
+
nodeText: error.node.text,
|
|
2367
|
+
severity: error.severity,
|
|
2368
|
+
fix: error.fix,
|
|
2369
|
+
fixDescription: error.fixDescription
|
|
2370
|
+
}));
|
|
2371
|
+
}
|
|
2206
2372
|
function formatError(error, source) {
|
|
2207
2373
|
const lines = source.split("\n");
|
|
2208
2374
|
const errorLine = error.line - 1;
|
|
@@ -2377,7 +2543,7 @@ async function run(args) {
|
|
|
2377
2543
|
let source = import_node_fs.default.readFileSync(file, "utf-8");
|
|
2378
2544
|
if (fixMode) {
|
|
2379
2545
|
const tree2 = parseDocument(source);
|
|
2380
|
-
const errors2 =
|
|
2546
|
+
const errors2 = collectErrors2(tree2, displayPath);
|
|
2381
2547
|
const fixed = applyFixes(source, errors2);
|
|
2382
2548
|
if (fixed !== source) {
|
|
2383
2549
|
import_node_fs.default.writeFileSync(file, fixed, "utf-8");
|
|
@@ -2385,7 +2551,7 @@ async function run(args) {
|
|
|
2385
2551
|
}
|
|
2386
2552
|
}
|
|
2387
2553
|
const tree = parseDocument(source);
|
|
2388
|
-
const errors =
|
|
2554
|
+
const errors = collectErrors2(tree, displayPath);
|
|
2389
2555
|
const fileErrors = errors.filter((e) => e.severity !== "warning");
|
|
2390
2556
|
const fileWarnings = errors.filter((e) => e.severity === "warning");
|
|
2391
2557
|
if (errors.length > 0) {
|
|
@@ -2828,21 +2994,6 @@ function isLine(doc) {
|
|
|
2828
2994
|
}
|
|
2829
2995
|
|
|
2830
2996
|
// lsp/server/src/formatting/utils.ts
|
|
2831
|
-
function getTagName2(node) {
|
|
2832
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
2833
|
-
const child = node.child(i);
|
|
2834
|
-
if (!child) continue;
|
|
2835
|
-
if (child.type === "html_start_tag" || child.type === "html_self_closing_tag") {
|
|
2836
|
-
for (let j = 0; j < child.childCount; j++) {
|
|
2837
|
-
const tagChild = child.child(j);
|
|
2838
|
-
if (tagChild && tagChild.type === "html_tag_name") {
|
|
2839
|
-
return tagChild.text;
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
}
|
|
2844
|
-
return null;
|
|
2845
|
-
}
|
|
2846
2997
|
function normalizeText(text2) {
|
|
2847
2998
|
return text2.split("\n").map((line2) => line2.replace(/[ \t]+/g, " ").trim()).filter((line2, i, arr) => line2 || i > 0 && i < arr.length - 1).join("\n");
|
|
2848
2999
|
}
|
|
@@ -2919,10 +3070,7 @@ function getIgnoreDirective(node) {
|
|
|
2919
3070
|
}
|
|
2920
3071
|
|
|
2921
3072
|
// lsp/server/src/formatting/classifier.ts
|
|
2922
|
-
var
|
|
2923
|
-
function setCustomCodeTags(tags) {
|
|
2924
|
-
customCodeTags = new Set(tags.map((t) => t.toLowerCase()));
|
|
2925
|
-
}
|
|
3073
|
+
var EMPTY_SET = /* @__PURE__ */ new Set();
|
|
2926
3074
|
var CSS_DISPLAY_MAP = {
|
|
2927
3075
|
// Block elements
|
|
2928
3076
|
address: "block",
|
|
@@ -3013,10 +3161,10 @@ var PRESERVE_CONTENT_ELEMENTS = /* @__PURE__ */ new Set([
|
|
|
3013
3161
|
"script",
|
|
3014
3162
|
"style"
|
|
3015
3163
|
]);
|
|
3016
|
-
function getCSSDisplay(node) {
|
|
3164
|
+
function getCSSDisplay(node, customCodeTags = EMPTY_SET) {
|
|
3017
3165
|
const type = node.type;
|
|
3018
3166
|
if (type === "html_element") {
|
|
3019
|
-
const tagName =
|
|
3167
|
+
const tagName = getTagName(node);
|
|
3020
3168
|
if (tagName) {
|
|
3021
3169
|
if (customCodeTags.has(tagName.toLowerCase())) {
|
|
3022
3170
|
return "block";
|
|
@@ -3025,11 +3173,11 @@ function getCSSDisplay(node) {
|
|
|
3025
3173
|
}
|
|
3026
3174
|
return "block";
|
|
3027
3175
|
}
|
|
3028
|
-
if (
|
|
3176
|
+
if (isRawContentElement(node)) {
|
|
3029
3177
|
return "block";
|
|
3030
3178
|
}
|
|
3031
|
-
if (
|
|
3032
|
-
return hasBlockContent(node) ? "block" : "inline";
|
|
3179
|
+
if (isMustacheSection(node)) {
|
|
3180
|
+
return hasBlockContent(node, customCodeTags) ? "block" : "inline";
|
|
3033
3181
|
}
|
|
3034
3182
|
return "inline";
|
|
3035
3183
|
}
|
|
@@ -3052,55 +3200,55 @@ function isWhitespaceInsensitive(display) {
|
|
|
3052
3200
|
return false;
|
|
3053
3201
|
}
|
|
3054
3202
|
}
|
|
3055
|
-
function isBlockLevel(node) {
|
|
3203
|
+
function isBlockLevel(node, customCodeTags = EMPTY_SET) {
|
|
3056
3204
|
const type = node.type;
|
|
3057
|
-
if (
|
|
3058
|
-
return hasBlockContent(node);
|
|
3205
|
+
if (isMustacheSection(node)) {
|
|
3206
|
+
return hasBlockContent(node, customCodeTags);
|
|
3059
3207
|
}
|
|
3060
3208
|
if (type === "html_element") {
|
|
3061
|
-
const display = getCSSDisplay(node);
|
|
3209
|
+
const display = getCSSDisplay(node, customCodeTags);
|
|
3062
3210
|
return isWhitespaceInsensitive(display);
|
|
3063
3211
|
}
|
|
3064
|
-
if (
|
|
3212
|
+
if (isRawContentElement(node)) {
|
|
3065
3213
|
return true;
|
|
3066
3214
|
}
|
|
3067
3215
|
return false;
|
|
3068
3216
|
}
|
|
3069
|
-
function shouldPreserveContent(node) {
|
|
3217
|
+
function shouldPreserveContent(node, customCodeTags = EMPTY_SET) {
|
|
3070
3218
|
const type = node.type;
|
|
3071
|
-
if (
|
|
3219
|
+
if (isRawContentElement(node)) {
|
|
3072
3220
|
return true;
|
|
3073
3221
|
}
|
|
3074
3222
|
if (type === "html_element") {
|
|
3075
|
-
const tagName =
|
|
3223
|
+
const tagName = getTagName(node);
|
|
3076
3224
|
if (!tagName) return false;
|
|
3077
3225
|
const lower = tagName.toLowerCase();
|
|
3078
3226
|
return PRESERVE_CONTENT_ELEMENTS.has(lower) || customCodeTags.has(lower);
|
|
3079
3227
|
}
|
|
3080
3228
|
return false;
|
|
3081
3229
|
}
|
|
3082
|
-
function hasBlockContent(sectionNode) {
|
|
3230
|
+
function hasBlockContent(sectionNode, customCodeTags = EMPTY_SET) {
|
|
3083
3231
|
const contentNodes = getContentNodes(sectionNode);
|
|
3084
3232
|
if (hasImplicitEndTags(contentNodes)) {
|
|
3085
3233
|
return true;
|
|
3086
3234
|
}
|
|
3087
3235
|
for (const node of contentNodes) {
|
|
3088
|
-
if (isBlockLevelContent(node)) {
|
|
3236
|
+
if (isBlockLevelContent(node, customCodeTags)) {
|
|
3089
3237
|
return true;
|
|
3090
3238
|
}
|
|
3091
3239
|
}
|
|
3092
3240
|
return false;
|
|
3093
3241
|
}
|
|
3094
|
-
function isBlockLevelContent(node) {
|
|
3242
|
+
function isBlockLevelContent(node, customCodeTags = EMPTY_SET) {
|
|
3095
3243
|
const type = node.type;
|
|
3096
3244
|
if (type === "html_element") {
|
|
3097
3245
|
return true;
|
|
3098
3246
|
}
|
|
3099
|
-
if (
|
|
3247
|
+
if (isRawContentElement(node)) {
|
|
3100
3248
|
return true;
|
|
3101
3249
|
}
|
|
3102
|
-
if (
|
|
3103
|
-
return hasBlockContent(node);
|
|
3250
|
+
if (isMustacheSection(node)) {
|
|
3251
|
+
return hasBlockContent(node, customCodeTags);
|
|
3104
3252
|
}
|
|
3105
3253
|
return false;
|
|
3106
3254
|
}
|
|
@@ -3207,10 +3355,10 @@ function shouldHtmlElementStayInline(node, index, nodes) {
|
|
|
3207
3355
|
}
|
|
3208
3356
|
return false;
|
|
3209
3357
|
}
|
|
3210
|
-
function shouldTreatAsBlock(node, index, nodes) {
|
|
3211
|
-
const
|
|
3212
|
-
const
|
|
3213
|
-
return
|
|
3358
|
+
function shouldTreatAsBlock(node, index, nodes, customCodeTags = EMPTY_SET) {
|
|
3359
|
+
const isHtmlEl = isHtmlElementType(node);
|
|
3360
|
+
const isMustacheSec = isMustacheSection(node);
|
|
3361
|
+
return isHtmlEl && !shouldHtmlElementStayInline(node, index, nodes) || isMustacheSec && !isInTextFlow(node, index, nodes) || isBlockLevel(node, customCodeTags) && !isInTextFlow(node, index, nodes);
|
|
3214
3362
|
}
|
|
3215
3363
|
|
|
3216
3364
|
// lsp/server/src/customCodeTags.ts
|
|
@@ -3353,9 +3501,10 @@ function formatText(node) {
|
|
|
3353
3501
|
return text(normalizeText(node.text));
|
|
3354
3502
|
}
|
|
3355
3503
|
function formatHtmlElement(node, context) {
|
|
3356
|
-
const
|
|
3504
|
+
const tags = context.customCodeTags;
|
|
3505
|
+
const display = getCSSDisplay(node, tags);
|
|
3357
3506
|
const isBlock = isWhitespaceInsensitive(display);
|
|
3358
|
-
const preserveContent = shouldPreserveContent(node);
|
|
3507
|
+
const preserveContent = shouldPreserveContent(node, tags);
|
|
3359
3508
|
const selfClosing = node.childCount === 1 && node.child(0)?.type === "html_self_closing_tag";
|
|
3360
3509
|
if (selfClosing) {
|
|
3361
3510
|
const tag = node.child(0);
|
|
@@ -3384,7 +3533,7 @@ function formatHtmlElement(node, context) {
|
|
|
3384
3533
|
parts.push(formatStartTag(startTag, context));
|
|
3385
3534
|
}
|
|
3386
3535
|
const hasHtmlElementChildren = contentNodes.some(
|
|
3387
|
-
(child) => child.type === "html_element" || child
|
|
3536
|
+
(child) => child.type === "html_element" || isRawContentElement(child) || isBlockLevel(child, tags)
|
|
3388
3537
|
);
|
|
3389
3538
|
if (preserveContent) {
|
|
3390
3539
|
const tagNameLower = startTag ? getTagNameFromStartTag(startTag) : null;
|
|
@@ -3448,11 +3597,11 @@ function formatHtmlElement(node, context) {
|
|
|
3448
3597
|
const hasContent = hasDocContent(formattedContent);
|
|
3449
3598
|
if (hasContent) {
|
|
3450
3599
|
const hasBlockChildren = contentNodes.some((child, i) => {
|
|
3451
|
-
if (!shouldTreatAsBlock(child, i, contentNodes)) {
|
|
3600
|
+
if (!shouldTreatAsBlock(child, i, contentNodes, tags)) {
|
|
3452
3601
|
return false;
|
|
3453
3602
|
}
|
|
3454
|
-
const childDisplay = getCSSDisplay(child);
|
|
3455
|
-
return isWhitespaceInsensitive(childDisplay) || child
|
|
3603
|
+
const childDisplay = getCSSDisplay(child, tags);
|
|
3604
|
+
return isWhitespaceInsensitive(childDisplay) || isRawContentElement(child);
|
|
3456
3605
|
});
|
|
3457
3606
|
if (isBlock && !hasBlockChildren) {
|
|
3458
3607
|
const doc = group(
|
|
@@ -3603,11 +3752,11 @@ function formatMustacheSection(node, context) {
|
|
|
3603
3752
|
parts.push(hardline);
|
|
3604
3753
|
} else {
|
|
3605
3754
|
const hasBlockChildren = contentNodes.some((child, i) => {
|
|
3606
|
-
if (!shouldTreatAsBlock(child, i, contentNodes)) {
|
|
3755
|
+
if (!shouldTreatAsBlock(child, i, contentNodes, context.customCodeTags)) {
|
|
3607
3756
|
return false;
|
|
3608
3757
|
}
|
|
3609
|
-
const childDisplay = getCSSDisplay(child);
|
|
3610
|
-
return isWhitespaceInsensitive(childDisplay) || child
|
|
3758
|
+
const childDisplay = getCSSDisplay(child, context.customCodeTags);
|
|
3759
|
+
return isWhitespaceInsensitive(childDisplay) || isRawContentElement(child);
|
|
3611
3760
|
});
|
|
3612
3761
|
if (!hasBlockChildren) {
|
|
3613
3762
|
parts.push(indent(concat([softline, formattedContent])));
|
|
@@ -3824,10 +3973,10 @@ function formatBlockChildren(nodes, context) {
|
|
|
3824
3973
|
lastNodeEnd = node.endIndex;
|
|
3825
3974
|
continue;
|
|
3826
3975
|
}
|
|
3827
|
-
const treatAsBlock = shouldTreatAsBlock(node, i, nodes);
|
|
3976
|
+
const treatAsBlock = shouldTreatAsBlock(node, i, nodes, context.customCodeTags);
|
|
3828
3977
|
if (lastNodeEnd >= 0 && node.startIndex > lastNodeEnd) {
|
|
3829
3978
|
const prevNode = nodes[i - 1];
|
|
3830
|
-
const prevTreatAsBlock = shouldTreatAsBlock(prevNode, i - 1, nodes);
|
|
3979
|
+
const prevTreatAsBlock = shouldTreatAsBlock(prevNode, i - 1, nodes, context.customCodeTags);
|
|
3831
3980
|
if (!prevTreatAsBlock && !treatAsBlock) {
|
|
3832
3981
|
const gap = context.document.getText().slice(lastNodeEnd, node.startIndex);
|
|
3833
3982
|
if (/\s/.test(gap)) {
|
|
@@ -4055,19 +4204,17 @@ function createIndentUnit(options) {
|
|
|
4055
4204
|
|
|
4056
4205
|
// lsp/server/src/formatting/index.ts
|
|
4057
4206
|
function formatDocument2(tree, document, options, params = {}) {
|
|
4058
|
-
const { customCodeTags
|
|
4207
|
+
const { customCodeTags, printWidth = 80, embeddedFormatted, mustacheSpaces, customCodeTagConfigs, configFile } = params;
|
|
4059
4208
|
const mergedOptions = mergeOptions(options, document.uri, configFile);
|
|
4060
4209
|
const indentUnit = createIndentUnit(mergedOptions);
|
|
4061
|
-
if (customCodeTags2) {
|
|
4062
|
-
setCustomCodeTags(customCodeTags2);
|
|
4063
|
-
}
|
|
4064
4210
|
if (tree.rootNode.hasError) {
|
|
4065
4211
|
return [];
|
|
4066
4212
|
}
|
|
4067
4213
|
const configMap = buildConfigMap(customCodeTagConfigs);
|
|
4214
|
+
const customCodeTagSet = customCodeTags ? new Set(customCodeTags.map((t) => t.toLowerCase())) : void 0;
|
|
4068
4215
|
const context = {
|
|
4069
4216
|
document,
|
|
4070
|
-
customCodeTags:
|
|
4217
|
+
customCodeTags: customCodeTagSet,
|
|
4071
4218
|
customCodeTagConfigs: configMap,
|
|
4072
4219
|
embeddedFormatted,
|
|
4073
4220
|
mustacheSpaces
|
|
@@ -4166,7 +4313,7 @@ function resolveSettings(flags, filePath) {
|
|
|
4166
4313
|
let insertSpaces = true;
|
|
4167
4314
|
let printWidth = 80;
|
|
4168
4315
|
let mustacheSpaces = false;
|
|
4169
|
-
let
|
|
4316
|
+
let customCodeTags;
|
|
4170
4317
|
let customCodeTagConfigs;
|
|
4171
4318
|
const configFile = filePath ? loadConfigFileForPath(filePath) : null;
|
|
4172
4319
|
if (configFile) {
|
|
@@ -4175,7 +4322,7 @@ function resolveSettings(flags, filePath) {
|
|
|
4175
4322
|
if (configFile.mustacheSpaces !== void 0) mustacheSpaces = configFile.mustacheSpaces;
|
|
4176
4323
|
if (configFile.customCodeTags && configFile.customCodeTags.length > 0) {
|
|
4177
4324
|
const parsed = parseCustomCodeTagSettings(configFile.customCodeTags);
|
|
4178
|
-
|
|
4325
|
+
customCodeTags = parsed.tagNames;
|
|
4179
4326
|
customCodeTagConfigs = parsed.configs;
|
|
4180
4327
|
}
|
|
4181
4328
|
}
|
|
@@ -4192,7 +4339,7 @@ function resolveSettings(flags, filePath) {
|
|
|
4192
4339
|
options: { tabSize, insertSpaces },
|
|
4193
4340
|
printWidth,
|
|
4194
4341
|
mustacheSpaces,
|
|
4195
|
-
customCodeTags
|
|
4342
|
+
customCodeTags,
|
|
4196
4343
|
customCodeTagConfigs,
|
|
4197
4344
|
configFile
|
|
4198
4345
|
};
|
package/package.json
CHANGED
package/src/scanner.c
CHANGED
|
@@ -83,7 +83,7 @@ static unsigned serialize(Scanner *scanner, char *buffer) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
memcpy(&buffer[0], &serialized_tag_count, sizeof(serialized_tag_count));
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
// Mustache tags
|
|
88
88
|
uint16_t m_tag_count =
|
|
89
89
|
scanner->mustache_tags.size > UINT16_MAX ? UINT16_MAX : scanner->mustache_tags.size;
|
|
@@ -93,7 +93,6 @@ static unsigned serialize(Scanner *scanner, char *buffer) {
|
|
|
93
93
|
size += sizeof(m_serialized_tag_count);
|
|
94
94
|
memcpy(&buffer[size], &m_tag_count, sizeof(m_tag_count));
|
|
95
95
|
size += sizeof(m_tag_count);
|
|
96
|
-
// printf("[S] m_tag_count: %d\n", m_tag_count);
|
|
97
96
|
|
|
98
97
|
for (; m_serialized_tag_count < m_tag_count; m_serialized_tag_count++) {
|
|
99
98
|
MustacheTag tag = scanner->mustache_tags.contents[m_serialized_tag_count];
|
|
@@ -113,12 +112,10 @@ static unsigned serialize(Scanner *scanner, char *buffer) {
|
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
memcpy(&buffer[mustache_start_offset], &m_serialized_tag_count, sizeof(m_serialized_tag_count));
|
|
116
|
-
// printf("[S] m_serialized_tag_count: %d\n", m_serialized_tag_count);
|
|
117
115
|
return size;
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
static void deserialize(Scanner *scanner, const char *buffer, unsigned length) {
|
|
121
|
-
// printf("deserialize\n");
|
|
122
119
|
for (unsigned i = 0; i < scanner->tags.size; i++) {
|
|
123
120
|
tag_free(&scanner->tags.contents[i]);
|
|
124
121
|
}
|
|
@@ -195,22 +192,12 @@ static void deserialize(Scanner *scanner, const char *buffer, unsigned length) {
|
|
|
195
192
|
}
|
|
196
193
|
}
|
|
197
194
|
|
|
198
|
-
static void print_tag_name(String *tag_name) {
|
|
199
|
-
// printf("tag_size: %d\n", tag->tag_name.size);
|
|
200
|
-
for (uint32_t i = 0; i < tag_name->size; i++) {
|
|
201
|
-
printf("%c", tag_name->contents[i]);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
195
|
static String scan_html_tag_name(TSLexer *lexer) {
|
|
206
196
|
String tag_name = array_new();
|
|
207
197
|
while (iswalnum(lexer->lookahead) || lexer->lookahead == '-' || lexer->lookahead == ':') {
|
|
208
198
|
array_push(&tag_name, towupper(lexer->lookahead));
|
|
209
199
|
advance(lexer);
|
|
210
200
|
}
|
|
211
|
-
// printf("tag_name: ");
|
|
212
|
-
// print_tag_name(&tag_name);
|
|
213
|
-
// printf("\n");
|
|
214
201
|
return tag_name;
|
|
215
202
|
}
|
|
216
203
|
|
|
@@ -431,8 +418,6 @@ static String scan_mustache_tag_name(Scanner *scanner, TSLexer *lexer) {
|
|
|
431
418
|
array_push(&tag_name, lexer->lookahead);
|
|
432
419
|
advance(lexer);
|
|
433
420
|
}
|
|
434
|
-
// print_tag_name(&tag_name);
|
|
435
|
-
// printf("\n");
|
|
436
421
|
return tag_name;
|
|
437
422
|
}
|
|
438
423
|
|
|
@@ -445,17 +430,7 @@ static bool scan_mustache_start_tag_name(Scanner *scanner, TSLexer *lexer) {
|
|
|
445
430
|
MustacheTag tag = mustache_tag_new();
|
|
446
431
|
tag.tag_name = tag_name;
|
|
447
432
|
tag.html_tag_stack_size = scanner->tags.size;
|
|
448
|
-
// printf("pushing tag: ");
|
|
449
|
-
// print_tag_name(&tag.tag_name);
|
|
450
|
-
// printf("\n");
|
|
451
433
|
array_push(&scanner->mustache_tags, tag);
|
|
452
|
-
// printf("--------------------------------\n");
|
|
453
|
-
// for (unsigned i = 0; i < scanner->mustache_tags.size; i++) {
|
|
454
|
-
// printf("\tSTACK (START), tag_name: ");
|
|
455
|
-
// print_tag_name(&scanner->mustache_tags.contents[i]);
|
|
456
|
-
// printf("\n");
|
|
457
|
-
// }
|
|
458
|
-
// printf("--------------------------------\n");
|
|
459
434
|
lexer->result_symbol = MUSTACHE_START_TAG_NAME;
|
|
460
435
|
return true;
|
|
461
436
|
}
|
|
@@ -469,23 +444,11 @@ static bool scan_mustache_end_tag_name(Scanner *scanner, TSLexer *lexer) {
|
|
|
469
444
|
}
|
|
470
445
|
|
|
471
446
|
|
|
472
|
-
// Print whole stack
|
|
473
|
-
// printf("--------------------------------\n");
|
|
474
|
-
// printf("num tags: %d\n", scanner->mustache_tags.size);
|
|
475
|
-
// for (unsigned i = 0; i < scanner->mustache_tags.size; i++) {
|
|
476
|
-
// printf("\tSTACK (END), tag_name: ");
|
|
477
|
-
// print_tag_name(&scanner->mustache_tags.contents[i].tag_name);
|
|
478
|
-
// printf("\n");
|
|
479
|
-
// }
|
|
480
|
-
// printf("--------------------------------\n");
|
|
481
447
|
MustacheTag tag = mustache_tag_new();
|
|
482
448
|
tag.tag_name = tag_name;
|
|
483
449
|
if (scanner->mustache_tags.size > 0 && mustache_tag_eq(array_back(&scanner->mustache_tags), &tag)) {
|
|
484
450
|
MustacheTag popped_tag = array_pop(&scanner->mustache_tags);
|
|
485
451
|
mustache_tag_free(&popped_tag);
|
|
486
|
-
// printf("popped tag (correct): ");
|
|
487
|
-
// print_tag_name(&popped_tag.tag_name);
|
|
488
|
-
// printf("\n");
|
|
489
452
|
lexer->result_symbol = MUSTACHE_END_TAG_NAME;
|
|
490
453
|
} else {
|
|
491
454
|
if (scanner->mustache_tags.size > 0) {
|
|
@@ -501,7 +464,6 @@ static bool scan_mustache_end_tag_name(Scanner *scanner, TSLexer *lexer) {
|
|
|
501
464
|
|
|
502
465
|
static bool scan_mustache_end_tag_html_implicit_end_tag(Scanner *scanner, TSLexer *lexer) {
|
|
503
466
|
lexer->mark_end(lexer);
|
|
504
|
-
// printf("next char: %c\n", lexer->lookahead);
|
|
505
467
|
if (lexer->lookahead != '{') {
|
|
506
468
|
return false;
|
|
507
469
|
}
|
|
@@ -547,7 +509,6 @@ static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
|
|
|
547
509
|
|
|
548
510
|
|
|
549
511
|
if (valid_symbols[MUSTACHE_START_TAG_NAME]) {
|
|
550
|
-
// printf("MUSTACHE_START_TAG_NAME\n");
|
|
551
512
|
return scan_mustache_start_tag_name(scanner, lexer);
|
|
552
513
|
}
|
|
553
514
|
|
|
Binary file
|