summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-08-18 12:22:23 +0200
committerGitHub <noreply@github.com>2024-08-18 10:22:23 +0000
commiteb0e0fec00e08c497a428d138c47a1235c89295f (patch)
treee810df458fbe656c9d9bdbc5a38e9688065dbea1
parente11ad7d82fad413e7a7fb5bcce4f9b3647b1c79d (diff)
Split behaved building and StyleVec building (#4782)
-rw-r--r--crates/typst/src/math/mod.rs2
-rw-r--r--crates/typst/src/realize/behaviour.rs144
-rw-r--r--crates/typst/src/realize/mod.rs66
3 files changed, 110 insertions, 102 deletions
diff --git a/crates/typst/src/math/mod.rs b/crates/typst/src/math/mod.rs
index 966945ce..6cf22edb 100644
--- a/crates/typst/src/math/mod.rs
+++ b/crates/typst/src/math/mod.rs
@@ -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(());
diff --git a/crates/typst/src/realize/behaviour.rs b/crates/typst/src/realize/behaviour.rs
index 945d2f2a..588e221f 100644
--- a/crates/typst/src/realize/behaviour.rs
+++ b/crates/typst/src/realize/behaviour.rs
@@ -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)
+}
diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs
index eb968605..fa7019e0 100644
--- a/crates/typst/src/realize/mod.rs
+++ b/crates/typst/src/realize/mod.rs
@@ -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
+}