Skip to content

fix(latex): do not re-escape a dollar that is already escaped - #15602

Merged
darkskygit merged 1 commit into
toeverything:canaryfrom
anandghegde:fix/currency-escape-odd-backslash
Sep 19, 2026
Merged

darkskygit merged 1 commit into
toeverything:canaryfrom
anandghegde:fix/currency-escape-odd-backslash

Conversation

@anandghegde

@anandghegde anandghegde commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Description

Split out of #15596 on review feedback — this is an independent, pre-existing bug in the currency pass, so it does not belong in that PR.

The currency pass escapes any $ followed by a digit without looking at what precedes it:

preprocessedContent = preprocessedContent.replace(/\$(?=\d)/g, '\\$');

When the dollar is already escaped, that turns an odd backslash run even:

input:  \$4        (an escaped dollar — the author wants a literal "$4")
output: \\$4       (a literal backslash, then an unescaped `$`)

MarkdownPreprocessorManager.process runs this before _markdownToAst, so remark-math sees that bare $ and can take it as an active delimiter — the opposite of what escaping it was for.

The fix

Escape only a dollar whose preceding backslash run is even, keeping the run itself:

preprocessedContent = preprocessedContent.replace(
  /(?<!\\)((?:\\\\)*)\$(?=\d)/g,
  '$1\\$'
);

(?<!\\) anchors the run to its start and ((?:\\\\)*) takes backslashes in pairs, so an odd run never matches. Behaviour that was already correct is untouched:

input before after
costs $5 today costs \$5 today unchanged
costs \\$4 today (even run — literal \ then a real $) costs \\\$4 today unchanged
costs \$4 today (odd run — already escaped) costs \\$4 today costs \$4 today

Relationship to #15596

Both touch this file, so whichever lands second will need a trivial rebase:

  • Both add export to preprocessLatex (the same one-word change).
  • They edit different statements — fix(latex): keep inline math that starts with a digit (#15588) #15596 changes the protection regex above, this one changes the currency regex below.
  • Test files are deliberately named differently (latex-preprocessor.unit.spec.ts there, latex-currency-escape.unit.spec.ts here) so they do not collide. Happy to fold them into one file once both are in, if you'd prefer that.

I verified this bug is not introduced by #15596: \$4 preprocesses to \\$4 identically with and without that branch.

Checklist

  • I have signed the AFFiNE Contributor License Agreement — required before merge; the license/cla check must be green (how it works)
  • The PR targets the canary branch and its title follows Conventional Commits
  • Tests are added or updated where it makes sense
  • yarn lint and yarn typecheck pass locally
  • If the PR code includes AI-generated edits, I have carefully reviewed it to ensure that the scope of the PR is consistent with what is claimed in the title and description, and that there are no redundant or invalid implementations.
  • I agree that maintainers may close the PR without discussion if they consider the code quality to be too low.

Notes on the checklist

  • CLA: not signed yet — the repository owner is signing it; the license/cla check will go green once that happens.

  • Tests: 7 cases in latex-currency-escape.unit.spec.ts. Reverting only the regex fails exactly the three already-escaped cases and leaves the four guards (plain price, bare amount, two prices, even run) green — they are regression guards, not props. Full blocksuite/affine/all suite: 230 passing.

  • Lint/format: oxlint --deny-warnings and oxfmt --check are both clean on the two changed files.

  • Typecheck: clean for these files. For transparency, a full yarn typecheck on my machine also reports pre-existing @prisma/client errors under packages/backend/server; those come from my installing with --mode=skip-build (so prisma generate never ran) and are unrelated to this change.

Summary by CodeRabbit

  • Bug Fixes

    • Improved LaTeX handling for currency values by escaping dollar signs only when required.
    • Preserved already-escaped dollar signs and backslash sequences correctly.
  • Tests

    • Added coverage for plain currency amounts, already-escaped dollar signs, and even backslash runs.

The currency pass escapes any `$` followed by a digit, without looking at
what precedes it. When the dollar is already escaped, that turns an odd
backslash run even: `\$4` becomes `\\$4`, which is a literal backslash
followed by an unescaped `$`. The preprocessor runs before the markdown is
parsed, so remark-math can then read that `$` as an active delimiter — the
opposite of what escaping it was meant to achieve.

Escape only a dollar whose preceding backslash run is even, preserving the
run itself. An odd run already means the dollar is escaped, so it is left
alone; `$4` and `\\$4` are unaffected.

`preprocessLatex` is exported so the rule can be tested directly.
@anandghegde
anandghegde requested a review from a team as a code owner September 13, 2026 17:32
@github-actions github-actions Bot added the test Related to test cases label Sep 13, 2026
@coderabbitai

coderabbitai Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: c320ebfd-650f-4de8-abd6-0f0d6bdb69c7

📥 Commits

Reviewing files that changed from the base of the PR and between 868acf8 and 6e036ea.

📒 Files selected for processing (2)
  • blocksuite/affine/all/src/__tests__/adapters/latex-currency-escape.unit.spec.ts
  • blocksuite/affine/blocks/latex/src/adapters/markdown/preprocessor.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The PR exports preprocessLatex, changes currency dollar escaping based on preceding backslash runs, and adds tests for plain, already-escaped, and even-run cases.

Changes

LaTeX currency escaping

Layer / File(s) Summary
Preprocessor export
blocksuite/affine/blocks/latex/src/adapters/markdown/preprocessor.ts
preprocessLatex is exported for direct testing.
Currency escaping and validation
blocksuite/affine/blocks/latex/src/adapters/markdown/preprocessor.ts, blocksuite/affine/all/src/__tests__/adapters/latex-currency-escape.unit.spec.ts
The escaping regex handles even and odd preceding backslash runs. Tests cover plain dollar amounts, already-escaped dollars, and even backslash runs.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: l-sun

Merge Risk: ⚪ Minimal · up to 6e036

The currency escaping update preserves already escaped amounts while continuing to escape unescaped currency values. No actionable merge risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preventing re-escaping of already escaped dollar signs in LaTeX preprocessing.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

* @returns The preprocessed content
*/
function preprocessLatex(content: string) {
export function preprocessLatex(content: string) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test can import this directly, no reason to export to global

Comment on lines +5 to +11
test.each([
['a plain price', 'costs $5 today', 'costs \\$5 today'],
['a bare amount', '$4', '\\$4'],
['two prices', 'costs $5 and $10', 'costs \\$5 and \\$10'],
])('escapes %s', (_, markdown, expected) => {
expect(preprocessLatex(markdown)).toBe(expected);
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing tests for the case fixed in this pr

@darkskygit
darkskygit merged commit 82f8ada into toeverything:canary Sep 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test Related to test cases

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants