From eb0e0fec00e08c497a428d138c47a1235c89295f Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 18 Aug 2024 12:22:23 +0200 Subject: [PATCH] Split behaved building and StyleVec building (#4782) --- crates/typst/src/math/mod.rs | 2 +- crates/typst/src/realize/behaviour.rs | 144 ++++++++++++-------------- crates/typst/src/realize/mod.rs | 66 +++++++----- 3 files changed, 110 insertions(+), 102 deletions(-) diff --git a/crates/typst/src/math/mod.rs b/crates/typst/src/math/mod.rs index 966945ce2..6cf22edbc 100644 --- a/crates/typst/src/math/mod.rs +++ b/crates/typst/src/math/mod.rs @@ -246,7 +246,7 @@ impl LayoutMath for Content { self.sequence_recursive_for_each(&mut |child: &Content| { bb.push(child, StyleChain::default()); }); - for (child, _) in bb.finish().0.chain(&styles) { + for (child, _) in bb.finish() { child.layout_math(ctx, styles)?; } return Ok(()); diff --git a/crates/typst/src/realize/behaviour.rs b/crates/typst/src/realize/behaviour.rs index 945d2f2a9..588e221f4 100644 --- a/crates/typst/src/realize/behaviour.rs +++ b/crates/typst/src/realize/behaviour.rs @@ -5,7 +5,6 @@ use std::fmt::{Debug, Formatter}; use ecow::EcoVec; use crate::foundations::{Content, StyleChain, Styles}; -use crate::syntax::Span; /// How an element interacts with other elements in a stream. #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -129,39 +128,9 @@ impl<'a> BehavedBuilder<'a> { /// Return the built content (possibly styled with local styles) plus a /// trunk style chain and a span for the collection. - pub fn finish(mut self) -> (StyleVec, StyleChain<'a>, Span) { + pub fn finish(mut self) -> Vec<(&'a Content, StyleChain<'a>)> { self.trim_weak(); - - let span = self.determine_span(); - let (trunk, depth) = self.determine_style_trunk(); - - let mut elements = EcoVec::with_capacity(self.buf.len()); - let mut styles = EcoVec::<(Styles, usize)>::new(); - let mut last: Option<(StyleChain<'a>, usize)> = None; - - for (element, chain) in self.buf.into_iter() { - elements.push(element.clone()); - - if let Some((prev, run)) = &mut last { - if chain == *prev { - *run += 1; - } else { - styles.push((prev.suffix(depth), *run)); - last = Some((chain, 1)); - } - } else { - last = Some((chain, 1)); - } - } - - if let Some((last, run)) = last { - let skippable = styles.is_empty() && last == trunk; - if !skippable { - styles.push((last.suffix(depth), run)); - } - } - - (StyleVec { elements, styles }, trunk, span) + self.buf } /// Trim a possibly remaining weak item. @@ -176,50 +145,6 @@ impl<'a> BehavedBuilder<'a> { fn find_last_weak(&self) -> Option { self.buf.iter().rposition(|(c, _)| c.behaviour().is_weak()) } - - /// Determine a span for the built collection. - fn determine_span(&self) -> Span { - let mut span = Span::detached(); - for &(content, _) in &self.buf { - span = content.span(); - if !span.is_detached() { - break; - } - } - span - } - - /// Determine the shared trunk style chain. - fn determine_style_trunk(&self) -> (StyleChain<'a>, usize) { - // Determine shared style depth and first span. - let mut trunk = match self.buf.first() { - Some(&(_, chain)) => chain, - None => Default::default(), - }; - - let mut depth = trunk.links().count(); - for (_, mut chain) in &self.buf { - let len = chain.links().count(); - if len < depth { - for _ in 0..depth - len { - trunk.pop(); - } - depth = len; - } else if len > depth { - for _ in 0..len - depth { - chain.pop(); - } - } - - while depth > 0 && chain != trunk { - trunk.pop(); - chain.pop(); - depth -= 1; - } - } - - (trunk, depth) - } } impl<'a> Default for BehavedBuilder<'a> { @@ -247,6 +172,39 @@ impl StyleVec { Self { elements, styles: EcoVec::new() } } + /// Create a `StyleVec` from a list of content with style chains. + pub fn create<'a>(buf: &[(&Content, StyleChain<'a>)]) -> (Self, StyleChain<'a>) { + let (trunk, depth) = determine_style_trunk(buf); + + let mut elements = EcoVec::with_capacity(buf.len()); + let mut styles = EcoVec::<(Styles, usize)>::new(); + let mut last: Option<(StyleChain<'a>, usize)> = None; + + for &(element, chain) in buf { + elements.push(element.clone()); + + if let Some((prev, run)) = &mut last { + if chain == *prev { + *run += 1; + } else { + styles.push((prev.suffix(depth), *run)); + last = Some((chain, 1)); + } + } else { + last = Some((chain, 1)); + } + } + + if let Some((last, run)) = last { + let skippable = styles.is_empty() && last == trunk; + if !skippable { + styles.push((last.suffix(depth), run)); + } + } + + (StyleVec { elements, styles }, trunk) + } + /// Whether there are no elements. pub fn is_empty(&self) -> bool { self.elements.is_empty() @@ -321,3 +279,35 @@ impl Debug for StyleVec { .finish() } } + +/// Determine the shared trunk style chain. +fn determine_style_trunk<'a, T>(buf: &[(T, StyleChain<'a>)]) -> (StyleChain<'a>, usize) { + // Determine shared style depth and first span. + let mut trunk = match buf.first() { + Some(&(_, chain)) => chain, + None => Default::default(), + }; + + let mut depth = trunk.links().count(); + for (_, mut chain) in buf { + let len = chain.links().count(); + if len < depth { + for _ in 0..depth - len { + trunk.pop(); + } + depth = len; + } else if len > depth { + for _ in 0..len - depth { + chain.pop(); + } + } + + while depth > 0 && chain != trunk { + trunk.pop(); + chain.pop(); + depth -= 1; + } + } + + (trunk, depth) +} diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs index eb968605b..fa7019e0b 100644 --- a/crates/typst/src/realize/mod.rs +++ b/crates/typst/src/realize/mod.rs @@ -36,6 +36,9 @@ use crate::model::{ use crate::syntax::Span; use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem}; +/// A pair of content and a style chain that applies to it. +type Pair<'a> = (&'a Content, StyleChain<'a>); + /// Realize at the root-level. #[typst_macros::time(name = "realize root")] pub fn realize_root<'a>( @@ -351,7 +354,8 @@ impl<'a> DocBuilder<'a> { /// Turns this builder into the resulting page runs, along with /// its [style chain][StyleChain]. fn finish(self) -> (StyleVec, StyleChain<'a>, DocumentInfo) { - let (children, trunk, _) = self.pages.finish(); + let buf = self.pages.finish(); + let (children, trunk) = StyleVec::create(&buf); (children, trunk, self.info) } } @@ -442,7 +446,9 @@ impl<'a> FlowBuilder<'a> { /// Turns this builder into the resulting flow, along with /// its [style chain][StyleChain]. fn finish(self) -> (Packed, StyleChain<'a>) { - let (children, trunk, span) = self.0.finish(); + let buf = self.0.finish(); + let span = determine_span(&buf); + let (children, trunk) = StyleVec::create(&buf); (Packed::new(FlowElem::new(children)).spanned(span), trunk) } } @@ -482,7 +488,9 @@ impl<'a> ParBuilder<'a> { /// Turns this builder into the resulting paragraph, along with /// its [style chain][StyleChain]. fn finish(self) -> (Packed, StyleChain<'a>) { - let (children, trunk, span) = self.0.finish(); + let buf = self.0.finish(); + let span = determine_span(&buf); + let (children, trunk) = StyleVec::create(&buf); (Packed::new(ParElem::new(children)).spanned(span), trunk) } } @@ -491,11 +499,11 @@ impl<'a> ParBuilder<'a> { /// from list or enum items, spaces, and paragraph breaks. struct ListBuilder<'a> { /// The list items collected so far. - items: BehavedBuilder<'a>, + items: Vec>, + /// Trailing content for which it is unclear whether it is part of the list. + staged: Vec>, /// Whether the list contains no paragraph breaks. tight: bool, - /// Trailing content for which it is unclear whether it is part of the list. - staged: Vec<(&'a Content, StyleChain<'a>)>, } impl<'a> ListBuilder<'a> { @@ -518,11 +526,10 @@ impl<'a> ListBuilder<'a> { || content.is::()) && self .items - .items() - .next() - .map_or(true, |first| first.func() == content.func()) + .first() + .map_or(true, |(first, _)| first.func() == content.func()) { - self.items.push(content, styles); + self.items.push((content, styles)); self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::()); return true; } @@ -533,25 +540,27 @@ impl<'a> ListBuilder<'a> { /// Turns this builder into the resulting list, along with /// its [style chain][StyleChain]. fn finish(self) -> (Content, StyleChain<'a>) { - let (items, trunk, span) = self.items.finish(); - let mut items = items.into_iter().peekable(); - let (first, _) = items.peek().unwrap(); + let span = determine_span(&self.items); + let (children, trunk) = StyleVec::create(&self.items); + + let mut iter = children.into_iter().peekable(); + let (first, _) = iter.peek().unwrap(); let output = if first.is::() { - let children = items + let children = iter .map(|(item, local)| { item.into_packed::().unwrap().styled(local) }) .collect(); ListElem::new(children).with_tight(self.tight).pack().spanned(span) } else if first.is::() { - let children = items + let children = iter .map(|(item, local)| { item.into_packed::().unwrap().styled(local) }) .collect(); EnumElem::new(children).with_tight(self.tight).pack().spanned(span) } else if first.is::() { - let children = items + let children = iter .map(|(item, local)| { item.into_packed::().unwrap().styled(local) }) @@ -560,29 +569,26 @@ impl<'a> ListBuilder<'a> { } else { unreachable!() }; + (output, trunk) } } impl Default for ListBuilder<'_> { fn default() -> Self { - Self { - items: BehavedBuilder::default(), - tight: true, - staged: vec![], - } + Self { items: vec![], staged: vec![], tight: true } } } /// Builds a [citation group][CiteGroup] from citations. #[derive(Default)] struct CiteGroupBuilder<'a> { - /// The styles. - styles: StyleChain<'a>, /// The citations. items: Vec>, /// Trailing content for which it is unclear whether it is part of the list. - staged: Vec<(&'a Content, StyleChain<'a>)>, + staged: Vec>, + /// The styles. + styles: StyleChain<'a>, } impl<'a> CiteGroupBuilder<'a> { @@ -619,3 +625,15 @@ impl<'a> CiteGroupBuilder<'a> { (Packed::new(CiteGroup::new(self.items)).spanned(span), self.styles) } } + +/// Determine a span for the built collection. +fn determine_span(buf: &[(&Content, StyleChain)]) -> Span { + let mut span = Span::detached(); + for &(content, _) in buf { + span = content.span(); + if !span.is_detached() { + break; + } + } + span +}