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::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 {
|
||||
|
@ -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(_))
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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(());
|
||||
|
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.
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user