fix(typst): support multi-line display math ($...$)
Build and Deploy Verso / deploy (push) Successful in 9m44s
Build and Deploy Verso / deploy (push) Successful in 9m44s
mathContentTokenizer now detects inline vs display math by scanning back to the opening '$': if @skip consumed whitespace between '$' and the content the tokenizer removes the newline stop (display math), otherwise it keeps it (inline math). contextual: true prevents the tokenizer from firing outside InlineMath entirely, avoiding the orange-body-text regression seen when this was previously attempted without the guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -252,19 +252,36 @@ export const lineCommentContentTokenizer = new ExternalTokenizer(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ── mathContentTokenizer ────────────────────────────────────────────────
|
// ── mathContentTokenizer ────────────────────────────────────────────────
|
||||||
// Emits MathContent — everything between the $...$ delimiters (no newlines).
|
// Emits MathContent — everything between the $...$ delimiters.
|
||||||
// External rather than a @tokens rule for the same reason as LineCommentContent:
|
//
|
||||||
// ![$\n]+ overlaps with spaces, '<', '@', and other literals in merged states.
|
// Typst distinguishes inline math ($x^2$) from display math ($ x^2 $):
|
||||||
|
// display math has whitespace between the opening '$' and the content.
|
||||||
|
// We detect this by scanning back to '$': if there is any whitespace
|
||||||
|
// between '$' and the current position (i.e. @skip consumed it), the
|
||||||
|
// tokenizer allows newlines so multi-line display math works. Inline math
|
||||||
|
// keeps the newline stop, preventing a lone '$' from consuming the rest of
|
||||||
|
// the document.
|
||||||
|
//
|
||||||
|
// contextual: true — only fires inside InlineMath after '$', never in
|
||||||
|
// body text. The '$' token appears nowhere else in the grammar so the
|
||||||
|
// post-'$' state does not merge with item* states.
|
||||||
export const mathContentTokenizer = new ExternalTokenizer(
|
export const mathContentTokenizer = new ExternalTokenizer(
|
||||||
(input, _stack) => {
|
(input, _stack) => {
|
||||||
|
// Scan back to the opening '$', detecting display vs inline math.
|
||||||
|
let back = -1
|
||||||
|
while (input.peek(back) === SPACE || input.peek(back) === TAB || input.peek(back) === NEWLINE) back--
|
||||||
|
if (input.peek(back) !== DOLLAR) return
|
||||||
|
const isDisplay = back < -1 // whitespace between '$' and current position
|
||||||
|
|
||||||
let hasContent = false
|
let hasContent = false
|
||||||
while (input.next !== -1 && input.next !== DOLLAR && input.next !== NEWLINE) {
|
while (input.next !== -1 && input.next !== DOLLAR) {
|
||||||
|
if (!isDisplay && input.next === NEWLINE) break
|
||||||
input.advance()
|
input.advance()
|
||||||
hasContent = true
|
hasContent = true
|
||||||
}
|
}
|
||||||
if (hasContent) input.acceptToken(MathContent)
|
if (hasContent) input.acceptToken(MathContent)
|
||||||
},
|
},
|
||||||
{ contextual: false }
|
{ contextual: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
// ── codeKeywordTokenizer ─────────────────────────────────────────────────
|
// ── codeKeywordTokenizer ─────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user