mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-25 02:32:47 +00:00
fix(editor): comment JSX children with JSX comments
Cmd+/ on a JSX child line in a .jsx or .tsx file inserted `//`, which
JSX renders as text. JSX nodes now resolve to `{/* */}` block tokens,
while JavaScript inside a JSX escape keeps `//`.
This commit is contained in:
@@ -15,8 +15,51 @@ test.each([
|
||||
])("toggles the comment and restores the source in %s", (filename, doc, commented) => {
|
||||
const language = getLanguageForFile(filename);
|
||||
if (!language) throw new Error(`Missing language for ${filename}`);
|
||||
let state = EditorState.create({ doc, extensions: [language] });
|
||||
const target = {
|
||||
const target = editorTarget(EditorState.create({ doc, extensions: [language] }));
|
||||
expect(toggleComment(target)).toBe(true);
|
||||
expect(target.state.doc.toString()).toBe(commented);
|
||||
expect(toggleComment(target)).toBe(true);
|
||||
expect(target.state.doc.toString()).toBe(doc);
|
||||
});
|
||||
|
||||
const jsxView = [
|
||||
"const view = (",
|
||||
" <div>",
|
||||
" text",
|
||||
" {value}",
|
||||
" <span />",
|
||||
" {items.map(() => {",
|
||||
" const item = 1;",
|
||||
" })}",
|
||||
" </div>",
|
||||
");",
|
||||
].join("\n");
|
||||
|
||||
test.each([
|
||||
["view.jsx", "text", "{/* text */}"],
|
||||
["view.tsx", "text", "{/* text */}"],
|
||||
["view.tsx", "{value}", "{/* {value} */}"],
|
||||
["view.tsx", "<span />", "{/* <span /> */}"],
|
||||
["view.tsx", "const item = 1;", "// const item = 1;"],
|
||||
])("toggles %s line %s as %s", (filename, line, commented) => {
|
||||
const language = getLanguageForFile(filename);
|
||||
if (!language) throw new Error(`Missing language for ${filename}`);
|
||||
const target = editorTarget(
|
||||
EditorState.create({
|
||||
doc: jsxView,
|
||||
extensions: [language],
|
||||
selection: { anchor: jsxView.indexOf(line) },
|
||||
}),
|
||||
);
|
||||
expect(toggleComment(target)).toBe(true);
|
||||
expect(target.state.doc.toString()).toBe(jsxView.replace(line, commented));
|
||||
expect(toggleComment(target)).toBe(true);
|
||||
expect(target.state.doc.toString()).toBe(jsxView);
|
||||
});
|
||||
|
||||
function editorTarget(initial: EditorState) {
|
||||
let state = initial;
|
||||
return {
|
||||
get state() {
|
||||
return state;
|
||||
},
|
||||
@@ -24,11 +67,7 @@ test.each([
|
||||
state = transaction.state;
|
||||
},
|
||||
};
|
||||
expect(toggleComment(target)).toBe(true);
|
||||
expect(state.doc.toString()).toBe(commented);
|
||||
expect(toggleComment(target)).toBe(true);
|
||||
expect(state.doc.toString()).toBe(doc);
|
||||
});
|
||||
}
|
||||
|
||||
test("keeps JSON comment-free", () => {
|
||||
const language = getLanguageForFile("data.json");
|
||||
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
Language,
|
||||
languageDataProp,
|
||||
StreamLanguage,
|
||||
sublanguageProp,
|
||||
type Sublanguage,
|
||||
} from "@codemirror/language";
|
||||
import { dart } from "@codemirror/legacy-modes/mode/clike";
|
||||
import { swift } from "@codemirror/legacy-modes/mode/swift";
|
||||
@@ -33,15 +35,28 @@ const hashComments = { commentTokens: { line: "#" } };
|
||||
const cssComments = { commentTokens: { block: { open: "/*", close: "*/" } } };
|
||||
const markupComments = { commentTokens: { block: { open: "<!--", close: "-->" } } };
|
||||
|
||||
// JSX children are text, so a `//` there renders on the page instead of commenting the line out.
|
||||
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: "*/}" } } }),
|
||||
};
|
||||
|
||||
function language(
|
||||
parser: LRParser | MarkdownParser,
|
||||
languageData: Parameters<typeof defineLanguageFacet>[0] = {},
|
||||
sublanguages: Sublanguage[] = [],
|
||||
): Language {
|
||||
const data = defineLanguageFacet(languageData);
|
||||
return new Language(
|
||||
data,
|
||||
parser.configure({
|
||||
props: [languageDataProp.add((node) => (node.isTop ? data : undefined))],
|
||||
props: [
|
||||
languageDataProp.add((node) => (node.isTop ? data : undefined)),
|
||||
sublanguageProp.add((node) =>
|
||||
node.isTop && sublanguages.length ? sublanguages : undefined,
|
||||
),
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -49,9 +64,9 @@ function language(
|
||||
const languagesByExtension: Record<string, Language> = {
|
||||
// JavaScript/TypeScript
|
||||
js: language(jsParser, cStyleComments),
|
||||
jsx: language(jsParser.configure({ dialect: "jsx" }), cStyleComments),
|
||||
jsx: language(jsParser.configure({ dialect: "jsx" }), cStyleComments, [jsxComments]),
|
||||
ts: language(jsParser.configure({ dialect: "ts" }), cStyleComments),
|
||||
tsx: language(jsParser.configure({ dialect: "ts jsx" }), cStyleComments),
|
||||
tsx: language(jsParser.configure({ dialect: "ts jsx" }), cStyleComments, [jsxComments]),
|
||||
mjs: language(jsParser, cStyleComments),
|
||||
cjs: language(jsParser, cStyleComments),
|
||||
// C / C++ / Objective-C
|
||||
|
||||
Reference in New Issue
Block a user