Split behaved building and StyleVec building (#4782)

This commit is contained in:
Laurenz 2024-08-18 12:22:23 +02:00 committed by GitHub
parent e11ad7d82f
commit eb0e0fec00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 110 additions and 102 deletions

View File

@ -246,7 +246,7 @@ impl LayoutMath for Content {
self.sequence_recursive_for_each(&mut |child: &Content| {
bb.push(child, StyleChain::default());
});
for (child, _) in bb.finish().0.chain(&styles) {
for (child, _) in bb.finish() {
child.layout_math(ctx, styles)?;
}
return Ok(());

View File

@ -5,7 +5,6 @@ use std::fmt::{Debug, Formatter};
use ecow::EcoVec;
use crate::foundations::{Content, StyleChain, Styles};
use crate::syntax::Span;
/// How an element interacts with other elements in a stream.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@ -129,39 +128,9 @@ impl<'a> BehavedBuilder<'a> {
/// Return the built content (possibly styled with local styles) plus a
/// trunk style chain and a span for the collection.
pub fn finish(mut self) -> (StyleVec, StyleChain<'a>, Span) {
pub fn finish(mut self) -> Vec<(&'a Content, StyleChain<'a>)> {
self.trim_weak();
let span = self.determine_span();
let (trunk, depth) = self.determine_style_trunk();
let mut elements = EcoVec::with_capacity(self.buf.len());
let mut styles = EcoVec::<(Styles, usize)>::new();
let mut last: Option<(StyleChain<'a>, usize)> = None;
for (element, chain) in self.buf.into_iter() {
elements.push(element.clone());
if let Some((prev, run)) = &mut last {
if chain == *prev {
*run += 1;
} else {
styles.push((prev.suffix(depth), *run));
last = Some((chain, 1));
}
} else {
last = Some((chain, 1));
}
}
if let Some((last, run)) = last {
let skippable = styles.is_empty() && last == trunk;
if !skippable {
styles.push((last.suffix(depth), run));
}
}
(StyleVec { elements, styles }, trunk, span)
self.buf
}
/// Trim a possibly remaining weak item.
@ -176,50 +145,6 @@ impl<'a> BehavedBuilder<'a> {
fn find_last_weak(&self) -> Option<usize> {
self.buf.iter().rposition(|(c, _)| c.behaviour().is_weak())
}
/// Determine a span for the built collection.
fn determine_span(&self) -> Span {
let mut span = Span::detached();
for &(content, _) in &self.buf {
span = content.span();
if !span.is_detached() {
break;
}
}
span
}
/// Determine the shared trunk style chain.
fn determine_style_trunk(&self) -> (StyleChain<'a>, usize) {
// Determine shared style depth and first span.
let mut trunk = match self.buf.first() {
Some(&(_, chain)) => chain,
None => Default::default(),
};
let mut depth = trunk.links().count();
for (_, mut chain) in &self.buf {
let len = chain.links().count();
if len < depth {
for _ in 0..depth - len {
trunk.pop();
}
depth = len;
} else if len > depth {
for _ in 0..len - depth {
chain.pop();
}
}
while depth > 0 && chain != trunk {
trunk.pop();
chain.pop();
depth -= 1;
}
}
(trunk, depth)
}
}
impl<'a> Default for BehavedBuilder<'a> {
@ -247,6 +172,39 @@ impl StyleVec {
Self { elements, styles: EcoVec::new() }
}
/// Create a `StyleVec` from a list of content with style chains.
pub fn create<'a>(buf: &[(&Content, StyleChain<'a>)]) -> (Self, StyleChain<'a>) {
let (trunk, depth) = determine_style_trunk(buf);
let mut elements = EcoVec::with_capacity(buf.len());
let mut styles = EcoVec::<(Styles, usize)>::new();
let mut last: Option<(StyleChain<'a>, usize)> = None;
for &(element, chain) in buf {
elements.push(element.clone());
if let Some((prev, run)) = &mut last {
if chain == *prev {
*run += 1;
} else {
styles.push((prev.suffix(depth), *run));
last = Some((chain, 1));
}
} else {
last = Some((chain, 1));
}
}
if let Some((last, run)) = last {
let skippable = styles.is_empty() && last == trunk;
if !skippable {
styles.push((last.suffix(depth), run));
}
}
(StyleVec { elements, styles }, trunk)
}
/// Whether there are no elements.
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
@ -321,3 +279,35 @@ impl Debug for StyleVec {
.finish()
}
}
/// Determine the shared trunk style chain.
fn determine_style_trunk<'a, T>(buf: &[(T, StyleChain<'a>)]) -> (StyleChain<'a>, usize) {
// Determine shared style depth and first span.
let mut trunk = match buf.first() {
Some(&(_, chain)) => chain,
None => Default::default(),
};
let mut depth = trunk.links().count();
for (_, mut chain) in 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

@ -36,6 +36,9 @@ use crate::model::{
use crate::syntax::Span;
use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
/// A pair of content and a style chain that applies to it.
type Pair<'a> = (&'a Content, StyleChain<'a>);
/// Realize at the root-level.
#[typst_macros::time(name = "realize root")]
pub fn realize_root<'a>(
@ -351,7 +354,8 @@ impl<'a> DocBuilder<'a> {
/// Turns this builder into the resulting page runs, along with
/// its [style chain][StyleChain].
fn finish(self) -> (StyleVec, StyleChain<'a>, DocumentInfo) {
let (children, trunk, _) = self.pages.finish();
let buf = self.pages.finish();
let (children, trunk) = StyleVec::create(&buf);
(children, trunk, self.info)
}
}
@ -442,7 +446,9 @@ impl<'a> FlowBuilder<'a> {
/// Turns this builder into the resulting flow, along with
/// its [style chain][StyleChain].
fn finish(self) -> (Packed<FlowElem>, StyleChain<'a>) {
let (children, trunk, span) = self.0.finish();
let buf = self.0.finish();
let span = determine_span(&buf);
let (children, trunk) = StyleVec::create(&buf);
(Packed::new(FlowElem::new(children)).spanned(span), trunk)
}
}
@ -482,7 +488,9 @@ impl<'a> ParBuilder<'a> {
/// Turns this builder into the resulting paragraph, along with
/// its [style chain][StyleChain].
fn finish(self) -> (Packed<ParElem>, StyleChain<'a>) {
let (children, trunk, span) = self.0.finish();
let buf = self.0.finish();
let span = determine_span(&buf);
let (children, trunk) = StyleVec::create(&buf);
(Packed::new(ParElem::new(children)).spanned(span), trunk)
}
}
@ -491,11 +499,11 @@ impl<'a> ParBuilder<'a> {
/// from list or enum items, spaces, and paragraph breaks.
struct ListBuilder<'a> {
/// The list items collected so far.
items: BehavedBuilder<'a>,
items: Vec<Pair<'a>>,
/// Trailing content for which it is unclear whether it is part of the list.
staged: Vec<Pair<'a>>,
/// Whether the list contains no paragraph breaks.
tight: bool,
/// Trailing content for which it is unclear whether it is part of the list.
staged: Vec<(&'a Content, StyleChain<'a>)>,
}
impl<'a> ListBuilder<'a> {
@ -518,11 +526,10 @@ impl<'a> ListBuilder<'a> {
|| content.is::<TermItem>())
&& self
.items
.items()
.next()
.map_or(true, |first| first.func() == content.func())
.first()
.map_or(true, |(first, _)| first.func() == content.func())
{
self.items.push(content, styles);
self.items.push((content, styles));
self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakElem>());
return true;
}
@ -533,25 +540,27 @@ impl<'a> ListBuilder<'a> {
/// Turns this builder into the resulting list, along with
/// its [style chain][StyleChain].
fn finish(self) -> (Content, StyleChain<'a>) {
let (items, trunk, span) = self.items.finish();
let mut items = items.into_iter().peekable();
let (first, _) = items.peek().unwrap();
let span = determine_span(&self.items);
let (children, trunk) = StyleVec::create(&self.items);
let mut iter = children.into_iter().peekable();
let (first, _) = iter.peek().unwrap();
let output = if first.is::<ListItem>() {
let children = items
let children = iter
.map(|(item, local)| {
item.into_packed::<ListItem>().unwrap().styled(local)
})
.collect();
ListElem::new(children).with_tight(self.tight).pack().spanned(span)
} else if first.is::<EnumItem>() {
let children = items
let children = iter
.map(|(item, local)| {
item.into_packed::<EnumItem>().unwrap().styled(local)
})
.collect();
EnumElem::new(children).with_tight(self.tight).pack().spanned(span)
} else if first.is::<TermItem>() {
let children = items
let children = iter
.map(|(item, local)| {
item.into_packed::<TermItem>().unwrap().styled(local)
})
@ -560,29 +569,26 @@ impl<'a> ListBuilder<'a> {
} else {
unreachable!()
};
(output, trunk)
}
}
impl Default for ListBuilder<'_> {
fn default() -> Self {
Self {
items: BehavedBuilder::default(),
tight: true,
staged: vec![],
}
Self { items: vec![], staged: vec![], tight: true }
}
}
/// Builds a [citation group][CiteGroup] from citations.
#[derive(Default)]
struct CiteGroupBuilder<'a> {
/// The styles.
styles: StyleChain<'a>,
/// The citations.
items: Vec<Packed<CiteElem>>,
/// Trailing content for which it is unclear whether it is part of the list.
staged: Vec<(&'a Content, StyleChain<'a>)>,
staged: Vec<Pair<'a>>,
/// The styles.
styles: StyleChain<'a>,
}
impl<'a> CiteGroupBuilder<'a> {
@ -619,3 +625,15 @@ impl<'a> CiteGroupBuilder<'a> {
(Packed::new(CiteGroup::new(self.items)).spanned(span), self.styles)
}
}
/// Determine a span for the built collection.
fn determine_span(buf: &[(&Content, StyleChain)]) -> Span {
let mut span = Span::detached();
for &(content, _) in buf {
span = content.span();
if !span.is_detached() {
break;
}
}
span
}