From ab9c360ebe50f848ef1ffac2e60bd45631c49e2b Mon Sep 17 00:00:00 2001 From: benmandrew Date: Tue, 20 Oct 2026 23:57:37 +0100 Subject: [PATCH] fix: keep a label on its added equation A label after a display equation is parsed as its own block and rendered after the equation's #diff-added wrapper, so it labels the wrapper. Any reference to it then fails with "cannot reference styled". Render the label inside the wrapper, next to the equation. --- src/render.rs | 32 +++++++++++++++++++++++++++++++- tests/integration.rs | 12 ++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/render.rs b/src/render.rs index 37a775a..329c12e 100644 --- a/src/render.rs +++ b/src/render.rs @@ -18,12 +18,29 @@ pub fn render(results: &[DiffResult]) -> String { out.push_str(PREAMBLE); out.push('\n'); - for result in results { + let mut results = results.iter().peekable(); + while let Some(result) = results.next() { match result { DiffResult::Unchanged(block) => { render_block(block, &mut out); out.push('\n'); } + // A label after the wrapper would label the styled wrapper, which + // cannot be referenced. Keep it inside, next to the equation. + DiffResult::Added(Block::Equation { content, .. }) + if results + .peek() + .is_some_and(|next| next_label(next).is_some()) => + { + let label = results.next().and_then(next_label).unwrap(); + write!( + out, + "#diff-added[{} {label}]", + escape_content(content, false) + ) + .unwrap(); + out.push('\n'); + } DiffResult::Added(block) => { render_added_or_deleted(block, DiffTag::Added, &mut out); out.push('\n'); @@ -95,6 +112,19 @@ fn is_label_only(text: &str) -> bool { TypstLabel::is_only(text) } +/// Returns the label if this result is a label that will be output bare. +fn next_label(result: &DiffResult) -> Option<&str> { + match result { + DiffResult::Unchanged(Block::Paragraph { source_text }) + | DiffResult::Added(Block::Paragraph { source_text }) + if is_label_only(source_text) => + { + Some(source_text.trim()) + } + _ => None, + } +} + /// Render a block wrapped in #diff-added[...] or #diff-deleted[...]. fn render_added_or_deleted(block: &Block, tag: DiffTag, out: &mut String) { let escape_refs = tag == DiffTag::Deleted; diff --git a/tests/integration.rs b/tests/integration.rs index dacad43..704eb27 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -209,3 +209,15 @@ fn test_indented_line_comment_does_not_split_paragraph() { // A whitespace-only line would be treated as a paragraph break by Typst. assert!(!output.contains("\n \n")); } + +#[test] +fn test_label_after_changed_equation_stays_on_equation() { + let old = "#set math.equation(numbering: \"(1)\")\n\n$ x = 1 $ \n\nSee @eq-main.\n"; + let new = "#set math.equation(numbering: \"(1)\")\n\n$ x = 2 $ \n\nSee @eq-main.\n"; + let output = run_diff(old, new); + + // Outside the wrapper the label would attach to the styled wrapper, and + // `@eq-main` would fail with "cannot reference styled". + assert!(output.contains("#diff-added[$ x = 2 $ ]")); + assert!(!output.contains("\n\n")); +}