@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 CHANGED
@@ -1715,16 +1715,39 @@ function loadConfigFileForPath(filePath) {
1715
1715
  }
1716
1716
  }
1717
1717
 
1718
- // lsp/server/src/htmlBalanceChecker.ts
1719
- function getTagName(element) {
1720
- const startTag = element.children.find((c) => c.type === "html_start_tag");
1721
- if (!startTag) return null;
1722
- const tagNameNode = startTag.children.find((c) => c.type === "html_tag_name");
1723
- return tagNameNode?.text?.toLowerCase() ?? null;
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 getErroneousEndTagName(node) {
1726
- const nameNode = node.children.find((c) => c.type === "html_erroneous_end_tag_name");
1727
- return nameNode?.text?.toLowerCase() ?? null;
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 = getTagName(node);
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 = getErroneousEndTagName(node);
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 = getTagName(node);
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.type === "mustache_section" || node.type === "mustache_inverted_section") {
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.type !== "mustache_section" && current.type !== "mustache_inverted_section" || current.type !== next.type) {
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
- // cli/src/check.ts
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
- var ERROR_NODE_TYPES = /* @__PURE__ */ new Set([
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
- file,
2142
- line: node.startPosition.row + 1,
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 rootNode = tree.rootNode;
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
- file,
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 = collectErrors(tree2, displayPath);
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 = collectErrors(tree, displayPath);
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 customCodeTags = /* @__PURE__ */ new Set();
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 = getTagName2(node);
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 (type === "html_script_element" || type === "html_style_element" || type === "html_raw_element") {
3176
+ if (isRawContentElement(node)) {
3029
3177
  return "block";
3030
3178
  }
3031
- if (type === "mustache_section" || type === "mustache_inverted_section") {
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 (type === "mustache_section" || type === "mustache_inverted_section") {
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 (type === "html_script_element" || type === "html_style_element" || type === "html_raw_element") {
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 (type === "html_script_element" || type === "html_style_element" || type === "html_raw_element") {
3219
+ if (isRawContentElement(node)) {
3072
3220
  return true;
3073
3221
  }
3074
3222
  if (type === "html_element") {
3075
- const tagName = getTagName2(node);
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 (type === "html_script_element" || type === "html_style_element" || type === "html_raw_element") {
3247
+ if (isRawContentElement(node)) {
3100
3248
  return true;
3101
3249
  }
3102
- if (type === "mustache_section" || type === "mustache_inverted_section") {
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 isHtmlElement = node.type === "html_element" || node.type === "html_script_element" || node.type === "html_style_element" || node.type === "html_raw_element";
3212
- const isMustacheSection = node.type === "mustache_section" || node.type === "mustache_inverted_section";
3213
- return isHtmlElement && !shouldHtmlElementStayInline(node, index, nodes) || isMustacheSection && !isInTextFlow(node, index, nodes) || isBlockLevel(node) && !isInTextFlow(node, index, nodes);
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 display = getCSSDisplay(node);
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.type === "html_script_element" || child.type === "html_style_element" || child.type === "html_raw_element" || isBlockLevel(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.type === "html_script_element" || child.type === "html_style_element" || child.type === "html_raw_element";
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.type === "html_script_element" || child.type === "html_style_element" || child.type === "html_raw_element";
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: customCodeTags2, printWidth = 80, embeddedFormatted, mustacheSpaces, customCodeTagConfigs, configFile } = params;
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: customCodeTags2 ? new Set(customCodeTags2.map((t) => t.toLowerCase())) : void 0,
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 customCodeTags2;
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
- customCodeTags2 = parsed.tagNames;
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: customCodeTags2,
4342
+ customCodeTags,
4196
4343
  customCodeTagConfigs,
4197
4344
  configFile
4198
4345
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reteps/tree-sitter-htmlmustache",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "HTML with Mustache/Handlebars template syntax grammar for tree-sitter",
5
5
  "repository": {
6
6
  "type": "git",
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
- // printf("[S] serialized_tag_count: %d\n", serialized_tag_count);
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