mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Split behaved building and StyleVec building (#4782)
This commit is contained in:
parent
e11ad7d82f
commit
eb0e0fec00
@ -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(());
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user