Skip to content

Commit 26ec05c

Browse files
committed
fix(markdown): preserve escaped delimiters in code contexts
1 parent 2d48c9e commit 26ec05c

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

src/features/messages/components/Markdown.test.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,38 @@ describe("Markdown file-like href behavior", () => {
639639
expect(container.textContent).toContain("\\]");
640640
});
641641

642+
it("does not rewrite escaped \\(inline\\) delimiters", () => {
643+
const { container } = render(
644+
<Markdown
645+
value={["Literal: \\\\(x\\\\)", "Outside: \\(z^2\\)"].join("\n")}
646+
className="markdown"
647+
enableMathRendering
648+
/>,
649+
);
650+
651+
expect(container.querySelectorAll(".katex").length).toBe(1);
652+
expect(container.textContent).toContain("\\(x\\)");
653+
});
654+
655+
it("does not render math inside blockquote-indented code blocks", () => {
656+
const { container } = render(
657+
<Markdown
658+
value={[
659+
"> \\(x^2\\)",
660+
"> \\[x+y\\]",
661+
"",
662+
"Outside: \\(z^2\\)",
663+
].join("\n")}
664+
className="markdown"
665+
enableMathRendering
666+
/>,
667+
);
668+
669+
expect(container.querySelectorAll(".katex").length).toBe(1);
670+
expect(container.textContent).toContain("\\(x^2\\)");
671+
expect(container.textContent).toContain("\\[x+y\\]");
672+
});
673+
642674
it("keeps math-like delimiters literal inside long fences with nested shorter fences", () => {
643675
const { container } = render(
644676
<Markdown

src/features/messages/components/Markdown.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,16 @@ function replaceInlineLatexMathDelimiters(value: string) {
455455
if (!trimmed) {
456456
return match;
457457
}
458+
let precedingBackslashes = 0;
459+
for (let index = offset - 1; index >= 0; index -= 1) {
460+
if (source[index] !== "\\") {
461+
break;
462+
}
463+
precedingBackslashes += 1;
464+
}
465+
if (precedingBackslashes % 2 === 1) {
466+
return match;
467+
}
458468
const before = offset > 0 ? source[offset - 1] : "";
459469
const afterIndex = offset + match.length;
460470
const after = afterIndex < source.length ? source[afterIndex] : "";
@@ -527,7 +537,15 @@ function replaceBlockLatexMathDelimiters(value: string) {
527537
}
528538

529539
function isIndentedCodeLine(line: string) {
530-
return /^(?: {4}|\t)/.test(line);
540+
let remaining = line;
541+
while (true) {
542+
const quotePrefixMatch = remaining.match(/^[ \t]{0,3}>[ \t]?/);
543+
if (!quotePrefixMatch) {
544+
break;
545+
}
546+
remaining = remaining.slice(quotePrefixMatch[0].length);
547+
}
548+
return /^(?: {4}|\t)/.test(remaining);
531549
}
532550

533551
function normalizeLatexMathDelimitersInChunk(value: string) {

0 commit comments

Comments
 (0)