summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-02-12 13:29:52 +0100
committerGitHub <noreply@github.com>2024-02-12 12:29:52 +0000
commit63b73ee98c75b26b7adb4b7517732e0d2bb7551c (patch)
tree40cd368674694f9d46aee688270fd6209d92d3d7
parent36d588ae5d5dbb198160f9ad7931bb4c5d3069ac (diff)
Refactor behaved building (#3403)
-rw-r--r--crates/typst/src/foundations/content.rs13
-rw-r--r--crates/typst/src/foundations/element.rs14
-rw-r--r--crates/typst/src/foundations/styles.rs206
-rw-r--r--crates/typst/src/layout/mod.rs11
-rw-r--r--crates/typst/src/layout/spacing.rs18
-rw-r--r--crates/typst/src/math/mod.rs7
-rw-r--r--crates/typst/src/realize/arenas.rs34
-rw-r--r--crates/typst/src/realize/behave.rs218
-rw-r--r--crates/typst/src/realize/mod.rs161
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)
-}