From 94e8ff35030a9939267c26bc97ca99b54ad33ad0 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 7 Jun 2026 20:58:05 +0000 Subject: [PATCH] fix(typst): eliminate LALR(1) conflict on heading title items Removing Strong and Emphasis from headingTitleItem eliminates the conflict: both appear in document-level item, causing the LR automaton to merge heading-title states with document-item states and make "*" ambiguous (Strong opener vs. end of heading). HeadingText is widened to ![\n$#`<@\\]+ so "*" and "_" inside headings are consumed as plain text rather than producing error nodes. Co-Authored-By: Claude Sonnet 4.6 --- .../source-editor/lezer-typst/typst.grammar | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/services/web/frontend/js/features/source-editor/lezer-typst/typst.grammar b/services/web/frontend/js/features/source-editor/lezer-typst/typst.grammar index 4b0b52d027..ad485b1704 100644 --- a/services/web/frontend/js/features/source-editor/lezer-typst/typst.grammar +++ b/services/web/frontend/js/features/source-editor/lezer-typst/typst.grammar @@ -31,12 +31,15 @@ item { // HeadingMark is produced by an external tokenizer that enforces the // start-of-line constraint and captures the "=+" prefix + trailing space. Heading { HeadingMark HeadingTitle } -// headingTitleItem+ (not *) so HeadingTitle is never empty: the generator -// would otherwise produce a shift/reduce conflict on "*" after HeadingMark, -// unable to tell if "*" starts a Strong inside the heading or at doc level. +// Strong and Emphasis are intentionally excluded from headingTitleItem. +// Including them causes an LALR(1) conflict: since Strong/Emphasis also appear +// in document-level `item`, the LR automaton merges heading-title states with +// document-item states, making "*" ambiguous (Strong opener vs. end of heading). +// Instead, HeadingText is widened to consume "*" and "_" as plain text inside +// headings — they are not interpreted as markup delimiters there. HeadingTitle { headingTitleItem+ } headingTitleItem { - Strong | Emphasis | CodeExpr | InlineMath | RawInline | Label | Ref | HeadingText + CodeExpr | InlineMath | RawInline | Label | Ref | HeadingText } // ── Comments ────────────────────────────────────────────────────────────── @@ -186,7 +189,7 @@ Escape { "\\" EscapeChar } MathContent { ![$\n]+ } // Text tokens for different markup contexts; each excludes its own delimiters. - HeadingText { ![\n*_$#`<@\\]+ } + HeadingText { ![\n$#`<@\\]+ } StrongText { ![\n*$#`@\\]+ } EmphText { ![\n_$#`@\\]+ }