Refactor behaved building (#3403)

This commit is contained in:
Laurenz 2024-02-12 13:29:52 +01:00 committed by GitHub
parent 36d588ae5d
commit 63b73ee98c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 283 additions and 397 deletions

View File

@ -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 {

View File

@ -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(_))
}
}

View File

@ -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.

View File

@ -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,

View File

@ -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,
}

View File

@ -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(());

View File

@ -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)
}
}

View File

@ -1,106 +1,184 @@
//! 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);
/// 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;
}
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);
}
let i = self.find_last_weak().unwrap();
if level == prev_level
&& !content
.with::<dyn Behave>()
.unwrap()
.larger(&self.buf[i], styles)
{
return;
}
if self.last != Behaviour::Destructive {
self.staged.push((elem, interaction, styles));
self.last = interaction;
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 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)
}
/// 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);
}
}
/// 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()
/// 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())
}
/// 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);
/// 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)
}
}

View File

@ -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)
}