diff --git a/crates/typst/src/realize.rs b/crates/typst/src/realize.rs index 97c53ace8..f999bfd42 100644 --- a/crates/typst/src/realize.rs +++ b/crates/typst/src/realize.rs @@ -1115,13 +1115,22 @@ fn visit_regex_match<'a>( elems: &[Pair<'a>], m: RegexMatch<'a>, ) -> SourceResult<()> { - let matched = m.offset..m.offset + m.text.len(); + let match_range = m.offset..m.offset + m.text.len(); let piece = TextElem::packed(m.text); let context = Context::new(None, Some(m.styles)); let output = m.recipe.apply(s.engine, context.track(), piece)?; - let mut output = Some(output); let mut cursor = 0; + let mut output = Some(output); + let mut visit_unconsumed_match = |s: &mut State<'a, '_, '_, '_>| -> SourceResult<()> { + if let Some(output) = output.take() { + let revocation = Style::Revocation(m.id).into(); + let outer = s.arenas.bump.alloc(m.styles); + let chained = outer.chain(s.arenas.styles.alloc(revocation)); + visit(s, s.store(output), chained)?; + } + Ok(()) + }; for &(content, styles) in elems { // Just forward tags. @@ -1130,48 +1139,49 @@ fn visit_regex_match<'a>( continue; } - // Determine the range of the element. + // At this point, we can have a `TextElem`, `SpaceElem`, + // `LinebreakElem`, or `SmartQuoteElem`. We now determine the range of + // the element. let len = content.to_packed::().map_or(1, |elem| elem.text.len()); - let subrange = cursor..cursor + len; + let elem_range = cursor..cursor + len; // If the element starts before the start of match, visit it fully or // sliced. - if subrange.start < matched.start { - if subrange.end <= matched.start { + if elem_range.start < match_range.start { + if elem_range.end <= match_range.start { visit(s, content, styles)?; } else { let mut elem = content.to_packed::().unwrap().clone(); - elem.text = elem.text[..matched.start - subrange.start].into(); + elem.text = elem.text[..match_range.start - elem_range.start].into(); visit(s, s.store(elem.pack()), styles)?; } } - // When the match starts at or before this element ends, visit the - // match. - if matched.start <= subrange.end { - if let Some(output) = output.take() { - let revocation = Style::Revocation(m.id).into(); - let outer = s.arenas.bump.alloc(m.styles); - let chained = outer.chain(s.arenas.styles.alloc(revocation)); - visit(s, s.store(output), chained)?; - } + // When the match starts before this element ends, visit it. + if match_range.start < elem_range.end { + visit_unconsumed_match(s)?; } // If the element ends after the end of the match, visit if fully or // sliced. - if subrange.end > matched.end { - if subrange.start >= matched.end { + if elem_range.end > match_range.end { + if elem_range.start >= match_range.end { visit(s, content, styles)?; } else { let mut elem = content.to_packed::().unwrap().clone(); - elem.text = elem.text[matched.end - subrange.start..].into(); + elem.text = elem.text[match_range.end - elem_range.start..].into(); visit(s, s.store(elem.pack()), styles)?; } } - cursor = subrange.end; + cursor = elem_range.end; } + // If the match wasn't consumed yet, visit it. This shouldn't really happen + // in practice (we'd need to have an empty match at the end), but it's an + // extra fail-safe. + visit_unconsumed_match(s)?; + Ok(()) } diff --git a/tests/ref/issue-5014-show-text-tags.png b/tests/ref/issue-5014-show-text-tags.png new file mode 100644 index 000000000..4cefe9d3c Binary files /dev/null and b/tests/ref/issue-5014-show-text-tags.png differ diff --git a/tests/suite/styling/show-text.typ b/tests/suite/styling/show-text.typ index 66ae6580f..4e0316488 100644 --- a/tests/suite/styling/show-text.typ +++ b/tests/suite/styling/show-text.typ @@ -199,3 +199,12 @@ $a^2 + b^2 = c^2$ is Pythagoras' theorem. a \ #h(0pt, weak: true) b \ #h(0pt, weak: true) $x$ c $y$ + +--- issue-5014-show-text-tags --- +#{ + let c = counter("c") + show "b": context c.get().first() + [a] + c.step() + [bc] +}