mirror of
https://github.com/typst/typst
synced 2025-05-15 01:25:28 +08:00
Refactor behaved building (#3403)
This commit is contained in:
parent
36d588ae5d
commit
63b73ee98c
@ -14,8 +14,9 @@ use smallvec::smallvec;
|
|||||||
use crate::diag::{SourceResult, StrResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, func, scope, ty, Dict, Element, Fields, IntoValue, Label, NativeElement,
|
elem, func, scope, ty, Behave, Behaviour, Dict, Element, Fields, IntoValue, Label,
|
||||||
Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles, Value,
|
NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Location, Meta, MetaElem};
|
use crate::introspection::{Location, Meta, MetaElem};
|
||||||
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
|
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
|
||||||
@ -167,6 +168,12 @@ impl Content {
|
|||||||
self.make_mut().lifecycle.insert(0);
|
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.
|
/// Get a field by ID.
|
||||||
///
|
///
|
||||||
/// This is the preferred way to access fields. However, you can only use it
|
/// 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
|
/// 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() {
|
if let Some(children) = self.to_sequence() {
|
||||||
children.for_each(|c| c.sequence_recursive_for_each(f));
|
children.for_each(|c| c.sequence_recursive_for_each(f));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
@ -309,11 +308,7 @@ pub trait Behave {
|
|||||||
/// Whether this weak element is larger than a previous one and thus picked
|
/// Whether this weak element is larger than a previous one and thus picked
|
||||||
/// as the maximum when the levels are the same.
|
/// as the maximum when the levels are the same.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn larger(
|
fn larger(&self, prev: &(&Content, StyleChain), styles: StyleChain) -> bool {
|
||||||
&self,
|
|
||||||
prev: &(Cow<Content>, Behaviour, StyleChain),
|
|
||||||
styles: StyleChain,
|
|
||||||
) -> bool {
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,3 +331,10 @@ pub enum Behaviour {
|
|||||||
/// An element that does not have a visual representation.
|
/// An element that does not have a visual representation.
|
||||||
Invisible,
|
Invisible,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Behaviour {
|
||||||
|
/// Whether this of `Weak(_)` variant.
|
||||||
|
pub fn is_weak(self) -> bool {
|
||||||
|
matches!(self, Self::Weak(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::{iter, mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
||||||
use comemo::Prehashed;
|
use comemo::Prehashed;
|
||||||
use ecow::{eco_vec, EcoString, EcoVec};
|
use ecow::{eco_vec, EcoString, EcoVec};
|
||||||
@ -570,13 +569,13 @@ impl<'a> StyleChain<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over the links of the chain.
|
/// Iterate over the links of the chain.
|
||||||
fn links(self) -> Links<'a> {
|
pub fn links(self) -> Links<'a> {
|
||||||
Links(Some(self))
|
Links(Some(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build owned styles from the suffix (all links beyond the `len`) of the
|
/// Build owned styles from the suffix (all links beyond the `len`) of the
|
||||||
/// chain.
|
/// chain.
|
||||||
fn suffix(self, len: usize) -> Styles {
|
pub fn suffix(self, len: usize) -> Styles {
|
||||||
let mut suffix = Styles::new();
|
let mut suffix = Styles::new();
|
||||||
let take = self.links().count().saturating_sub(len);
|
let take = self.links().count().saturating_sub(len);
|
||||||
for link in self.links().take(take) {
|
for link in self.links().take(take) {
|
||||||
@ -586,7 +585,7 @@ impl<'a> StyleChain<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the last link from the chain.
|
/// Remove the last link from the chain.
|
||||||
fn pop(&mut self) {
|
pub fn pop(&mut self) {
|
||||||
*self = self.tail.copied().unwrap_or_default();
|
*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.
|
/// 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> {
|
impl<'a> Iterator for Links<'a> {
|
||||||
type Item = &'a [Prehashed<Style>];
|
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.
|
/// A property that is resolved with other properties from the style chain.
|
||||||
pub trait Resolve {
|
pub trait Resolve {
|
||||||
/// The type of the resolved output.
|
/// The type of the resolved output.
|
||||||
|
@ -77,7 +77,7 @@ use crate::eval::Tracer;
|
|||||||
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
||||||
use crate::introspection::{Introspector, Locator};
|
use crate::introspection::{Introspector, Locator};
|
||||||
use crate::model::Document;
|
use crate::model::Document;
|
||||||
use crate::realize::{realize_block, realize_root, Scratch};
|
use crate::realize::{realize_block, realize_root, Arenas};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Arranging elements on the page in different ways.
|
/// Arranging elements on the page in different ways.
|
||||||
@ -195,9 +195,8 @@ impl LayoutRoot for Content {
|
|||||||
locator: &mut locator,
|
locator: &mut locator,
|
||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
let scratch = Scratch::default();
|
let arenas = Arenas::default();
|
||||||
let (document, styles) =
|
let (document, styles) = realize_root(&mut engine, &arenas, content, styles)?;
|
||||||
realize_root(&mut engine, &scratch, content, styles)?;
|
|
||||||
document.layout_root(&mut engine, 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) =
|
let (realized, styles) =
|
||||||
realize_block(&mut engine, &scratch, content, styles)?;
|
realize_block(&mut engine, &arenas, content, styles)?;
|
||||||
realized.with::<dyn LayoutMultiple>().unwrap().layout(
|
realized.with::<dyn LayoutMultiple>().unwrap().layout(
|
||||||
&mut engine,
|
&mut engine,
|
||||||
styles,
|
styles,
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, Behave, Behaviour, Content, Packed, Resolve, StyleChain,
|
cast, elem, Behave, Behaviour, Content, Packed, Resolve, StyleChain,
|
||||||
};
|
};
|
||||||
@ -75,16 +73,12 @@ impl Behave for Packed<HElem> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn larger(
|
fn larger(&self, prev: &(&Content, StyleChain), styles: StyleChain) -> bool {
|
||||||
&self,
|
|
||||||
prev: &(Cow<Content>, Behaviour, StyleChain),
|
|
||||||
styles: StyleChain,
|
|
||||||
) -> bool {
|
|
||||||
let Some(other) = prev.0.to_packed::<HElem>() else { return false };
|
let Some(other) = prev.0.to_packed::<HElem>() else { return false };
|
||||||
match (self.amount(), other.amount()) {
|
match (self.amount(), other.amount()) {
|
||||||
(Spacing::Fr(this), Spacing::Fr(other)) => this > other,
|
(Spacing::Fr(this), Spacing::Fr(other)) => this > other,
|
||||||
(Spacing::Rel(this), Spacing::Rel(other)) => {
|
(Spacing::Rel(this), Spacing::Rel(other)) => {
|
||||||
this.resolve(styles) > other.resolve(prev.2)
|
this.resolve(styles) > other.resolve(prev.1)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -177,16 +171,12 @@ impl Behave for Packed<VElem> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn larger(
|
fn larger(&self, prev: &(&Content, StyleChain), styles: StyleChain) -> bool {
|
||||||
&self,
|
|
||||||
prev: &(Cow<Content>, Behaviour, StyleChain),
|
|
||||||
styles: StyleChain,
|
|
||||||
) -> bool {
|
|
||||||
let Some(other) = prev.0.to_packed::<VElem>() else { return false };
|
let Some(other) = prev.0.to_packed::<VElem>() else { return false };
|
||||||
match (self.amount(), other.amount()) {
|
match (self.amount(), other.amount()) {
|
||||||
(Spacing::Fr(this), Spacing::Fr(other)) => this > other,
|
(Spacing::Fr(this), Spacing::Fr(other)) => this > other,
|
||||||
(Spacing::Rel(this), Spacing::Rel(other)) => {
|
(Spacing::Rel(this), Spacing::Rel(other)) => {
|
||||||
this.resolve(styles) > other.resolve(prev.2)
|
this.resolve(styles) > other.resolve(prev.1)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,6 @@ use self::fragment::*;
|
|||||||
use self::row::*;
|
use self::row::*;
|
||||||
use self::spacing::*;
|
use self::spacing::*;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
category, Category, Content, Module, Resolve, Scope, StyleChain,
|
category, Category, Content, Module, Resolve, Scope, StyleChain,
|
||||||
@ -239,10 +237,9 @@ impl LayoutMath for Content {
|
|||||||
if self.is_sequence() {
|
if self.is_sequence() {
|
||||||
let mut bb = BehavedBuilder::new();
|
let mut bb = BehavedBuilder::new();
|
||||||
self.sequence_recursive_for_each(&mut |child: &Content| {
|
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::<Content>().0 {
|
||||||
for (child, _) in bb.finish().0.iter() {
|
|
||||||
child.layout_math(ctx, styles)?;
|
child.layout_math(ctx, styles)?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
34
crates/typst/src/realize/arenas.rs
Normal file
34
crates/typst/src/realize/arenas.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,106 +1,184 @@
|
|||||||
//! Element interaction.
|
//! Element interaction.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use crate::foundations::{Behave, Behaviour, Content, StyleChain, Styles};
|
||||||
|
use crate::syntax::Span;
|
||||||
|
|
||||||
use crate::foundations::{
|
/// Processes a sequence of content and resolves behaviour interactions between
|
||||||
Behave, Behaviour, Content, StyleChain, StyleVec, StyleVecBuilder,
|
/// them and separates local styles for each element from the shared trunk of
|
||||||
};
|
/// styles.
|
||||||
|
|
||||||
/// A wrapper around a [`StyleVecBuilder`] that allows elements to interact.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BehavedBuilder<'a> {
|
pub struct BehavedBuilder<'a> {
|
||||||
/// The internal builder.
|
/// The collected content with its styles.
|
||||||
builder: StyleVecBuilder<'a, Cow<'a, Content>>,
|
buf: Vec<(&'a Content, StyleChain<'a>)>,
|
||||||
/// Staged weak and ignorant elements that we can't yet commit to the
|
/// What the last non-ignorant, visible item was.
|
||||||
/// 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.
|
|
||||||
last: Behaviour,
|
last: Behaviour,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BehavedBuilder<'a> {
|
impl<'a> BehavedBuilder<'a> {
|
||||||
/// Create a new style-vec builder.
|
/// Create a new style-vec builder.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { buf: vec![], last: Behaviour::Destructive }
|
||||||
builder: StyleVecBuilder::new(),
|
|
||||||
staged: vec![],
|
|
||||||
last: Behaviour::Destructive,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the builder is totally empty.
|
/// Whether the builder is totally empty.
|
||||||
pub fn is_empty(&self) -> bool {
|
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
|
/// Whether the builder has any proper (non-weak & visible) elements.
|
||||||
/// probably collapse.
|
|
||||||
pub fn has_strong_elements(&self, last: bool) -> bool {
|
pub fn has_strong_elements(&self, last: bool) -> bool {
|
||||||
!self.builder.is_empty()
|
self.buf.iter().any(|(content, _)| {
|
||||||
|| self.staged.iter().any(|(_, behaviour, _)| {
|
let behaviour = content.behaviour();
|
||||||
!matches!(behaviour, Behaviour::Weak(_) | Behaviour::Invisible)
|
!matches!(behaviour, Behaviour::Weak(_) | Behaviour::Invisible)
|
||||||
|| (last && *behaviour == Behaviour::Invisible)
|
|| (last && behaviour == Behaviour::Invisible)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push an item into the sequence.
|
/// Push an item into the builder.
|
||||||
pub fn push(&mut self, elem: Cow<'a, Content>, styles: StyleChain<'a>) {
|
pub fn push(&mut self, content: &'a Content, styles: StyleChain<'a>) {
|
||||||
let interaction = elem
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = self.find_last_weak().unwrap();
|
||||||
|
if level == prev_level
|
||||||
|
&& !content
|
||||||
.with::<dyn Behave>()
|
.with::<dyn Behave>()
|
||||||
.map_or(Behaviour::Supportive, Behave::behaviour);
|
.unwrap()
|
||||||
|
.larger(&self.buf[i], styles)
|
||||||
match interaction {
|
{
|
||||||
Behaviour::Weak(level) => {
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.last != Behaviour::Destructive {
|
self.buf.remove(i);
|
||||||
self.staged.push((elem, interaction, styles));
|
|
||||||
self.last = interaction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behaviour::Supportive => {
|
|
||||||
self.flush(true);
|
|
||||||
self.builder.push(elem, styles);
|
|
||||||
self.last = interaction;
|
|
||||||
}
|
}
|
||||||
|
Behaviour::Destructive => return,
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
Behaviour::Destructive => {
|
Behaviour::Destructive => {
|
||||||
self.flush(false);
|
// Remove preceding weak item.
|
||||||
self.builder.push(elem, styles);
|
if self.last.is_weak() {
|
||||||
self.last = interaction;
|
let i = self.find_last_weak().unwrap();
|
||||||
|
self.buf.remove(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Behaviour::Ignorant | Behaviour::Invisible => {
|
Behaviour::Ignorant | Behaviour::Invisible => {
|
||||||
self.staged.push((elem, interaction, styles));
|
behaviour = self.last;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the finish style vec and the common prefix chain.
|
self.last = behaviour;
|
||||||
pub fn finish(mut self) -> (StyleVec<Cow<'a, Content>>, StyleChain<'a>) {
|
self.buf.push((content, styles));
|
||||||
self.flush(false);
|
|
||||||
self.builder.finish()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push the staged elements, filtering out weak elements if `supportive` is
|
/// Iterate over the content that has been pushed so far.
|
||||||
/// false.
|
pub fn items(&self) -> impl Iterator<Item = &'a Content> + '_ {
|
||||||
fn flush(&mut self, supportive: bool) {
|
self.buf.iter().map(|&(c, _)| c)
|
||||||
for (item, interaction, styles) in self.staged.drain(..) {
|
}
|
||||||
if supportive
|
|
||||||
|| interaction == Behaviour::Ignorant
|
/// Return the built content (possibly styled with local styles) plus a
|
||||||
|| interaction == Behaviour::Invisible
|
/// trunk style chain and a span for the collection.
|
||||||
{
|
pub fn finish<F: From<Content>>(self) -> (Vec<F>, StyleChain<'a>, Span) {
|
||||||
self.builder.push(item, styles);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
//! Realization of content.
|
//! Realization of content.
|
||||||
|
|
||||||
|
mod arenas;
|
||||||
mod behave;
|
mod behave;
|
||||||
|
|
||||||
|
pub use self::arenas::Arenas;
|
||||||
pub use self::behave::BehavedBuilder;
|
pub use self::behave::BehavedBuilder;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@ -9,14 +11,12 @@ use std::cell::OnceCell;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
use typed_arena::Arena;
|
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::{Engine, Route};
|
use crate::engine::{Engine, Route};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Behave, Behaviour, Content, NativeElement, Packed, Recipe, RecipeIndex, Regex,
|
Content, NativeElement, Packed, Recipe, RecipeIndex, Regex, Selector, Show, ShowSet,
|
||||||
Selector, Show, ShowSet, Style, StyleChain, StyleVec, StyleVecBuilder, Styles,
|
Style, StyleChain, Styles, Synthesize, Transformation,
|
||||||
Synthesize, Transformation,
|
|
||||||
};
|
};
|
||||||
use crate::introspection::{Locatable, Meta, MetaElem};
|
use crate::introspection::{Locatable, Meta, MetaElem};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
@ -36,15 +36,14 @@ use crate::util::{hash128, BitSet};
|
|||||||
#[typst_macros::time(name = "realize root")]
|
#[typst_macros::time(name = "realize root")]
|
||||||
pub fn realize_root<'a>(
|
pub fn realize_root<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
scratch: &'a Scratch<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<(Packed<DocumentElem>, 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.accept(content, styles)?;
|
||||||
builder.interrupt_page(Some(styles), true)?;
|
builder.interrupt_page(Some(styles), true)?;
|
||||||
let (pages, shared) = builder.doc.unwrap().pages.finish();
|
let (pages, shared, span) = builder.doc.unwrap().pages.finish();
|
||||||
let span = first_span(&pages);
|
|
||||||
Ok((Packed::new(DocumentElem::new(pages.to_vec())).spanned(span), shared))
|
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")]
|
#[typst_macros::time(name = "realize block")]
|
||||||
pub fn realize_block<'a>(
|
pub fn realize_block<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
scratch: &'a Scratch<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
|
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
|
||||||
@ -62,13 +61,12 @@ pub fn realize_block<'a>(
|
|||||||
return Ok((Cow::Borrowed(content), styles));
|
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.accept(content, styles)?;
|
||||||
builder.interrupt_par()?;
|
builder.interrupt_par()?;
|
||||||
|
|
||||||
let (children, shared) = builder.flow.0.finish();
|
let (children, shared, span) = builder.flow.0.finish();
|
||||||
let span = first_span(&children);
|
Ok((Cow::Owned(FlowElem::new(children).pack().spanned(span)), shared))
|
||||||
Ok((Cow::Owned(FlowElem::new(children.to_vec()).pack().spanned(span)), shared))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the show rules in the given style chain to a target element.
|
/// Apply the show rules in the given style chain to a target element.
|
||||||
@ -366,7 +364,7 @@ struct Builder<'a, 'v, 't> {
|
|||||||
/// The engine.
|
/// The engine.
|
||||||
engine: &'v mut Engine<'t>,
|
engine: &'v mut Engine<'t>,
|
||||||
/// Scratch arenas for building.
|
/// Scratch arenas for building.
|
||||||
scratch: &'a Scratch<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
/// The current document building state.
|
/// The current document building state.
|
||||||
doc: Option<DocBuilder<'a>>,
|
doc: Option<DocBuilder<'a>>,
|
||||||
/// The current flow building state.
|
/// The current flow building state.
|
||||||
@ -379,20 +377,11 @@ struct Builder<'a, 'v, 't> {
|
|||||||
cites: CiteGroupBuilder<'a>,
|
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> {
|
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 {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
scratch,
|
arenas,
|
||||||
doc: top.then(DocBuilder::default),
|
doc: top.then(DocBuilder::default),
|
||||||
flow: FlowBuilder::default(),
|
flow: FlowBuilder::default(),
|
||||||
par: ParBuilder::default(),
|
par: ParBuilder::default(),
|
||||||
@ -408,9 +397,8 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
if content.can::<dyn LayoutMath>() && !content.is::<EquationElem>() {
|
if content.can::<dyn LayoutMath>() && !content.is::<EquationElem>() {
|
||||||
content = self
|
content = self
|
||||||
.scratch
|
.arenas
|
||||||
.content
|
.store(EquationElem::new(content.clone()).pack().spanned(content.span()));
|
||||||
.alloc(EquationElem::new(content.clone()).pack().spanned(content.span()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(realized) = realize(self.engine, content, styles)? {
|
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"
|
hint: "check whether the show rule matches its own output"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let stored = self.scratch.content.alloc(realized);
|
let result = self.accept(self.arenas.store(realized), styles);
|
||||||
let v = self.accept(stored, styles);
|
|
||||||
self.engine.route.decrease();
|
self.engine.route.decrease();
|
||||||
return v;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((elem, local)) = content.to_styled() {
|
if let Some((elem, local)) = content.to_styled() {
|
||||||
@ -460,7 +447,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
|
|
||||||
self.interrupt_par()?;
|
self.interrupt_par()?;
|
||||||
|
|
||||||
if self.flow.accept(content, styles) {
|
if self.flow.accept(self.arenas, content, styles) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +458,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
self.interrupt_page(keep.then_some(styles), false)?;
|
self.interrupt_page(keep.then_some(styles), false)?;
|
||||||
|
|
||||||
if let Some(doc) = &mut self.doc {
|
if let Some(doc) = &mut self.doc {
|
||||||
if doc.accept(content, styles) {
|
if doc.accept(self.arenas, content, styles) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,7 +476,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
map: &'a Styles,
|
map: &'a Styles,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let stored = self.scratch.styles.alloc(styles);
|
let stored = self.arenas.store(styles);
|
||||||
let styles = stored.chain(map);
|
let styles = stored.chain(map);
|
||||||
self.interrupt_style(map, None)?;
|
self.interrupt_style(map, None)?;
|
||||||
self.accept(elem, styles)?;
|
self.accept(elem, styles)?;
|
||||||
@ -535,8 +522,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
if !self.cites.items.is_empty() {
|
if !self.cites.items.is_empty() {
|
||||||
let staged = mem::take(&mut self.cites.staged);
|
let staged = mem::take(&mut self.cites.staged);
|
||||||
let (group, styles) = mem::take(&mut self.cites).finish();
|
let (group, styles) = mem::take(&mut self.cites).finish();
|
||||||
let stored = self.scratch.content.alloc(group);
|
self.accept(self.arenas.store(group), styles)?;
|
||||||
self.accept(stored, styles)?;
|
|
||||||
for (content, styles) in staged {
|
for (content, styles) in staged {
|
||||||
self.accept(content, styles)?;
|
self.accept(content, styles)?;
|
||||||
}
|
}
|
||||||
@ -549,8 +535,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
if !self.list.items.is_empty() {
|
if !self.list.items.is_empty() {
|
||||||
let staged = mem::take(&mut self.list.staged);
|
let staged = mem::take(&mut self.list.staged);
|
||||||
let (list, styles) = mem::take(&mut self.list).finish();
|
let (list, styles) = mem::take(&mut self.list).finish();
|
||||||
let stored = self.scratch.content.alloc(list);
|
self.accept(self.arenas.store(list), styles)?;
|
||||||
self.accept(stored, styles)?;
|
|
||||||
for (content, styles) in staged {
|
for (content, styles) in staged {
|
||||||
self.accept(content, styles)?;
|
self.accept(content, styles)?;
|
||||||
}
|
}
|
||||||
@ -562,8 +547,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
self.interrupt_list()?;
|
self.interrupt_list()?;
|
||||||
if !self.par.0.is_empty() {
|
if !self.par.0.is_empty() {
|
||||||
let (par, styles) = mem::take(&mut self.par).finish();
|
let (par, styles) = mem::take(&mut self.par).finish();
|
||||||
let stored = self.scratch.content.alloc(par);
|
self.accept(self.arenas.store(par), styles)?;
|
||||||
self.accept(stored, styles)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -577,17 +561,15 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
self.interrupt_par()?;
|
self.interrupt_par()?;
|
||||||
let Some(doc) = &mut self.doc else { return Ok(()) };
|
let Some(doc) = &mut self.doc else { return Ok(()) };
|
||||||
if (doc.keep_next && styles.is_some()) || self.flow.0.has_strong_elements(last) {
|
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() {
|
let styles = if shared == StyleChain::default() {
|
||||||
styles.unwrap_or_default()
|
styles.unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
shared
|
shared
|
||||||
};
|
};
|
||||||
let span = first_span(&children);
|
let flow = FlowElem::new(children);
|
||||||
let flow = FlowElem::new(children.to_vec());
|
|
||||||
let page = PageElem::new(flow.pack().spanned(span));
|
let page = PageElem::new(flow.pack().spanned(span));
|
||||||
let stored = self.scratch.content.alloc(page.pack().spanned(span));
|
self.accept(self.arenas.store(page.pack().spanned(span)), styles)?;
|
||||||
self.accept(stored, styles)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -596,7 +578,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
/// Accepts pagebreaks and pages.
|
/// Accepts pagebreaks and pages.
|
||||||
struct DocBuilder<'a> {
|
struct DocBuilder<'a> {
|
||||||
/// The page runs built so far.
|
/// 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.
|
/// Whether to keep a following page even if it is empty.
|
||||||
keep_next: bool,
|
keep_next: bool,
|
||||||
/// Whether the next page should be cleared to an even or odd number.
|
/// Whether the next page should be cleared to an even or odd number.
|
||||||
@ -604,7 +586,12 @@ struct DocBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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>() {
|
if let Some(pagebreak) = content.to_packed::<PagebreakElem>() {
|
||||||
self.keep_next = !pagebreak.weak(styles);
|
self.keep_next = !pagebreak.weak(styles);
|
||||||
self.clear_next = pagebreak.to(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 elem = if let Some(clear_to) = self.clear_next.take() {
|
||||||
let mut page = page.clone();
|
let mut page = page.clone();
|
||||||
page.push_clear_to(Some(clear_to));
|
page.push_clear_to(Some(clear_to));
|
||||||
Cow::Owned(page.pack())
|
arenas.store(page.pack())
|
||||||
} else {
|
} else {
|
||||||
Cow::Borrowed(content)
|
content
|
||||||
};
|
};
|
||||||
|
|
||||||
self.pages.push(elem, styles);
|
self.pages.push(elem, styles);
|
||||||
@ -632,7 +619,7 @@ impl<'a> DocBuilder<'a> {
|
|||||||
impl Default for DocBuilder<'_> {
|
impl Default for DocBuilder<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pages: StyleVecBuilder::new(),
|
pages: BehavedBuilder::new(),
|
||||||
keep_next: true,
|
keep_next: true,
|
||||||
clear_next: None,
|
clear_next: None,
|
||||||
}
|
}
|
||||||
@ -644,7 +631,12 @@ impl Default for DocBuilder<'_> {
|
|||||||
struct FlowBuilder<'a>(BehavedBuilder<'a>, bool);
|
struct FlowBuilder<'a>(BehavedBuilder<'a>, bool);
|
||||||
|
|
||||||
impl<'a> FlowBuilder<'a> {
|
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>() {
|
if content.is::<ParbreakElem>() {
|
||||||
self.1 = true;
|
self.1 = true;
|
||||||
return true;
|
return true;
|
||||||
@ -658,7 +650,7 @@ impl<'a> FlowBuilder<'a> {
|
|||||||
|| content.is::<MetaElem>()
|
|| content.is::<MetaElem>()
|
||||||
|| content.is::<PlaceElem>()
|
|| content.is::<PlaceElem>()
|
||||||
{
|
{
|
||||||
self.0.push(Cow::Borrowed(content), styles);
|
self.0.push(content, styles);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,7 +671,7 @@ impl<'a> FlowBuilder<'a> {
|
|||||||
if !last_was_parbreak && is_tight_list {
|
if !last_was_parbreak && is_tight_list {
|
||||||
let leading = ParElem::leading_in(styles);
|
let leading = ParElem::leading_in(styles);
|
||||||
let spacing = VElem::list_attach(leading.into());
|
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>() {
|
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))
|
(BlockElem::above_in(styles), BlockElem::below_in(styles))
|
||||||
};
|
};
|
||||||
|
|
||||||
self.0.push(Cow::Owned(above.pack()), styles);
|
self.0.push(arenas.store(above.pack()), styles);
|
||||||
self.0.push(Cow::Borrowed(content), styles);
|
self.0.push(content, styles);
|
||||||
self.0.push(Cow::Owned(below.pack()), styles);
|
self.0.push(arenas.store(below.pack()), styles);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -706,7 +698,7 @@ impl<'a> ParBuilder<'a> {
|
|||||||
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
|
||||||
if content.is::<MetaElem>() {
|
if content.is::<MetaElem>() {
|
||||||
if self.0.has_strong_elements(false) {
|
if self.0.has_strong_elements(false) {
|
||||||
self.0.push(Cow::Borrowed(content), styles);
|
self.0.push(content, styles);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if content.is::<SpaceElem>()
|
} else if content.is::<SpaceElem>()
|
||||||
@ -719,7 +711,7 @@ impl<'a> ParBuilder<'a> {
|
|||||||
.map_or(false, |elem| !elem.block(styles))
|
.map_or(false, |elem| !elem.block(styles))
|
||||||
|| content.is::<BoxElem>()
|
|| content.is::<BoxElem>()
|
||||||
{
|
{
|
||||||
self.0.push(Cow::Borrowed(content), styles);
|
self.0.push(content, styles);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,16 +719,15 @@ impl<'a> ParBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self) -> (Content, StyleChain<'a>) {
|
fn finish(self) -> (Content, StyleChain<'a>) {
|
||||||
let (children, shared) = self.0.finish();
|
let (children, shared, span) = self.0.finish();
|
||||||
let span = first_span(&children);
|
(ParElem::new(children).pack().spanned(span), shared)
|
||||||
(ParElem::new(children.to_vec()).pack().spanned(span), shared)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts list / enum items, spaces, paragraph breaks.
|
/// Accepts list / enum items, spaces, paragraph breaks.
|
||||||
struct ListBuilder<'a> {
|
struct ListBuilder<'a> {
|
||||||
/// The list items collected so far.
|
/// The list items collected so far.
|
||||||
items: StyleVecBuilder<'a, Cow<'a, Content>>,
|
items: BehavedBuilder<'a>,
|
||||||
/// Whether the list contains no paragraph breaks.
|
/// Whether the list contains no paragraph breaks.
|
||||||
tight: bool,
|
tight: bool,
|
||||||
/// Trailing content for which it is unclear whether it is part of the list.
|
/// 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>())
|
|| content.is::<TermItem>())
|
||||||
&& self
|
&& self
|
||||||
.items
|
.items
|
||||||
.elems()
|
.items()
|
||||||
.next()
|
.next()
|
||||||
.map_or(true, |first| first.func() == content.func())
|
.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>());
|
self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakElem>());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -770,53 +761,50 @@ impl<'a> ListBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self) -> (Content, StyleChain<'a>) {
|
fn finish(self) -> (Content, StyleChain<'a>) {
|
||||||
let (items, shared) = self.items.finish();
|
let (items, shared, span) = self.items.finish_iter();
|
||||||
let span = first_span(&items);
|
let mut items = items.peekable();
|
||||||
let item = items.items().next().unwrap();
|
let (first, _) = items.peek().unwrap();
|
||||||
let output = if item.is::<ListItem>() {
|
let output = if first.is::<ListItem>() {
|
||||||
ListElem::new(
|
ListElem::new(
|
||||||
items
|
items
|
||||||
.iter()
|
|
||||||
.map(|(item, local)| {
|
.map(|(item, local)| {
|
||||||
let mut item = item.to_packed::<ListItem>().unwrap().clone();
|
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.push_body(body);
|
||||||
item
|
item
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.with_tight(self.tight)
|
.with_tight(self.tight)
|
||||||
.pack()
|
.pack()
|
||||||
.spanned(span)
|
.spanned(span)
|
||||||
} else if item.is::<EnumItem>() {
|
} else if first.is::<EnumItem>() {
|
||||||
EnumElem::new(
|
EnumElem::new(
|
||||||
items
|
items
|
||||||
.iter()
|
|
||||||
.map(|(item, local)| {
|
.map(|(item, local)| {
|
||||||
let mut item = item.to_packed::<EnumItem>().unwrap().clone();
|
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.push_body(body);
|
||||||
item
|
item
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.with_tight(self.tight)
|
.with_tight(self.tight)
|
||||||
.pack()
|
.pack()
|
||||||
.spanned(span)
|
.spanned(span)
|
||||||
} else if item.is::<TermItem>() {
|
} else if first.is::<TermItem>() {
|
||||||
TermsElem::new(
|
TermsElem::new(
|
||||||
items
|
items
|
||||||
.iter()
|
|
||||||
.map(|(item, local)| {
|
.map(|(item, local)| {
|
||||||
let mut item = item.to_packed::<TermItem>().unwrap().clone();
|
let mut item = item.to_packed::<TermItem>().unwrap().clone();
|
||||||
let term = item.term().clone().styled_with_map(local.clone());
|
let term = item.term().clone().styled_with_map(local.clone());
|
||||||
let description =
|
let description =
|
||||||
item.description().clone().styled_with_map(local.clone());
|
item.description().clone().styled_with_map(local);
|
||||||
item.push_term(term);
|
item.push_term(term);
|
||||||
item.push_description(description);
|
item.push_description(description);
|
||||||
item
|
item
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.with_tight(self.tight)
|
.with_tight(self.tight)
|
||||||
.pack()
|
.pack()
|
||||||
@ -831,7 +819,7 @@ impl<'a> ListBuilder<'a> {
|
|||||||
impl Default for ListBuilder<'_> {
|
impl Default for ListBuilder<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
items: StyleVecBuilder::default(),
|
items: BehavedBuilder::default(),
|
||||||
tight: true,
|
tight: true,
|
||||||
staged: vec![],
|
staged: vec![],
|
||||||
}
|
}
|
||||||
@ -875,16 +863,3 @@ impl<'a> CiteGroupBuilder<'a> {
|
|||||||
(CiteGroup::new(self.items).pack().spanned(span), self.styles)
|
(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)
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user