From f77692fefba4d66a0229309a92727859fd4076ef Mon Sep 17 00:00:00 2001
From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com>
Date: Mon, 5 Oct 2026 03:54:11 +0200
Subject: [PATCH] fix(editor): keep // on JSX attribute lines and use {/* */}
in MDX
Cmd+/ on an attribute line of a multi-line JSX tag wrapped it in {/* */},
which TypeScript rejects. Only lines that start a JSX child (text, the {
of a child escape, or the < of a nested element) take {/* */} now, and
attribute lines keep //.
.mdx files got , which MDX 2 and later fail to compile. They use
{/* */} now.
Co-authored-by: 3ae3ae <83870598+3ae3ae@users.noreply.github.com>
---
.../app/src/file-pane/editor/language.test.ts | 16 ++++++++++++-
packages/highlight/src/parsers.ts | 24 +++++++++++++++----
2 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/packages/app/src/file-pane/editor/language.test.ts b/packages/app/src/file-pane/editor/language.test.ts
index 1f1bb09fe6..4c45094c3c 100644
--- a/packages/app/src/file-pane/editor/language.test.ts
+++ b/packages/app/src/file-pane/editor/language.test.ts
@@ -9,6 +9,7 @@ test.each([
["source.css", "body {}", "/* body {} */"],
["source.html", "
",
+ "
go()}",
+ " >",
" text",
" {value}",
" ",
+ "{flag}",
+ "
",
+ " ",
" {items.map(() => {",
" const item = 1;",
" })}",
@@ -40,6 +49,11 @@ test.each([
["view.tsx", "text", "{/* text */}"],
["view.tsx", "{value}", "{/* {value} */}"],
["view.tsx", "", "{/* */}"],
+ ["view.tsx", "{flag}", "{/* {flag} */}"],
+ ["view.tsx", "
", "{/*
*/}"],
+ ["view.tsx", 'className="x"', '// className="x"'],
+ ["view.tsx", "onClick={() => go()}", "// onClick={() => go()}"],
+ ["view.tsx", 'a="1"', '// a="1"'],
["view.tsx", "const item = 1;", "// const item = 1;"],
])("toggles %s line %s as %s", (filename, line, commented) => {
const language = getLanguageForFile(filename);
diff --git a/packages/highlight/src/parsers.ts b/packages/highlight/src/parsers.ts
index 8b7fae7c59..8464978dd0 100644
--- a/packages/highlight/src/parsers.ts
+++ b/packages/highlight/src/parsers.ts
@@ -22,7 +22,7 @@ import { parser as rustParser } from "@lezer/rust";
import { parser as xmlParser } from "@lezer/xml";
import { parser as yamlParser } from "@lezer/yaml";
import { parser as elixirParser } from "lezer-elixir";
-import type { Parser } from "@lezer/common";
+import type { Parser, SyntaxNode } from "@lezer/common";
import type { LRParser } from "@lezer/lr";
import { csharpLanguage } from "./csharp/language.js";
import { astroParser } from "./astro/parser.js";
@@ -35,13 +35,27 @@ const hashComments = { commentTokens: { line: "#" } };
const cssComments = { commentTokens: { block: { open: "/*", close: "*/" } } };
const markupComments = { commentTokens: { block: { open: "" } } };
+const jsxChildComments = { commentTokens: { block: { open: "{/*", close: "*/}" } } };
+
// JSX children are text, so a `//` there renders on the page instead of commenting the line out.
+// Attribute lines of a multi-line tag are not children and keep `//`.
const jsxComments: Sublanguage = {
- // The `{` opening a JSX escape sits on a child line too, so judge it by its escape.
- test: (node) => (node.name === "{" && node.parent ? node.parent : node).name.startsWith("JSX"),
- facet: defineLanguageFacet({ commentTokens: { block: { open: "{/*", close: "*/}" } } }),
+ test: isJsxChild,
+ facet: defineLanguageFacet(jsxChildComments),
};
+// A line starts on a child as text, as the `{` of an escape, or as the `<` of a nested element.
+function isJsxChild(node: SyntaxNode): boolean {
+ return childAt(node)?.parent?.name === "JSXElement";
+}
+
+function childAt(node: SyntaxNode): SyntaxNode | null {
+ if (node.name === "JSXText") return node;
+ if (node.name === "{") return node.parent;
+ if (node.name === "JSXStartTag") return node.parent?.parent ?? null;
+ return null;
+}
+
function language(
parser: LRParser | MarkdownParser,
languageData: Parameters[0] = {},
@@ -122,7 +136,7 @@ const languagesByExtension: Record = {
exs: language(elixirParser, hashComments),
// Markdown
md: language(markdownParser, markupComments),
- mdx: language(markdownParser, markupComments),
+ mdx: language(markdownParser, jsxChildComments),
};
export function getLanguageForFile(filename: string): Language | null {