mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-21 03:03:15 +08:00
Fixes #15588. ## Root cause `preprocessLatex` escapes any `$` that is followed by a digit, assuming it is a price: ```ts // Escape dollar signs that are likely currency indicators preprocessedContent = preprocessedContent.replace(/\$(?=\d)/g, '\\$'); ``` The step above it shields existing LaTeX from that escape — but only `$$…$$`, `\[…\]` and `\(…\)`. **Single-dollar inline math was never protected.** So for `$4\vee 6=12$`: 1. the protection pass does not match it, 2. the currency pass sees `$4` and rewrites it to `\$4`, 3. the expression never parses as math, and the closing `$` is left orphaned, which throws off the parse of everything after it. That last part is why a single such expression breaks the rest of the document, as the report describes. It also explains the reporter's `{ }` workaround: `${4}` is `$` followed by `{`, not a digit, so the escape never fires. ## The fix Protect single-dollar inline math too, using the usual rule for telling math from prices: - the opening `$` is not followed by whitespace, - the closing `$` is not preceded by whitespace, - the closing `$` is not followed by a digit. ```ts /(\$\$[\s\S]*?\$\$|\\\[[\s\S]*?\\\]|\\\(.*?\\\)|\$(?!\s)[^\n$]*?(?<!\s)\$(?!\d))/g ``` Currency is unaffected, which is the part worth checking: - `costs $5 and $10 today` — the only closing candidate is the `$` before `10`, and it is preceded by a space, so no match; both escape as before. - `$100$200` — the closing candidate is followed by `2`, so no match; both escape as before. - `it costs $5.00 total` — no closing `$` at all. `$$…$$` stays first in the alternation, so display math is still matched as display math. ## Verification New `blocksuite/affine/all/src/__tests__/adapters/latex-preprocessor.unit.spec.ts`, 11 cases: the digit-first expressions from the issue, the currency cases above, and guards for display math, a `$` followed by whitespace, `\(…\)` rewriting, and code spans. Reverting **only** the regex (keeping the export so the module still loads) fails exactly the four digit-first tests and leaves the other seven green: ``` × leaves binary operators untouched × leaves relation untouched × leaves single digit untouched × keeps every expression in a sentence mixing math and prose Tests 4 failed | 7 passed (11) ``` That the currency and existing-behaviour cases pass **without** the fix is the point — they are regression guards, not props for it. With the fix: ``` Test Files 14 passed (14) Tests 234 passed (234) ``` (the whole `blocksuite/affine/all` suite, which includes the 193 existing adapter tests). ## Notes - `preprocessLatex` is now exported so the delimiter rule can be tested directly. It was previously module-private with only the extension exported; `adapters/markdown/index.ts` already does `export * from './preprocessor.js'`, so nothing else changed. - I kept the change to the one regex and did not reformat the rest of the file — there is no Prettier config in the repo, and running Prettier with its defaults rewrites quotes and trailing commas across the whole file. - The lookbehind `(?<!\s)` is ES2018; the file already targets modern runtimes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved Markdown LaTeX preprocessing for inline expressions preceded by even-length backslash runs. - Continued to distinguish escaped dollar signs from valid math delimiters, while preserving currency values, display math, `\( \)` expressions, whitespace, mixed content, and code spans. - **Tests** - Added comprehensive coverage for inline and display math, delimiter escaping, currency values, whitespace, conversions, mixed content, digit-leading expressions, and code-span protection. <!-- end of auto-generated comment: release notes by coderabbit.ai -->