diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-02-12 13:29:52 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-12 12:29:52 +0000 |
| commit | 63b73ee98c75b26b7adb4b7517732e0d2bb7551c (patch) | |
| tree | 40cd368674694f9d46aee688270fd6209d92d3d7 | |
| parent | 36d588ae5d5dbb198160f9ad7931bb4c5d3069ac (diff) | |
Refactor behaved building (#3403)
| -rw-r--r-- | crates/typst/src/foundations/content.rs | 13 | ||||
| -rw-r--r-- | crates/typst/src/foundations/element.rs | 14 | ||||
| -rw-r--r-- | crates/typst/src/foundations/styles.rs | 206 | ||||
| -rw-r--r-- | crates/typst/src/layout/mod.rs | 11 | ||||
| -rw-r--r-- | crates/typst/src/layout/spacing.rs | 18 | ||||
| -rw-r--r-- | crates/typst/src/math/mod.rs | 7 | ||||
| -rw-r--r-- | crates/typst/src/realize/arenas.rs | 34 | ||||
| -rw-r--r-- | crates/typst/src/realize/behave.rs | 218 | ||||
| -rw-r--r-- | crates/typst/src/realize/mod.rs | 161 |
9 files changed, 284 insertions, 398 deletions
diff --git a/crates/typst/src/foundations/content.rs b/crates/typst/src/foundations/content.rs index 0d764f11..8c093764 100644 --- a/crates/typst/src/foundations/content.rs +++ b/crates/typst/src/foundations/content.rs @@ -14,8 +14,9 @@ use smallvec::smallvec; use crate::diag::{SourceResult, StrResult}; use crate::engine::Engine; use crate::foundations::{ - elem, func, scope, ty, Dict, Element, Fields, IntoValue, Label, NativeElement, - Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles, Value, + elem, func, scope, ty, Behave, Behaviour, Dict, Element, Fields, IntoValue, Label, + NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles, + Value, }; use crate::introspection::{Location, Meta, MetaElem}; use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides}; @@ -167,6 +168,12 @@ impl Content { self.make_mut().lifecycle.insert(0); } + /// How this element interacts with other elements in a stream. + pub fn behaviour(&self) -> Behaviour { + self.with::<dyn Behave>() + .map_or(Behaviour::Supportive, Behave::behaviour) + } + /// Get a field by ID. /// /// This is the preferred way to access fields. However, you can only use it @@ -335,7 +342,7 @@ impl Content { } /// Also auto expands sequence of sequences into flat sequence - pub fn sequence_recursive_for_each(&self, f: &mut impl FnMut(&Self)) { + pub fn sequence_recursive_for_each<'a>(&'a self, f: &mut impl FnMut(&'a Self)) { if let Some(children) = self.to_sequence() { children.for_each(|c| c.sequence_recursive_for_each(f)); } else { diff --git a/crates/typst/src/foundations/element.rs b/crates/typst/src/foundations/element.rs index 412e3089..b59a16cb 100644 --- a/crates/typst/src/foundations/element.rs +++ b/crates/typst/src/foundations/element.rs @@ -1,5 +1,4 @@ use std::any::TypeId; -use std::borrow::Cow; use std::cmp::Ordering; use std::fmt::{self, Debug}; use std::hash::Hash; @@ -309,11 +308,7 @@ pub trait Behave { /// Whether this weak element is larger than a previous one and thus picked /// as the maximum when the levels are the same. #[allow(unused_variables)] - fn larger( - &self, - prev: &(Cow<Content>, Behaviour, StyleChain), - styles: StyleChain, - ) -> bool { + fn larger(&self, prev: &(&Content, StyleChain), styles: StyleChain) -> bool { false } } @@ -336,3 +331,10 @@ pub enum Behaviour { /// An element that does not have a visual representation. Invisible, } + +impl Behaviour { + /// Whether this of `Weak(_)` variant. + pub fn is_weak(self) -> bool { + matches!(self, Self::Weak(_)) + } +} diff --git a/crates/typst/src/foundations/styles.rs b/crates/typst/src/foundations/styles.rs index c6ee3c5e..9656fafb 100644 --- a/crates/typst/src/foundations/styles.rs +++ b/crates/typst/src/foundations/styles.rs @@ -1,8 +1,7 @@ use std::any::{Any, TypeId}; -use std::borrow::Cow; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; -use std::{iter, mem, ptr}; +use std::{mem, ptr}; use comemo::Prehashed; use ecow::{eco_vec, EcoString, EcoVec}; @@ -570,13 +569,13 @@ impl<'a> StyleChain<'a> { } /// Iterate over the links of the chain. - fn links(self) -> Links<'a> { + pub fn links(self) -> Links<'a> { Links(Some(self)) } /// Build owned styles from the suffix (all links beyond the `len`) of the /// chain. - fn suffix(self, len: usize) -> Styles { + pub fn suffix(self, len: usize) -> Styles { let mut suffix = Styles::new(); let take = self.links().count().saturating_sub(len); for link in self.links().take(take) { @@ -586,7 +585,7 @@ impl<'a> StyleChain<'a> { } /// Remove the last link from the chain. - fn pop(&mut self) { + pub fn pop(&mut self) { *self = self.tail.copied().unwrap_or_default(); } } @@ -672,7 +671,7 @@ impl<'a> Iterator for Entries<'a> { } /// An iterator over the links of a style chain. -struct Links<'a>(Option<StyleChain<'a>>); +pub struct Links<'a>(Option<StyleChain<'a>>); impl<'a> Iterator for Links<'a> { type Item = &'a [Prehashed<Style>]; @@ -684,201 +683,6 @@ impl<'a> Iterator for Links<'a> { } } -/// A sequence of items with associated styles. -#[derive(Clone, Hash)] -pub struct StyleVec<T> { - items: Vec<T>, - styles: Vec<(Styles, usize)>, -} - -impl<T> StyleVec<T> { - /// Whether there are any items in the sequence. - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - - /// Number of items in the sequence. - pub fn len(&self) -> usize { - self.items.len() - } - - /// Insert an item in the front. The item will share the style of the - /// current first item. - /// - /// This method has no effect if the vector is empty. - pub fn push_front(&mut self, item: T) { - if !self.styles.is_empty() { - self.items.insert(0, item); - self.styles[0].1 += 1; - } - } - - /// Map the contained items. - pub fn map<F, U>(&self, f: F) -> StyleVec<U> - where - F: FnMut(&T) -> U, - { - StyleVec { - items: self.items.iter().map(f).collect(), - styles: self.styles.clone(), - } - } - - /// Iterate over references to the contained items and associated styles. - pub fn iter(&self) -> impl Iterator<Item = (&T, &Styles)> + '_ { - self.items().zip( - self.styles - .iter() - .flat_map(|(map, count)| iter::repeat(map).take(*count)), - ) - } - - /// Iterate over the contained items. - pub fn items(&self) -> std::slice::Iter<'_, T> { - self.items.iter() - } - - /// Extract the contained items. - pub fn into_items(self) -> Vec<T> { - self.items - } - - /// Iterate over the contained style lists. Note that zipping this with - /// `items()` does not yield the same result as calling `iter()` because - /// this method only returns lists once that are shared by consecutive - /// items. This method is designed for use cases where you want to check, - /// for example, whether any of the lists fulfills a specific property. - pub fn styles(&self) -> impl Iterator<Item = &Styles> { - self.styles.iter().map(|(map, _)| map) - } -} - -impl<'a> StyleVec<Cow<'a, Content>> { - pub fn to_vec<F: From<Content>>(self) -> Vec<F> { - self.items - .into_iter() - .zip( - self.styles - .iter() - .flat_map(|(map, count)| iter::repeat(map).take(*count)), - ) - .map(|(content, styles)| content.into_owned().styled_with_map(styles.clone())) - .map(F::from) - .collect() - } -} - -impl<T> Default for StyleVec<T> { - fn default() -> Self { - Self { items: vec![], styles: vec![] } - } -} - -impl<T> FromIterator<T> for StyleVec<T> { - fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { - let items: Vec<_> = iter.into_iter().collect(); - let styles = vec![(Styles::new(), items.len())]; - Self { items, styles } - } -} - -impl<T: Debug> Debug for StyleVec<T> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_list() - .entries(self.iter().map(|(item, styles)| { - crate::util::debug(|f| { - styles.fmt(f)?; - item.fmt(f) - }) - })) - .finish() - } -} - -/// Assists in the construction of a [`StyleVec`]. -#[derive(Debug)] -pub struct StyleVecBuilder<'a, T> { - items: Vec<T>, - chains: Vec<(StyleChain<'a>, usize)>, -} - -impl<'a, T> StyleVecBuilder<'a, T> { - /// Create a new style-vec builder. - pub fn new() -> Self { - Self { items: vec![], chains: vec![] } - } - - /// Whether the builder is empty. - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - - /// Push a new item into the style vector. - pub fn push(&mut self, item: T, styles: StyleChain<'a>) { - self.items.push(item); - - if let Some((prev, count)) = self.chains.last_mut() { - if *prev == styles { - *count += 1; - return; - } - } - - self.chains.push((styles, 1)); - } - - /// Iterate over the contained items. - pub fn elems(&self) -> std::slice::Iter<'_, T> { - self.items.iter() - } - - /// Finish building, returning a pair of two things: - /// - a style vector of items with the non-shared styles - /// - a shared prefix chain of styles that apply to all items - pub fn finish(self) -> (StyleVec<T>, StyleChain<'a>) { - let mut iter = self.chains.iter(); - let mut trunk = match iter.next() { - Some(&(chain, _)) => chain, - None => return Default::default(), - }; - - let mut shared = trunk.links().count(); - for &(mut chain, _) in iter { - let len = chain.links().count(); - if len < shared { - for _ in 0..shared - len { - trunk.pop(); - } - shared = len; - } else if len > shared { - for _ in 0..len - shared { - chain.pop(); - } - } - - while shared > 0 && chain != trunk { - trunk.pop(); - chain.pop(); - shared -= 1; - } - } - - let styles = self - .chains - .into_iter() - .map(|(chain, count)| (chain.suffix(shared), count)) - .collect(); - - (StyleVec { items: self.items, styles }, trunk) - } -} - -impl<'a, T> Default for StyleVecBuilder<'a, T> { - fn default() -> Self { - Self::new() - } -} - /// A property that is resolved with other properties from the style chain. pub trait Resolve { /// The type of the resolved output. diff --git a/crates/typst/src/layout/mod.rs b/crates/typst/src/layout/mod.rs index c961b693..7f0c3916 100644 --- a/crates/typst/src/layout/mod.rs +++ b/crates/typst/src/layout/mod.rs @@ -77,7 +77,7 @@ use crate::eval::Tracer; use crate::foundations::{category, Category, Content, Scope, StyleChain}; use crate::introspection::{Introspector, Locator}; use crate::model::Document; -use crate::realize::{realize_block, realize_root, Scratch}; +use crate::realize::{realize_block, realize_root, Arenas}; use crate::World; /// Arranging elements on the page in different ways. @@ -195,9 +195,8 @@ impl LayoutRoot for Content { locator: &mut locator, tracer, }; - let scratch = Scratch::default(); - let (document, styles) = - realize_root(&mut engine, &scratch, content, styles)?; + let arenas = Arenas::default(); + let (document, styles) = realize_root(&mut engine, &arenas, content, styles)?; document.layout_root(&mut engine, styles) } @@ -248,9 +247,9 @@ impl LayoutMultiple for Content { ); } - let scratch = Scratch::default(); + let arenas = Arenas::default(); let (realized, styles) = - realize_block(&mut engine, &scratch, content, styles)?; + realize_block(&mut engine, &arenas, content, styles)?; realized.with::<dyn LayoutMultiple>().unwrap().layout( &mut engine, styles, diff --git a/crates/typst/src/layout/spacing.rs b/crates/typst/src/layout/spacing.rs index ec029ecd..d6a1d592 100644 --- a/crates/typst/src/layout/spacing.rs +++ b/crates/typst/src/layout/spacing.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use crate::foundations::{ cast, elem, Behave, Behaviour, Content, Packed, Resolve, StyleChain, }; @@ -75,16 +73,12 @@ impl Behave for Packed<HElem> { } } - fn larger( - &self, - prev: &(Cow<Content>, Behaviour, StyleChain), - styles: StyleChain, - ) -> bool { + fn larger(&self, prev: &(&Content, StyleChain), styles: StyleChain) -> bool { let Some(other) = prev.0.to_packed::<HElem>() else { return false }; match (self.amount(), other.amount()) { (Spacing::Fr(this), Spacing::Fr(other)) => this > other, (Spacing::Rel(this), Spacing::Rel(other)) => { - this.resolve(styles) > other.resolve(prev.2) + this.resolve(styles) > other.resolve(prev.1) } _ => false, } @@ -177,16 +171,12 @@ impl Behave for Packed<VElem> { } } - fn larger( - &self, - prev: &(Cow<Content>, Behaviour, StyleChain), - styles: StyleChain, - ) -> bool { + fn larger(&self, prev: &(&Content, StyleChain), styles: StyleChain) -> bool { let Some(other) = prev.0.to_packed::<VElem>() else { return false }; match (self.amount(), other.amount()) { (Spacing::Fr(this), Spacing::Fr(other)) => this > other, (Spacing::Rel(this), Spacing::Rel(other)) => { - this.resolve(styles) > other.resolve(prev.2) + this.resolve(styles) > other.resolve(prev.1) } _ => false, } diff --git a/crates/typst/src/math/mod.rs b/crates/typst/src/math/mod.rs index 3338fb63..63333d0f 100644 --- a/crates/typst/src/math/mod.rs +++ b/crates/typst/src/math/mod.rs @@ -40,8 +40,6 @@ use self::fragment::*; use self::row::*; use self::spacing::*; -use std::borrow::Cow; - use crate::diag::SourceResult; use crate::foundations::{ category, Category, Content, Module, Resolve, Scope, StyleChain, @@ -239,10 +237,9 @@ impl LayoutMath for Content { if self.is_sequence() { let mut bb = BehavedBuilder::new(); self.sequence_recursive_for_each(&mut |child: &Content| { - bb.push(Cow::Owned(child.clone()), StyleChain::default()) + bb.push(child, StyleChain::default()); }); - - for (child, _) in bb.finish().0.iter() { + for child in bb.finish::<Content>().0 { child.layout_math(ctx, styles)?; } return Ok(()); diff --git a/crates/typst/src/realize/arenas.rs b/crates/typst/src/realize/arenas.rs new file mode 100644 index 00000000..9c90091c --- /dev/null +++ b/crates/typst/src/realize/arenas.rs @@ -0,0 +1,34 @@ +use typed_arena::Arena; + +use crate::foundations::{Content, StyleChain}; + +/// Temporary storage arenas for building. +#[derive(Default)] +pub struct Arenas<'a> { + chains: Arena<StyleChain<'a>>, + content: Arena<Content>, +} + +impl<'a> Arenas<'a> { + /// Store a value in the matching arena. + pub fn store<T: Store<'a>>(&'a self, val: T) -> &'a T { + val.store(self) + } +} + +/// Implemented by storable types. +pub trait Store<'a> { + fn store(self, arenas: &'a Arenas<'a>) -> &'a Self; +} + +impl<'a> Store<'a> for Content { + fn store(self, arenas: &'a Arenas<'a>) -> &'a Self { + arenas.content.alloc(self) + } +} + +impl<'a> Store<'a> for StyleChain<'a> { + fn store(self, arenas: &'a Arenas<'a>) -> &'a Self { + arenas.chains.alloc(self) + } +} diff --git a/crates/typst/src/realize/behave.rs b/crates/typst/src/realize/behave.rs index bc900994..035f6644 100644 --- a/crates/typst/src/realize/behave.rs +++ b/crates/typst/src/realize/behave.rs @@ -1,107 +1,185 @@ //! Element interaction. -use std::borrow::Cow; +use crate::foundations::{Behave, Behaviour, Content, StyleChain, Styles}; +use crate::syntax::Span; -use crate::foundations::{ - Behave, Behaviour, Content, StyleChain, StyleVec, StyleVecBuilder, -}; - -/// A wrapper around a [`StyleVecBuilder`] that allows elements to interact. +/// Processes a sequence of content and resolves behaviour interactions between +/// them and separates local styles for each element from the shared trunk of +/// styles. #[derive(Debug)] pub struct BehavedBuilder<'a> { - /// The internal builder. - builder: StyleVecBuilder<'a, Cow<'a, Content>>, - /// Staged weak and ignorant elements that we can't yet commit to the - /// builder. The option is `Some(_)` for weak elements and `None` for - /// ignorant elements. - staged: Vec<(Cow<'a, Content>, Behaviour, StyleChain<'a>)>, - /// What the last non-ignorant item was. + /// The collected content with its styles. + buf: Vec<(&'a Content, StyleChain<'a>)>, + /// What the last non-ignorant, visible item was. last: Behaviour, } impl<'a> BehavedBuilder<'a> { /// Create a new style-vec builder. pub fn new() -> Self { - Self { - builder: StyleVecBuilder::new(), - staged: vec![], - last: Behaviour::Destructive, - } + Self { buf: vec![], last: Behaviour::Destructive } } /// Whether the builder is totally empty. pub fn is_empty(&self) -> bool { - self.builder.is_empty() && self.staged.is_empty() + self.buf.is_empty() } - /// Whether the builder is empty except for some weak elements that will - /// probably collapse. + /// Whether the builder has any proper (non-weak & visible) elements. pub fn has_strong_elements(&self, last: bool) -> bool { - !self.builder.is_empty() - || self.staged.iter().any(|(_, behaviour, _)| { - !matches!(behaviour, Behaviour::Weak(_) | Behaviour::Invisible) - || (last && *behaviour == Behaviour::Invisible) - }) + self.buf.iter().any(|(content, _)| { + let behaviour = content.behaviour(); + !matches!(behaviour, Behaviour::Weak(_) | Behaviour::Invisible) + || (last && behaviour == Behaviour::Invisible) + }) } - /// Push an item into the sequence. - pub fn push(&mut self, elem: Cow<'a, Content>, styles: StyleChain<'a>) { - let interaction = elem - .with::<dyn Behave>() - .map_or(Behaviour::Supportive, Behave::behaviour); - - match interaction { - Behaviour::Weak(level) => { - if matches!(self.last, Behaviour::Weak(_)) { - let item = elem.with::<dyn Behave>().unwrap(); - let i = self.staged.iter().position(|prev| { - let Behaviour::Weak(prev_level) = prev.1 else { return false }; - level < prev_level - || (level == prev_level && item.larger(prev, styles)) - }); - let Some(i) = i else { return }; - self.staged.remove(i); - } + /// Push an item into the builder. + pub fn push(&mut self, content: &'a Content, styles: StyleChain<'a>) { + let mut behaviour = content.behaviour(); + match behaviour { + Behaviour::Supportive => {} + Behaviour::Weak(level) => match self.last { + // Remove either this or the preceding weak item. + Behaviour::Weak(prev_level) => { + if level > prev_level { + return; + } - if self.last != Behaviour::Destructive { - self.staged.push((elem, interaction, styles)); - self.last = interaction; + let i = self.find_last_weak().unwrap(); + if level == prev_level + && !content + .with::<dyn Behave>() + .unwrap() + .larger(&self.buf[i], styles) + { + return; + } + + self.buf.remove(i); } - } - Behaviour::Supportive => { - self.flush(true); - self.builder.push(elem, styles); - self.last = interaction; - } + Behaviour::Destructive => return, + _ => {} + }, Behaviour::Destructive => { - self.flush(false); - self.builder.push(elem, styles); - self.last = interaction; + // Remove preceding weak item. + if self.last.is_weak() { + let i = self.find_last_weak().unwrap(); + self.buf.remove(i); + } } Behaviour::Ignorant | Behaviour::Invisible => { - self.staged.push((elem, interaction, styles)); + behaviour = self.last; } } + + self.last = behaviour; + self.buf.push((content, styles)); + } + + /// Iterate over the content that has been pushed so far. + pub fn items(&self) -> impl Iterator<Item = &'a Content> + '_ { + self.buf.iter().map(|&(c, _)| c) } - /// Return the finish style vec and the common prefix chain. - pub fn finish(mut self) -> (StyleVec<Cow<'a, Content>>, StyleChain<'a>) { - self.flush(false); - self.builder.finish() + /// Return the built content (possibly styled with local styles) plus a + /// trunk style chain and a span for the collection. + pub fn finish<F: From<Content>>(self) -> (Vec<F>, StyleChain<'a>, Span) { + let (output, trunk, span) = self.finish_iter(); + let output = output.map(|(c, s)| c.clone().styled_with_map(s).into()).collect(); + (output, trunk, span) } - /// Push the staged elements, filtering out weak elements if `supportive` is - /// false. - fn flush(&mut self, supportive: bool) { - for (item, interaction, styles) in self.staged.drain(..) { - if supportive - || interaction == Behaviour::Ignorant - || interaction == Behaviour::Invisible - { - self.builder.push(item, styles); + /// Return an iterator over the built content and its local styles plus a + /// trunk style chain and a span for the collection. + pub fn finish_iter( + mut self, + ) -> (impl Iterator<Item = (&'a Content, Styles)>, StyleChain<'a>, Span) { + self.trim_weak(); + + let span = self.determine_span(); + let (trunk, depth) = self.determine_style_trunk(); + + let mut iter = self.buf.into_iter().peekable(); + let mut reuse = None; + + // Map the content + style chains to content + suffix maps, reusing + // equivalent adjacent suffix maps, if possible. + let output = std::iter::from_fn(move || { + let (c, s) = iter.next()?; + + // Try to reuse a suffix map that the previous element has + // stored for us. + let suffix = reuse.take().unwrap_or_else(|| s.suffix(depth)); + + // Store a suffix map for the next element if it has the same style + // chain. + if iter.peek().map_or(false, |&(_, s2)| s == s2) { + reuse = Some(suffix.clone()); } + + Some((c, suffix)) + }); + + (output, trunk, span) + } + + /// Trim a possibly remaining weak item. + fn trim_weak(&mut self) { + if self.last.is_weak() { + let i = self.find_last_weak().unwrap(); + self.buf.remove(i); } } + + /// Get the position of the right most weak item. + fn find_last_weak(&self) -> Option<usize> { + 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> { diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs index 191d9eed..a03a209e 100644 --- a/crates/typst/src/realize/mod.rs +++ b/crates/typst/src/realize/mod.rs @@ -1,7 +1,9 @@ //! Realization of content. +mod arenas; mod behave; +pub use self::arenas::Arenas; pub use self::behave::BehavedBuilder; use std::borrow::Cow; @@ -9,14 +11,12 @@ use std::cell::OnceCell; use std::mem; use smallvec::smallvec; -use typed_arena::Arena; use crate::diag::{bail, SourceResult}; use crate::engine::{Engine, Route}; use crate::foundations::{ - Behave, Behaviour, Content, NativeElement, Packed, Recipe, RecipeIndex, Regex, - Selector, Show, ShowSet, Style, StyleChain, StyleVec, StyleVecBuilder, Styles, - Synthesize, Transformation, + Content, NativeElement, Packed, Recipe, RecipeIndex, Regex, Selector, Show, ShowSet, + Style, StyleChain, Styles, Synthesize, Transformation, }; use crate::introspection::{Locatable, Meta, MetaElem}; use crate::layout::{ @@ -36,15 +36,14 @@ use crate::util::{hash128, BitSet}; #[typst_macros::time(name = "realize root")] pub fn realize_root<'a>( engine: &mut Engine, - scratch: &'a Scratch<'a>, + arenas: &'a Arenas<'a>, content: &'a Content, styles: StyleChain<'a>, ) -> SourceResult<(Packed<DocumentElem>, StyleChain<'a>)> { - let mut builder = Builder::new(engine, scratch, true); + let mut builder = Builder::new(engine, arenas, true); builder.accept(content, styles)?; builder.interrupt_page(Some(styles), true)?; - let (pages, shared) = builder.doc.unwrap().pages.finish(); - let span = first_span(&pages); + let (pages, shared, span) = builder.doc.unwrap().pages.finish(); Ok((Packed::new(DocumentElem::new(pages.to_vec())).spanned(span), shared)) } @@ -52,7 +51,7 @@ pub fn realize_root<'a>( #[typst_macros::time(name = "realize block")] pub fn realize_block<'a>( engine: &mut Engine, - scratch: &'a Scratch<'a>, + arenas: &'a Arenas<'a>, content: &'a Content, styles: StyleChain<'a>, ) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> { @@ -62,13 +61,12 @@ pub fn realize_block<'a>( return Ok((Cow::Borrowed(content), styles)); } - let mut builder = Builder::new(engine, scratch, false); + let mut builder = Builder::new(engine, arenas, false); builder.accept(content, styles)?; builder.interrupt_par()?; - let (children, shared) = builder.flow.0.finish(); - let span = first_span(&children); - Ok((Cow::Owned(FlowElem::new(children.to_vec()).pack().spanned(span)), shared)) + let (children, shared, span) = builder.flow.0.finish(); + Ok((Cow::Owned(FlowElem::new(children).pack().spanned(span)), shared)) } /// Apply the show rules in the given style chain to a target element. @@ -366,7 +364,7 @@ struct Builder<'a, 'v, 't> { /// The engine. engine: &'v mut Engine<'t>, /// Scratch arenas for building. - scratch: &'a Scratch<'a>, + arenas: &'a Arenas<'a>, /// The current document building state. doc: Option<DocBuilder<'a>>, /// The current flow building state. @@ -379,20 +377,11 @@ struct Builder<'a, 'v, 't> { cites: CiteGroupBuilder<'a>, } -/// Temporary storage arenas for building. -#[derive(Default)] -pub struct Scratch<'a> { - /// An arena where intermediate style chains are stored. - styles: Arena<StyleChain<'a>>, - /// An arena where intermediate content resulting from show rules is stored. - content: Arena<Content>, -} - impl<'a, 'v, 't> Builder<'a, 'v, 't> { - fn new(engine: &'v mut Engine<'t>, scratch: &'a Scratch<'a>, top: bool) -> Self { + fn new(engine: &'v mut Engine<'t>, arenas: &'a Arenas<'a>, top: bool) -> Self { Self { engine, - scratch, + arenas, doc: top.then(DocBuilder::default), flow: FlowBuilder::default(), par: ParBuilder::default(), @@ -408,9 +397,8 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { ) -> SourceResult<()> { if content.can::<dyn LayoutMath>() && !content.is::<EquationElem>() { content = self - .scratch - .content - .alloc(EquationElem::new(content.clone()).pack().spanned(content.span())); + .arenas + .store(EquationElem::new(content.clone()).pack().spanned(content.span())); } if let Some(realized) = realize(self.engine, content, styles)? { @@ -421,10 +409,9 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { hint: "check whether the show rule matches its own output" ); } - let stored = self.scratch.content.alloc(realized); - let v = self.accept(stored, styles); + let result = self.accept(self.arenas.store(realized), styles); self.engine.route.decrease(); - return v; + return result; } if let Some((elem, local)) = content.to_styled() { @@ -460,7 +447,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { self.interrupt_par()?; - if self.flow.accept(content, styles) { + if self.flow.accept(self.arenas, content, styles) { return Ok(()); } @@ -471,7 +458,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { self.interrupt_page(keep.then_some(styles), false)?; if let Some(doc) = &mut self.doc { - if doc.accept(content, styles) { + if doc.accept(self.arenas, content, styles) { return Ok(()); } } @@ -489,7 +476,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { map: &'a Styles, styles: StyleChain<'a>, ) -> SourceResult<()> { - let stored = self.scratch.styles.alloc(styles); + let stored = self.arenas.store(styles); let styles = stored.chain(map); self.interrupt_style(map, None)?; self.accept(elem, styles)?; @@ -535,8 +522,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { if !self.cites.items.is_empty() { let staged = mem::take(&mut self.cites.staged); let (group, styles) = mem::take(&mut self.cites).finish(); - let stored = self.scratch.content.alloc(group); - self.accept(stored, styles)?; + self.accept(self.arenas.store(group), styles)?; for (content, styles) in staged { self.accept(content, styles)?; } @@ -549,8 +535,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { if !self.list.items.is_empty() { let staged = mem::take(&mut self.list.staged); let (list, styles) = mem::take(&mut self.list).finish(); - let stored = self.scratch.content.alloc(list); - self.accept(stored, styles)?; + self.accept(self.arenas.store(list), styles)?; for (content, styles) in staged { self.accept(content, styles)?; } @@ -562,8 +547,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { self.interrupt_list()?; if !self.par.0.is_empty() { let (par, styles) = mem::take(&mut self.par).finish(); - let stored = self.scratch.content.alloc(par); - self.accept(stored, styles)?; + self.accept(self.arenas.store(par), styles)?; } Ok(()) @@ -577,17 +561,15 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { self.interrupt_par()?; let Some(doc) = &mut self.doc else { return Ok(()) }; if (doc.keep_next && styles.is_some()) || self.flow.0.has_strong_elements(last) { - let (children, shared) = mem::take(&mut self.flow).0.finish(); + let (children, shared, span) = mem::take(&mut self.flow).0.finish(); let styles = if shared == StyleChain::default() { styles.unwrap_or_default() } else { shared }; - let span = first_span(&children); - let flow = FlowElem::new(children.to_vec()); + let flow = FlowElem::new(children); let page = PageElem::new(flow.pack().spanned(span)); - let stored = self.scratch.content.alloc(page.pack().spanned(span)); - self.accept(stored, styles)?; + self.accept(self.arenas.store(page.pack().spanned(span)), styles)?; } Ok(()) } @@ -596,7 +578,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { /// Accepts pagebreaks and pages. struct DocBuilder<'a> { /// The page runs built so far. - pages: StyleVecBuilder<'a, Cow<'a, Content>>, + pages: BehavedBuilder<'a>, /// Whether to keep a following page even if it is empty. keep_next: bool, /// Whether the next page should be cleared to an even or odd number. @@ -604,7 +586,12 @@ struct DocBuilder<'a> { } impl<'a> DocBuilder<'a> { - fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool { + fn accept( + &mut self, + arenas: &'a Arenas<'a>, + content: &'a Content, + styles: StyleChain<'a>, + ) -> bool { if let Some(pagebreak) = content.to_packed::<PagebreakElem>() { self.keep_next = !pagebreak.weak(styles); self.clear_next = pagebreak.to(styles); @@ -615,9 +602,9 @@ impl<'a> DocBuilder<'a> { let elem = if let Some(clear_to) = self.clear_next.take() { let mut page = page.clone(); page.push_clear_to(Some(clear_to)); - Cow::Owned(page.pack()) + arenas.store(page.pack()) } else { - Cow::Borrowed(content) + content }; self.pages.push(elem, styles); @@ -632,7 +619,7 @@ impl<'a> DocBuilder<'a> { impl Default for DocBuilder<'_> { fn default() -> Self { Self { - pages: StyleVecBuilder::new(), + pages: BehavedBuilder::new(), keep_next: true, clear_next: None, } @@ -644,7 +631,12 @@ impl Default for DocBuilder<'_> { struct FlowBuilder<'a>(BehavedBuilder<'a>, bool); impl<'a> FlowBuilder<'a> { - fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool { + fn accept( + &mut self, + arenas: &'a Arenas<'a>, + content: &'a Content, + styles: StyleChain<'a>, + ) -> bool { if content.is::<ParbreakElem>() { self.1 = true; return true; @@ -658,7 +650,7 @@ impl<'a> FlowBuilder<'a> { || content.is::<MetaElem>() || content.is::<PlaceElem>() { - self.0.push(Cow::Borrowed(content), styles); + self.0.push(content, styles); return true; } @@ -679,7 +671,7 @@ impl<'a> FlowBuilder<'a> { if !last_was_parbreak && is_tight_list { let leading = ParElem::leading_in(styles); let spacing = VElem::list_attach(leading.into()); - self.0.push(Cow::Owned(spacing.pack()), styles); + self.0.push(arenas.store(spacing.pack()), styles); } let (above, below) = if let Some(block) = content.to_packed::<BlockElem>() { @@ -688,9 +680,9 @@ impl<'a> FlowBuilder<'a> { (BlockElem::above_in(styles), BlockElem::below_in(styles)) }; - self.0.push(Cow::Owned(above.pack()), styles); - self.0.push(Cow::Borrowed(content), styles); - self.0.push(Cow::Owned(below.pack()), styles); + self.0.push(arenas.store(above.pack()), styles); + self.0.push(content, styles); + self.0.push(arenas.store(below.pack()), styles); return true; } @@ -706,7 +698,7 @@ impl<'a> ParBuilder<'a> { fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool { if content.is::<MetaElem>() { if self.0.has_strong_elements(false) { - self.0.push(Cow::Borrowed(content), styles); + self.0.push(content, styles); return true; } } else if content.is::<SpaceElem>() @@ -719,7 +711,7 @@ impl<'a> ParBuilder<'a> { .map_or(false, |elem| !elem.block(styles)) || content.is::<BoxElem>() { - self.0.push(Cow::Borrowed(content), styles); + self.0.push(content, styles); return true; } @@ -727,16 +719,15 @@ impl<'a> ParBuilder<'a> { } fn finish(self) -> (Content, StyleChain<'a>) { - let (children, shared) = self.0.finish(); - let span = first_span(&children); - (ParElem::new(children.to_vec()).pack().spanned(span), shared) + let (children, shared, span) = self.0.finish(); + (ParElem::new(children).pack().spanned(span), shared) } } /// Accepts list / enum items, spaces, paragraph breaks. struct ListBuilder<'a> { /// The list items collected so far. - items: StyleVecBuilder<'a, Cow<'a, Content>>, + items: BehavedBuilder<'a>, /// Whether the list contains no paragraph breaks. tight: bool, /// Trailing content for which it is unclear whether it is part of the list. @@ -757,11 +748,11 @@ impl<'a> ListBuilder<'a> { || content.is::<TermItem>()) && self .items - .elems() + .items() .next() .map_or(true, |first| first.func() == content.func()) { - self.items.push(Cow::Borrowed(content), styles); + self.items.push(content, styles); self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakElem>()); return true; } @@ -770,53 +761,50 @@ impl<'a> ListBuilder<'a> { } fn finish(self) -> (Content, StyleChain<'a>) { - let (items, shared) = self.items.finish(); - let span = first_span(&items); - let item = items.items().next().unwrap(); - let output = if item.is::<ListItem>() { + let (items, shared, span) = self.items.finish_iter(); + let mut items = items.peekable(); + let (first, _) = items.peek().unwrap(); + let output = if first.is::<ListItem>() { ListElem::new( items - .iter() .map(|(item, local)| { let mut item = item.to_packed::<ListItem>().unwrap().clone(); - let body = item.body().clone().styled_with_map(local.clone()); + let body = item.body().clone().styled_with_map(local); item.push_body(body); item }) - .collect::<Vec<_>>(), + .collect(), ) .with_tight(self.tight) .pack() .spanned(span) - } else if item.is::<EnumItem>() { + } else if first.is::<EnumItem>() { EnumElem::new( items - .iter() .map(|(item, local)| { let mut item = item.to_packed::<EnumItem>().unwrap().clone(); - let body = item.body().clone().styled_with_map(local.clone()); + let body = item.body().clone().styled_with_map(local); item.push_body(body); item }) - .collect::<Vec<_>>(), + .collect(), ) .with_tight(self.tight) .pack() .spanned(span) - } else if item.is::<TermItem>() { + } else if first.is::<TermItem>() { TermsElem::new( items - .iter() .map(|(item, local)| { let mut item = item.to_packed::<TermItem>().unwrap().clone(); let term = item.term().clone().styled_with_map(local.clone()); let description = - item.description().clone().styled_with_map(local.clone()); + item.description().clone().styled_with_map(local); item.push_term(term); item.push_description(description); item }) - .collect::<Vec<_>>(), + .collect(), ) .with_tight(self.tight) .pack() @@ -831,7 +819,7 @@ impl<'a> ListBuilder<'a> { impl Default for ListBuilder<'_> { fn default() -> Self { Self { - items: StyleVecBuilder::default(), + items: BehavedBuilder::default(), tight: true, staged: vec![], } @@ -875,16 +863,3 @@ impl<'a> CiteGroupBuilder<'a> { (CiteGroup::new(self.items).pack().spanned(span), self.styles) } } - -/// Find the first span that isn't detached. -fn first_span(children: &StyleVec<Cow<Content>>) -> Span { - children - .iter() - .filter(|(elem, _)| { - elem.with::<dyn Behave>() - .map_or(true, |b| b.behaviour() != Behaviour::Invisible) - }) - .map(|(elem, _)| elem.span()) - .find(|span| !span.is_detached()) - .unwrap_or_else(Span::detached) -} |
