mirror of
https://github.com/typst/typst
synced 2025-05-20 12:05:27 +08:00
Bring back StyleVec
(#4323)
This commit is contained in:
parent
755dd4112d
commit
3257efd03a
@ -72,8 +72,8 @@ pub struct Styles(EcoVec<LazyHash<Style>>);
|
|||||||
|
|
||||||
impl Styles {
|
impl Styles {
|
||||||
/// Create a new, empty style list.
|
/// Create a new, empty style list.
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self::default()
|
Self(EcoVec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this contains no styles.
|
/// Whether this contains no styles.
|
||||||
|
@ -9,7 +9,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
elem, Content, NativeElement, Packed, Resolve, Smart, StyleChain, StyledElem,
|
elem, Args, Construct, Content, NativeElement, Packed, Resolve, Smart, StyleChain,
|
||||||
};
|
};
|
||||||
use crate::introspection::TagElem;
|
use crate::introspection::TagElem;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
@ -17,17 +17,25 @@ use crate::layout::{
|
|||||||
Fragment, Frame, FrameItem, PlaceElem, Point, Regions, Rel, Size, Spacing, VElem,
|
Fragment, Frame, FrameItem, PlaceElem, Point, Regions, Rel, Size, Spacing, VElem,
|
||||||
};
|
};
|
||||||
use crate::model::{FootnoteElem, FootnoteEntry, ParElem};
|
use crate::model::{FootnoteElem, FootnoteEntry, ParElem};
|
||||||
|
use crate::realize::StyleVec;
|
||||||
use crate::utils::Numeric;
|
use crate::utils::Numeric;
|
||||||
|
|
||||||
/// Arranges spacing, paragraphs and block-level elements into a flow.
|
/// Arranges spacing, paragraphs and block-level elements into a flow.
|
||||||
///
|
///
|
||||||
/// This element is responsible for layouting both the top-level content flow
|
/// This element is responsible for layouting both the top-level content flow
|
||||||
/// and the contents of boxes.
|
/// and the contents of boxes.
|
||||||
#[elem(Debug)]
|
#[elem(Debug, Construct)]
|
||||||
pub struct FlowElem {
|
pub struct FlowElem {
|
||||||
/// The children that will be arranged into a flow.
|
/// The children that will be arranged into a flow.
|
||||||
|
#[internal]
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<Content>,
|
pub children: StyleVec,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Construct for FlowElem {
|
||||||
|
fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
bail!(args.span, "cannot be constructed manually");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Packed<FlowElem> {
|
impl Packed<FlowElem> {
|
||||||
@ -54,23 +62,12 @@ impl Packed<FlowElem> {
|
|||||||
// through the block & pad and reach the innermost flow, so that things
|
// through the block & pad and reach the innermost flow, so that things
|
||||||
// are properly bottom-aligned.
|
// are properly bottom-aligned.
|
||||||
let mut alone = false;
|
let mut alone = false;
|
||||||
if let [child] = self.children().as_slice() {
|
if let [child] = self.children().elements() {
|
||||||
alone = child
|
alone = child.is::<BlockElem>();
|
||||||
.to_packed::<StyledElem>()
|
|
||||||
.map_or(child, |styled| &styled.child)
|
|
||||||
.is::<BlockElem>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let outer = styles;
|
|
||||||
|
|
||||||
let mut layouter = FlowLayouter::new(regions, styles, alone);
|
let mut layouter = FlowLayouter::new(regions, styles, alone);
|
||||||
for mut child in self.children().iter() {
|
for (child, styles) in self.children().chain(&styles) {
|
||||||
let mut styles = styles;
|
|
||||||
if let Some(styled) = child.to_packed::<StyledElem>() {
|
|
||||||
child = &styled.child;
|
|
||||||
styles = outer.chain(&styled.styles);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(elem) = child.to_packed::<TagElem>() {
|
if let Some(elem) = child.to_packed::<TagElem>() {
|
||||||
layouter.layout_tag(elem);
|
layouter.layout_tag(elem);
|
||||||
} else if child.is::<FlushElem>() {
|
} else if child.is::<FlushElem>() {
|
||||||
@ -100,7 +97,7 @@ impl Packed<FlowElem> {
|
|||||||
impl Debug for FlowElem {
|
impl Debug for FlowElem {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "Flow ")?;
|
write!(f, "Flow ")?;
|
||||||
f.debug_list().entries(&self.children).finish()
|
self.children.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,13 +13,14 @@ use self::shaping::{
|
|||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::{Engine, Route};
|
use crate::engine::{Engine, Route};
|
||||||
use crate::eval::Tracer;
|
use crate::eval::Tracer;
|
||||||
use crate::foundations::{Content, Packed, Resolve, Smart, StyleChain, StyledElem};
|
use crate::foundations::{Packed, Resolve, Smart, StyleChain};
|
||||||
use crate::introspection::{Introspector, Locator, TagElem};
|
use crate::introspection::{Introspector, Locator, TagElem};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, BoxElem, Dir, Em, FixedAlignment, Fr, Fragment, Frame, FrameItem,
|
Abs, AlignElem, BoxElem, Dir, Em, FixedAlignment, Fr, Fragment, Frame, FrameItem,
|
||||||
HElem, InlineElem, InlineItem, Point, Size, Sizing, Spacing,
|
HElem, InlineElem, InlineItem, Point, Size, Sizing, Spacing,
|
||||||
};
|
};
|
||||||
use crate::model::{Linebreaks, ParElem};
|
use crate::model::{Linebreaks, ParElem};
|
||||||
|
use crate::realize::StyleVec;
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::text::{
|
use crate::text::{
|
||||||
Costs, Lang, LinebreakElem, SmartQuoteElem, SmartQuoter, SmartQuotes, SpaceElem,
|
Costs, Lang, LinebreakElem, SmartQuoteElem, SmartQuoter, SmartQuotes, SpaceElem,
|
||||||
@ -30,7 +31,7 @@ use crate::World;
|
|||||||
|
|
||||||
/// Layouts content inline.
|
/// Layouts content inline.
|
||||||
pub(crate) fn layout_inline(
|
pub(crate) fn layout_inline(
|
||||||
children: &[Content],
|
children: &StyleVec,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
consecutive: bool,
|
consecutive: bool,
|
||||||
@ -40,7 +41,7 @@ pub(crate) fn layout_inline(
|
|||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn cached(
|
fn cached(
|
||||||
children: &[Content],
|
children: &StyleVec,
|
||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
introspector: Tracked<Introspector>,
|
introspector: Tracked<Introspector>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
@ -428,14 +429,14 @@ impl<'a> Line<'a> {
|
|||||||
/// Collect all text of the paragraph into one string and layout equations. This
|
/// Collect all text of the paragraph into one string and layout equations. This
|
||||||
/// also performs string-level preprocessing like case transformations.
|
/// also performs string-level preprocessing like case transformations.
|
||||||
fn collect<'a>(
|
fn collect<'a>(
|
||||||
children: &'a [Content],
|
children: &'a StyleVec,
|
||||||
engine: &mut Engine<'_>,
|
engine: &mut Engine<'_>,
|
||||||
styles: &'a StyleChain<'a>,
|
styles: &'a StyleChain<'a>,
|
||||||
region: Size,
|
region: Size,
|
||||||
consecutive: bool,
|
consecutive: bool,
|
||||||
) -> SourceResult<(String, Vec<Segment<'a>>, SpanMapper)> {
|
) -> SourceResult<(String, Vec<Segment<'a>>, SpanMapper)> {
|
||||||
let mut collector = Collector::new(2 + children.len());
|
let mut collector = Collector::new(2 + children.len());
|
||||||
let mut iter = children.iter().peekable();
|
let mut iter = children.chain(styles).peekable();
|
||||||
|
|
||||||
let first_line_indent = ParElem::first_line_indent_in(*styles);
|
let first_line_indent = ParElem::first_line_indent_in(*styles);
|
||||||
if !first_line_indent.is_zero()
|
if !first_line_indent.is_zero()
|
||||||
@ -455,14 +456,7 @@ fn collect<'a>(
|
|||||||
|
|
||||||
let outer_dir = TextElem::dir_in(*styles);
|
let outer_dir = TextElem::dir_in(*styles);
|
||||||
|
|
||||||
while let Some(mut child) = iter.next() {
|
while let Some((child, styles)) = iter.next() {
|
||||||
let outer = styles;
|
|
||||||
let mut styles = *styles;
|
|
||||||
if let Some(styled) = child.to_packed::<StyledElem>() {
|
|
||||||
child = &styled.child;
|
|
||||||
styles = outer.chain(&styled.styles);
|
|
||||||
}
|
|
||||||
|
|
||||||
let prev_len = collector.full.len();
|
let prev_len = collector.full.len();
|
||||||
|
|
||||||
if child.is::<SpaceElem>() {
|
if child.is::<SpaceElem>() {
|
||||||
@ -515,12 +509,7 @@ fn collect<'a>(
|
|||||||
TextElem::region_in(styles),
|
TextElem::region_in(styles),
|
||||||
elem.alternative(styles),
|
elem.alternative(styles),
|
||||||
);
|
);
|
||||||
let peeked = iter.peek().and_then(|&child| {
|
let peeked = iter.peek().and_then(|(child, _)| {
|
||||||
let child = if let Some(styled) = child.to_packed::<StyledElem>() {
|
|
||||||
&styled.child
|
|
||||||
} else {
|
|
||||||
child
|
|
||||||
};
|
|
||||||
if let Some(elem) = child.to_packed::<TextElem>() {
|
if let Some(elem) = child.to_packed::<TextElem>() {
|
||||||
elem.text().chars().next()
|
elem.text().chars().next()
|
||||||
} else if child.is::<SmartQuoteElem>() {
|
} else if child.is::<SmartQuoteElem>() {
|
||||||
@ -642,7 +631,7 @@ impl<'a> Collector<'a> {
|
|||||||
/// Prepare paragraph layout by shaping the whole paragraph.
|
/// Prepare paragraph layout by shaping the whole paragraph.
|
||||||
fn prepare<'a>(
|
fn prepare<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
children: &'a [Content],
|
children: &'a StyleVec,
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
segments: Vec<Segment<'a>>,
|
segments: Vec<Segment<'a>>,
|
||||||
spans: SpanMapper,
|
spans: SpanMapper,
|
||||||
@ -682,9 +671,9 @@ fn prepare<'a>(
|
|||||||
bidi,
|
bidi,
|
||||||
items,
|
items,
|
||||||
spans,
|
spans,
|
||||||
hyphenate: shared_get(styles, children, TextElem::hyphenate_in),
|
hyphenate: children.shared_get(styles, TextElem::hyphenate_in),
|
||||||
costs: TextElem::costs_in(styles),
|
costs: TextElem::costs_in(styles),
|
||||||
lang: shared_get(styles, children, TextElem::lang_in),
|
lang: children.shared_get(styles, TextElem::lang_in),
|
||||||
align: AlignElem::alignment_in(styles).resolve(styles).x,
|
align: AlignElem::alignment_in(styles).resolve(styles).x,
|
||||||
justify: ParElem::justify_in(styles),
|
justify: ParElem::justify_in(styles),
|
||||||
hang: ParElem::hanging_indent_in(styles),
|
hang: ParElem::hanging_indent_in(styles),
|
||||||
@ -815,21 +804,6 @@ fn is_compatible(a: Script, b: Script) -> bool {
|
|||||||
is_generic_script(a) || is_generic_script(b) || a == b
|
is_generic_script(a) || is_generic_script(b) || a == b
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a style property, but only if it is the same for all children of the
|
|
||||||
/// paragraph.
|
|
||||||
fn shared_get<T: PartialEq>(
|
|
||||||
styles: StyleChain<'_>,
|
|
||||||
children: &[Content],
|
|
||||||
getter: fn(StyleChain) -> T,
|
|
||||||
) -> Option<T> {
|
|
||||||
let value = getter(styles);
|
|
||||||
children
|
|
||||||
.iter()
|
|
||||||
.filter_map(|child| child.to_packed::<StyledElem>())
|
|
||||||
.all(|styled| getter(styles.chain(&styled.styles)) == value)
|
|
||||||
.then_some(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find suitable linebreaks.
|
/// Find suitable linebreaks.
|
||||||
fn linebreak<'a>(engine: &Engine, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
fn linebreak<'a>(engine: &Engine, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
||||||
let linebreaks = p.linebreaks.unwrap_or_else(|| {
|
let linebreaks = p.linebreaks.unwrap_or_else(|| {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::f64::consts::SQRT_2;
|
use std::f64::consts::SQRT_2;
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::{eco_vec, EcoString};
|
||||||
use rustybuzz::Feature;
|
use rustybuzz::Feature;
|
||||||
use ttf_parser::gsub::{AlternateSubstitution, SingleSubstitution, SubstitutionSubtable};
|
use ttf_parser::gsub::{AlternateSubstitution, SingleSubstitution, SubstitutionSubtable};
|
||||||
use ttf_parser::math::MathValue;
|
use ttf_parser::math::MathValue;
|
||||||
@ -18,6 +18,7 @@ use crate::math::{
|
|||||||
LayoutMath, MathFragment, MathRun, MathSize, THICK,
|
LayoutMath, MathFragment, MathRun, MathSize, THICK,
|
||||||
};
|
};
|
||||||
use crate::model::ParElem;
|
use crate::model::ParElem;
|
||||||
|
use crate::realize::StyleVec;
|
||||||
use crate::syntax::{is_newline, Span};
|
use crate::syntax::{is_newline, Span};
|
||||||
use crate::text::{
|
use crate::text::{
|
||||||
features, BottomEdge, BottomEdgeMetric, Font, TextElem, TextSize, TopEdge,
|
features, BottomEdge, BottomEdgeMetric, Font, TextElem, TextSize, TopEdge,
|
||||||
@ -286,7 +287,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
|
|||||||
// to extend as far as needed.
|
// to extend as far as needed.
|
||||||
let spaced = text.graphemes(true).nth(1).is_some();
|
let spaced = text.graphemes(true).nth(1).is_some();
|
||||||
let text = TextElem::packed(text).spanned(span);
|
let text = TextElem::packed(text).spanned(span);
|
||||||
let par = ParElem::new(vec![text]);
|
let par = ParElem::new(StyleVec::wrap(eco_vec![text]));
|
||||||
let frame = Packed::new(par)
|
let frame = Packed::new(par)
|
||||||
.spanned(span)
|
.spanned(span)
|
||||||
.layout(self.engine, styles, false, Size::splat(Abs::inf()), false)?
|
.layout(self.engine, styles, false, Size::splat(Abs::inf()), false)?
|
||||||
|
@ -241,7 +241,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::<Content>().0 {
|
for (child, _) in bb.finish().0.chain(&styles) {
|
||||||
child.layout_math(ctx, styles)?;
|
child.layout_math(ctx, styles)?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -4,10 +4,11 @@ use crate::diag::{bail, SourceResult, StrResult};
|
|||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, Args, Array, Construct, Content, Datetime, Packed, Smart, StyleChain,
|
cast, elem, Args, Array, Construct, Content, Datetime, Packed, Smart, StyleChain,
|
||||||
StyledElem, Value,
|
Value,
|
||||||
};
|
};
|
||||||
use crate::introspection::{Introspector, ManualPageCounter};
|
use crate::introspection::{Introspector, ManualPageCounter};
|
||||||
use crate::layout::{Page, PageElem};
|
use crate::layout::{Page, PageElem};
|
||||||
|
use crate::realize::StyleVec;
|
||||||
|
|
||||||
/// The root element of a document and its metadata.
|
/// The root element of a document and its metadata.
|
||||||
///
|
///
|
||||||
@ -60,7 +61,7 @@ pub struct DocumentElem {
|
|||||||
/// The page runs.
|
/// The page runs.
|
||||||
#[internal]
|
#[internal]
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<Content>,
|
pub children: StyleVec,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Construct for DocumentElem {
|
impl Construct for DocumentElem {
|
||||||
@ -81,24 +82,13 @@ impl Packed<DocumentElem> {
|
|||||||
let mut page_counter = ManualPageCounter::new();
|
let mut page_counter = ManualPageCounter::new();
|
||||||
|
|
||||||
let children = self.children();
|
let children = self.children();
|
||||||
let mut iter = children.iter().peekable();
|
let mut iter = children.chain(&styles).peekable();
|
||||||
|
|
||||||
while let Some(mut child) = iter.next() {
|
|
||||||
let outer = styles;
|
|
||||||
let mut styles = styles;
|
|
||||||
if let Some(styled) = child.to_packed::<StyledElem>() {
|
|
||||||
child = &styled.child;
|
|
||||||
styles = outer.chain(&styled.styles);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while let Some((child, styles)) = iter.next() {
|
||||||
if let Some(page) = child.to_packed::<PageElem>() {
|
if let Some(page) = child.to_packed::<PageElem>() {
|
||||||
let extend_to = iter.peek().and_then(|&next| {
|
let extend_to = iter
|
||||||
*next
|
.peek()
|
||||||
.to_packed::<StyledElem>()
|
.and_then(|(next, _)| *next.to_packed::<PageElem>()?.clear_to()?);
|
||||||
.map_or(next, |styled| &styled.child)
|
|
||||||
.to_packed::<PageElem>()?
|
|
||||||
.clear_to()?
|
|
||||||
});
|
|
||||||
let run = page.layout(engine, styles, &mut page_counter, extend_to)?;
|
let run = page.layout(engine, styles, &mut page_counter, extend_to)?;
|
||||||
pages.extend(run);
|
pages.extend(run);
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,6 +7,7 @@ use crate::foundations::{
|
|||||||
Unlabellable,
|
Unlabellable,
|
||||||
};
|
};
|
||||||
use crate::layout::{Em, Fragment, Length, Size};
|
use crate::layout::{Em, Fragment, Length, Size};
|
||||||
|
use crate::realize::StyleVec;
|
||||||
|
|
||||||
/// Arranges text, spacing and inline-level elements into a paragraph.
|
/// Arranges text, spacing and inline-level elements into a paragraph.
|
||||||
///
|
///
|
||||||
@ -113,7 +114,7 @@ pub struct ParElem {
|
|||||||
/// The paragraph's children.
|
/// The paragraph's children.
|
||||||
#[internal]
|
#[internal]
|
||||||
#[variadic]
|
#[variadic]
|
||||||
pub children: Vec<Content>,
|
pub children: StyleVec,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Construct for ParElem {
|
impl Construct for ParElem {
|
||||||
@ -143,7 +144,7 @@ impl Packed<ParElem> {
|
|||||||
expand: bool,
|
expand: bool,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
crate::layout::layout_inline(
|
crate::layout::layout_inline(
|
||||||
self.children(),
|
&self.children,
|
||||||
engine,
|
engine,
|
||||||
styles,
|
styles,
|
||||||
consecutive,
|
consecutive,
|
||||||
@ -156,7 +157,7 @@ impl Packed<ParElem> {
|
|||||||
impl Debug for ParElem {
|
impl Debug for ParElem {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "Par ")?;
|
write!(f, "Par ")?;
|
||||||
f.debug_list().entries(&self.children).finish()
|
self.children.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
//! Element interaction.
|
//! Element interaction.
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
use ecow::EcoVec;
|
||||||
|
|
||||||
use crate::foundations::{Content, StyleChain, Styles};
|
use crate::foundations::{Content, StyleChain, Styles};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
|
|
||||||
@ -125,44 +129,39 @@ 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<F: From<Content>>(self) -> (Vec<F>, StyleChain<'a>, Span) {
|
pub fn finish(mut self) -> (StyleVec, 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();
|
self.trim_weak();
|
||||||
|
|
||||||
let span = self.determine_span();
|
let span = self.determine_span();
|
||||||
let (trunk, depth) = self.determine_style_trunk();
|
let (trunk, depth) = self.determine_style_trunk();
|
||||||
|
|
||||||
let mut iter = self.buf.into_iter().peekable();
|
let mut elements = EcoVec::with_capacity(self.buf.len());
|
||||||
let mut reuse = None;
|
let mut styles = EcoVec::<(Styles, usize)>::new();
|
||||||
|
let mut last: Option<(StyleChain<'a>, usize)> = None;
|
||||||
|
|
||||||
// Map the content + style chains to content + suffix maps, reusing
|
for (element, chain) in self.buf.into_iter() {
|
||||||
// equivalent adjacent suffix maps, if possible.
|
elements.push(element.clone());
|
||||||
let output = std::iter::from_fn(move || {
|
|
||||||
let (c, s) = iter.next()?;
|
|
||||||
|
|
||||||
// Try to reuse a suffix map that the previous element has
|
if let Some((prev, run)) = &mut last {
|
||||||
// stored for us.
|
if chain == *prev {
|
||||||
let suffix = reuse.take().unwrap_or_else(|| s.suffix(depth));
|
*run += 1;
|
||||||
|
} else {
|
||||||
// Store a suffix map for the next element if it has the same style
|
styles.push((prev.suffix(depth), *run));
|
||||||
// chain.
|
last = Some((chain, 1));
|
||||||
if iter.peek().is_some_and(|&(_, s2)| s == s2) {
|
}
|
||||||
reuse = Some(suffix.clone());
|
} else {
|
||||||
|
last = Some((chain, 1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some((c, suffix))
|
if let Some((last, run)) = last {
|
||||||
});
|
let skippable = styles.is_empty() && last == trunk;
|
||||||
|
if !skippable {
|
||||||
|
styles.push((last.suffix(depth), run));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(output, trunk, span)
|
(StyleVec { elements, styles }, trunk, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trim a possibly remaining weak item.
|
/// Trim a possibly remaining weak item.
|
||||||
@ -228,3 +227,91 @@ impl<'a> Default for BehavedBuilder<'a> {
|
|||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A sequence of elements with associated styles.
|
||||||
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
|
pub struct StyleVec {
|
||||||
|
elements: EcoVec<Content>,
|
||||||
|
styles: EcoVec<(Styles, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StyleVec {
|
||||||
|
/// Create a style vector from an unstyled vector content.
|
||||||
|
pub fn wrap(elements: EcoVec<Content>) -> Self {
|
||||||
|
Self { elements, styles: EcoVec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether there are no elements.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.elements.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of elements.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.elements.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The raw, unstyled elements.
|
||||||
|
pub fn elements(&self) -> &[Content] {
|
||||||
|
&self.elements
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a style property, but only if it is the same for all children of the
|
||||||
|
/// style vector.
|
||||||
|
pub fn shared_get<T: PartialEq>(
|
||||||
|
&self,
|
||||||
|
styles: StyleChain<'_>,
|
||||||
|
getter: fn(StyleChain) -> T,
|
||||||
|
) -> Option<T> {
|
||||||
|
let value = getter(styles);
|
||||||
|
self.styles
|
||||||
|
.iter()
|
||||||
|
.all(|(local, _)| getter(styles.chain(local)) == value)
|
||||||
|
.then_some(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the contained content and style chains.
|
||||||
|
pub fn chain<'a>(
|
||||||
|
&'a self,
|
||||||
|
outer: &'a StyleChain<'_>,
|
||||||
|
) -> impl Iterator<Item = (&'a Content, StyleChain<'a>)> {
|
||||||
|
self.iter().map(|(element, local)| (element, outer.chain(local)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over pairs of content and styles.
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&Content, &Styles)> {
|
||||||
|
static EMPTY: Styles = Styles::new();
|
||||||
|
self.elements.iter().zip(
|
||||||
|
self.styles
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(local, count)| std::iter::repeat(local).take(*count))
|
||||||
|
.chain(std::iter::repeat(&EMPTY)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over pairs of content and styles.
|
||||||
|
#[allow(clippy::should_implement_trait)]
|
||||||
|
pub fn into_iter(self) -> impl Iterator<Item = (Content, Styles)> {
|
||||||
|
self.elements.into_iter().zip(
|
||||||
|
self.styles
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|(local, count)| std::iter::repeat(local).take(count))
|
||||||
|
.chain(std::iter::repeat(Styles::new())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for StyleVec {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||||
|
f.debug_list()
|
||||||
|
.entries(self.iter().map(|(element, local)| {
|
||||||
|
typst_utils::debug(|f| {
|
||||||
|
for style in local.iter() {
|
||||||
|
writeln!(f, "#{style:?}")?;
|
||||||
|
}
|
||||||
|
element.fmt(f)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ mod behaviour;
|
|||||||
mod process;
|
mod process;
|
||||||
|
|
||||||
pub use self::arenas::Arenas;
|
pub use self::arenas::Arenas;
|
||||||
pub use self::behaviour::{Behave, BehavedBuilder, Behaviour};
|
pub use self::behaviour::{Behave, BehavedBuilder, Behaviour, StyleVec};
|
||||||
pub use self::process::process;
|
pub use self::process::process;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@ -504,8 +504,8 @@ 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_iter();
|
let (items, trunk, span) = self.items.finish();
|
||||||
let mut items = items.peekable();
|
let mut items = items.into_iter().peekable();
|
||||||
let (first, _) = items.peek().unwrap();
|
let (first, _) = items.peek().unwrap();
|
||||||
let output = if first.is::<ListItem>() {
|
let output = if first.is::<ListItem>() {
|
||||||
ListElem::new(
|
ListElem::new(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user