mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +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| {
|
self.sequence_recursive_for_each(&mut |child: &Content| {
|
||||||
bb.push(child, StyleChain::default());
|
bb.push(child, StyleChain::default());
|
||||||
});
|
});
|
||||||
for (child, _) in bb.finish().0.chain(&styles) {
|
for (child, _) in bb.finish() {
|
||||||
child.layout_math(ctx, styles)?;
|
child.layout_math(ctx, styles)?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -5,7 +5,6 @@ use std::fmt::{Debug, Formatter};
|
|||||||
use ecow::EcoVec;
|
use ecow::EcoVec;
|
||||||
|
|
||||||
use crate::foundations::{Content, StyleChain, Styles};
|
use crate::foundations::{Content, StyleChain, Styles};
|
||||||
use crate::syntax::Span;
|
|
||||||
|
|
||||||
/// How an element interacts with other elements in a stream.
|
/// How an element interacts with other elements in a stream.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[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
|
/// Return the built content (possibly styled with local styles) plus a
|
||||||
/// trunk style chain and a span for the collection.
|
/// 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();
|
self.trim_weak();
|
||||||
|
self.buf
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trim a possibly remaining weak item.
|
/// Trim a possibly remaining weak item.
|
||||||
@ -176,50 +145,6 @@ impl<'a> BehavedBuilder<'a> {
|
|||||||
fn find_last_weak(&self) -> Option<usize> {
|
fn find_last_weak(&self) -> Option<usize> {
|
||||||
self.buf.iter().rposition(|(c, _)| c.behaviour().is_weak())
|
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> {
|
impl<'a> Default for BehavedBuilder<'a> {
|
||||||
@ -247,6 +172,39 @@ impl StyleVec {
|
|||||||
Self { elements, styles: EcoVec::new() }
|
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.
|
/// Whether there are no elements.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.elements.is_empty()
|
self.elements.is_empty()
|
||||||
@ -321,3 +279,35 @@ impl Debug for StyleVec {
|
|||||||
.finish()
|
.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::syntax::Span;
|
||||||
use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
|
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.
|
/// Realize at the root-level.
|
||||||
#[typst_macros::time(name = "realize root")]
|
#[typst_macros::time(name = "realize root")]
|
||||||
pub fn realize_root<'a>(
|
pub fn realize_root<'a>(
|
||||||
@ -351,7 +354,8 @@ impl<'a> DocBuilder<'a> {
|
|||||||
/// Turns this builder into the resulting page runs, along with
|
/// Turns this builder into the resulting page runs, along with
|
||||||
/// its [style chain][StyleChain].
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (StyleVec, StyleChain<'a>, DocumentInfo) {
|
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)
|
(children, trunk, self.info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -442,7 +446,9 @@ impl<'a> FlowBuilder<'a> {
|
|||||||
/// Turns this builder into the resulting flow, along with
|
/// Turns this builder into the resulting flow, along with
|
||||||
/// its [style chain][StyleChain].
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Packed<FlowElem>, StyleChain<'a>) {
|
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)
|
(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
|
/// Turns this builder into the resulting paragraph, along with
|
||||||
/// its [style chain][StyleChain].
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Packed<ParElem>, StyleChain<'a>) {
|
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)
|
(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.
|
/// from list or enum items, spaces, and paragraph breaks.
|
||||||
struct ListBuilder<'a> {
|
struct ListBuilder<'a> {
|
||||||
/// The list items collected so far.
|
/// 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.
|
/// 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.
|
|
||||||
staged: Vec<(&'a Content, StyleChain<'a>)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ListBuilder<'a> {
|
impl<'a> ListBuilder<'a> {
|
||||||
@ -518,11 +526,10 @@ impl<'a> ListBuilder<'a> {
|
|||||||
|| content.is::<TermItem>())
|
|| content.is::<TermItem>())
|
||||||
&& self
|
&& self
|
||||||
.items
|
.items
|
||||||
.items()
|
.first()
|
||||||
.next()
|
.map_or(true, |(first, _)| first.func() == content.func())
|
||||||
.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>());
|
self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakElem>());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -533,25 +540,27 @@ impl<'a> ListBuilder<'a> {
|
|||||||
/// Turns this builder into the resulting list, along with
|
/// Turns this builder into the resulting list, along with
|
||||||
/// its [style chain][StyleChain].
|
/// its [style chain][StyleChain].
|
||||||
fn finish(self) -> (Content, StyleChain<'a>) {
|
fn finish(self) -> (Content, StyleChain<'a>) {
|
||||||
let (items, trunk, span) = self.items.finish();
|
let span = determine_span(&self.items);
|
||||||
let mut items = items.into_iter().peekable();
|
let (children, trunk) = StyleVec::create(&self.items);
|
||||||
let (first, _) = items.peek().unwrap();
|
|
||||||
|
let mut iter = children.into_iter().peekable();
|
||||||
|
let (first, _) = iter.peek().unwrap();
|
||||||
let output = if first.is::<ListItem>() {
|
let output = if first.is::<ListItem>() {
|
||||||
let children = items
|
let children = iter
|
||||||
.map(|(item, local)| {
|
.map(|(item, local)| {
|
||||||
item.into_packed::<ListItem>().unwrap().styled(local)
|
item.into_packed::<ListItem>().unwrap().styled(local)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
ListElem::new(children).with_tight(self.tight).pack().spanned(span)
|
ListElem::new(children).with_tight(self.tight).pack().spanned(span)
|
||||||
} else if first.is::<EnumItem>() {
|
} else if first.is::<EnumItem>() {
|
||||||
let children = items
|
let children = iter
|
||||||
.map(|(item, local)| {
|
.map(|(item, local)| {
|
||||||
item.into_packed::<EnumItem>().unwrap().styled(local)
|
item.into_packed::<EnumItem>().unwrap().styled(local)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
EnumElem::new(children).with_tight(self.tight).pack().spanned(span)
|
EnumElem::new(children).with_tight(self.tight).pack().spanned(span)
|
||||||
} else if first.is::<TermItem>() {
|
} else if first.is::<TermItem>() {
|
||||||
let children = items
|
let children = iter
|
||||||
.map(|(item, local)| {
|
.map(|(item, local)| {
|
||||||
item.into_packed::<TermItem>().unwrap().styled(local)
|
item.into_packed::<TermItem>().unwrap().styled(local)
|
||||||
})
|
})
|
||||||
@ -560,29 +569,26 @@ impl<'a> ListBuilder<'a> {
|
|||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
(output, trunk)
|
(output, trunk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ListBuilder<'_> {
|
impl Default for ListBuilder<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self { items: vec![], staged: vec![], tight: true }
|
||||||
items: BehavedBuilder::default(),
|
|
||||||
tight: true,
|
|
||||||
staged: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a [citation group][CiteGroup] from citations.
|
/// Builds a [citation group][CiteGroup] from citations.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct CiteGroupBuilder<'a> {
|
struct CiteGroupBuilder<'a> {
|
||||||
/// The styles.
|
|
||||||
styles: StyleChain<'a>,
|
|
||||||
/// The citations.
|
/// The citations.
|
||||||
items: Vec<Packed<CiteElem>>,
|
items: Vec<Packed<CiteElem>>,
|
||||||
/// 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.
|
||||||
staged: Vec<(&'a Content, StyleChain<'a>)>,
|
staged: Vec<Pair<'a>>,
|
||||||
|
/// The styles.
|
||||||
|
styles: StyleChain<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CiteGroupBuilder<'a> {
|
impl<'a> CiteGroupBuilder<'a> {
|
||||||
@ -619,3 +625,15 @@ impl<'a> CiteGroupBuilder<'a> {
|
|||||||
(Packed::new(CiteGroup::new(self.items)).spanned(span), self.styles)
|
(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