summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-10-02 13:51:09 +0200
committerGitHub <noreply@github.com>2024-10-02 11:51:09 +0000
commitfcdccc9cbae02be0ac61f5f50f7f1a256fdd2b11 (patch)
tree671065febf3d6474c0d94f27a90c1fd927847414 /crates
parentb791aede82cc1424ba38b74200193096390ceecd (diff)
Fix textual grouping (#5097)
Diffstat (limited to 'crates')
-rw-r--r--crates/typst/src/realize.rs51
1 files changed, 26 insertions, 25 deletions
diff --git a/crates/typst/src/realize.rs b/crates/typst/src/realize.rs
index f999bfd4..7c199ff1 100644
--- a/crates/typst/src/realize.rs
+++ b/crates/typst/src/realize.rs
@@ -873,40 +873,41 @@ const fn list_like_grouping<T: ListLike>() -> GroupingRule {
/// as part of a paragraph grouping,
/// - if that's not possible because another grouping is active, temporarily
/// disables textual grouping and revisits the elements.
-fn finish_textual(Grouped { s, start }: Grouped) -> SourceResult<()> {
- // Try to find a regex match in the grouped textual elements.
+fn finish_textual(Grouped { s, mut start }: Grouped) -> SourceResult<()> {
+ // Try to find a regex match in the grouped textual elements. Returns early
+ // if there is one.
if visit_textual(s, start)? {
return Ok(());
}
- // No regex match.
- match s.groupings.last() {
- // Transparently apply the grouped content to an active paragraph. This
- // is more efficient than revisiting everything. Checking the priority
- // is a bit of a hack, but the simplest way to check which rule is
- // active for now.
- Some(grouping) if std::ptr::eq(grouping.rule, &PAR) => {}
-
- // Start a new paragraph based on this textual group.
- None => s.groupings.push(Grouping { rule: &PAR, start }),
-
- // If a non-paragraph grouping is top-level, revisit the grouped
- // content with the `TEXTUAL` rule disabled.
- _ => {
- let elems = s.store_slice(&s.sink[start..]);
- let rules = s.rules;
- s.sink.truncate(start);
- s.rules = &s.rules[1..];
- for &(content, styles) in &elems {
- visit(s, content, styles)?;
- }
- s.rules = rules;
- }
+ // There was no regex match, so we need to collect the text into a paragraph
+ // grouping. To do that, we first terminate all non-paragraph groupings.
+ if in_non_par_grouping(s) {
+ let elems = s.store_slice(&s.sink[start..]);
+ s.sink.truncate(start);
+ finish_grouping_while(s, in_non_par_grouping)?;
+ start = s.sink.len();
+ s.sink.extend(elems);
+ }
+
+ // Now, there are only two options:
+ // 1. We are already in a paragraph group. In this case, the elements just
+ // transparently become part of it.
+ // 2. There is no group at all. In this case, we create one.
+ if s.groupings.is_empty() {
+ s.groupings.push(Grouping { start, rule: &PAR });
}
Ok(())
}
+/// Whether there is an active grouping, but it is not a `PAR` grouping.
+fn in_non_par_grouping(s: &State) -> bool {
+ s.groupings
+ .last()
+ .is_some_and(|grouping| !std::ptr::eq(grouping.rule, &PAR))
+}
+
/// Builds the `ParElem` from inline-level elements.
fn finish_par(mut grouped: Grouped) -> SourceResult<()> {
// Collapse unsupported spaces in-place.