fix(math): collapse over-escaped backslashes in inline equations

Some sources (e.g. the math-academy posts) over-escape their LaTeX, so
the content carries `\\ldots` / `\\cdots` instead of `\ldots` / `\cdots`.
JLaTeXMath reads the `\\` as a forced TeX line break — splitting every
inline equation across two lines — and renders the trailing command name
as the literal letters "ldots"/"cdots", so the ellipsis symbol is lost.

Collapse doubled backslashes (`\\cmd` -> `\cmd`) before rendering inline
math; display math is left untouched since `\\` can be a genuine line
break there. No-op when the content isn't over-escaped.
This commit is contained in:
Claude
2026-06-09 00:52:47 +00:00
parent b8ac919acd
commit be06b7335b
3 changed files with 35 additions and 2 deletions
@@ -69,6 +69,20 @@ object MathParser {
return first >= 0 && line.indexOf('$', first + 1) >= 0
}
/**
* Collapses doubled backslashes (`\\cmd` → `\cmd`).
*
* Some sources over-escape their LaTeX (every `\` written as `\\`). In inline
* math a `\\` is a TeX forced line break that is never intended, so `\\ldots`
* really means `\ldots`. Left as-is, a renderer like JLaTeXMath reads `\\` as
* a line break — splitting the equation across two lines — and the trailing
* command name (`ldots`, `cdots`) as literal letters, so the symbol is lost.
*
* No-op when the input isn't over-escaped. Only meant for inline math; display
* math (`$$…$$`) may use `\\` as a genuine line break in aligned/matrix forms.
*/
fun collapseDoubledBackslashes(latex: String): String = if (latex.contains("\\\\")) latex.replace("\\\\", "\\") else latex
/**
* Splits [line] on spaces into [Token.Word]s, with any whitespace-delimited
* math span surfaced as a [Token.Math].
@@ -122,6 +122,19 @@ class MathParserTest {
assertTrue(math(MathParser.split("empty $d$d here")).isEmpty())
}
@Test
fun collapsesOverEscapedBackslashes() {
// `\\ldots` (over-escaped) -> `\ldots`; otherwise JLaTeXMath sees a line
// break plus the literal letters "ldots".
assertEquals("A_1, ${bs}ldots, A_n", MathParser.collapseDoubledBackslashes("A_1, $bs$bs" + "ldots, A_n"))
assertEquals("x_1 = ${bs}cdots = x_n", MathParser.collapseDoubledBackslashes("x_1 = $bs$bs" + "cdots = x_n"))
}
@Test
fun singleBackslashLatexIsLeftAlone() {
assertEquals("${bs}frac{1}{2} + ${bs}sqrt{x}", MathParser.collapseDoubledBackslashes("${bs}frac{1}{2} + ${bs}sqrt{x}"))
}
@Test
fun gluedMathStaysPlainWord() {
// Math without a separating space is not whitespace-delimited, so it