Skip to main content

typst_library/foundations/
styles.rs

1use std::any::{Any, TypeId};
2use std::fmt::{self, Debug, Formatter};
3use std::hash::{Hash, Hasher};
4use std::{mem, ptr};
5
6use comemo::Tracked;
7use ecow::{EcoString, EcoVec, eco_vec};
8use indexmap::IndexMap;
9use rustc_hash::FxBuildHasher;
10use smallvec::SmallVec;
11use typst_syntax::Span;
12use typst_utils::LazyHash;
13
14use crate::diag::{SourceResult, Trace, Tracepoint};
15use crate::engine::Engine;
16use crate::foundations::{
17    Content, Context, Element, Field, Func, NativeElement, OneOrMultiple, Packed,
18    RefableProperty, Repr, Selector, SettableProperty, Target, cast, ty,
19};
20use crate::introspection::TagElem;
21
22/// A list of style properties.
23#[ty(cast)]
24#[derive(Default, Clone, PartialEq, Hash)]
25pub struct Styles(EcoVec<LazyHash<Style>>);
26
27impl Styles {
28    /// Create a new, empty style list.
29    pub const fn new() -> Self {
30        Self(EcoVec::new())
31    }
32
33    /// Whether this contains no styles.
34    pub fn is_empty(&self) -> bool {
35        self.0.is_empty()
36    }
37
38    /// Iterate over the contained styles.
39    pub fn iter(&self) -> impl Iterator<Item = &Style> {
40        self.0.iter().map(|style| &**style)
41    }
42
43    /// Iterate over the contained styles.
44    pub fn as_slice(&self) -> &[LazyHash<Style>] {
45        self.0.as_slice()
46    }
47
48    /// Set an inner value for a style property.
49    ///
50    /// If the property needs folding and the value is already contained in the
51    /// style map, `self` contributes the outer values and `value` is the inner
52    /// one.
53    pub fn set<E, const I: u8>(&mut self, field: Field<E, I>, value: E::Type)
54    where
55        E: SettableProperty<I>,
56        E::Type: Debug + Clone + Hash + Send + Sync + 'static,
57    {
58        self.push(Property::new(field, value));
59    }
60
61    /// Add a new style to the list.
62    pub fn push(&mut self, style: impl Into<Style>) {
63        self.0.push(LazyHash::new(style.into()));
64    }
65
66    /// Remove the style that was last set.
67    pub fn unset(&mut self) {
68        self.0.pop();
69    }
70
71    /// Apply outer styles. Like [`chain`](StyleChain::chain), but in-place.
72    pub fn apply(&mut self, mut outer: Self) {
73        outer.0.extend(mem::take(self).0);
74        *self = outer;
75    }
76
77    /// Apply one outer styles.
78    pub fn apply_one(&mut self, outer: Style) {
79        self.0.insert(0, LazyHash::new(outer));
80    }
81
82    /// Add an origin span to all contained properties.
83    pub fn spanned(mut self, span: Span) -> Self {
84        for entry in self.0.make_mut() {
85            if let Style::Property(property) = &mut **entry {
86                property.span = span;
87            }
88        }
89        self
90    }
91
92    /// Marks the styles as having been applied outside of any show rule.
93    pub fn outside(mut self) -> Self {
94        for entry in self.0.make_mut() {
95            match &mut **entry {
96                Style::Property(property) => property.outside = true,
97                Style::Recipe(recipe) => recipe.outside = true,
98                _ => {}
99            }
100        }
101        self
102    }
103
104    /// Marks the styles as being allowed to be lifted up to the page level.
105    pub fn liftable(mut self) -> Self {
106        for entry in self.0.make_mut() {
107            if let Style::Property(property) = &mut **entry {
108                property.liftable = true;
109            }
110        }
111        self
112    }
113
114    /// Determines the styles used for content that it at the root, outside of
115    /// the user-controlled content (e.g. page marginals and footnotes). This
116    /// applies to both paged and HTML export.
117    ///
118    /// As a base, we collect the styles that are shared by all elements in the
119    /// children (this can be a whole document in HTML or a page run in paged
120    /// export). As a fallback if there are no elements, we use the styles
121    /// active at the very start or, for page runs, at the pagebreak that
122    /// introduced the page. Then, to produce our trunk styles, we filter this
123    /// list of styles according to a few rules:
124    ///
125    /// - Other styles are only kept if they are `outside && (initial ||
126    ///   liftable)`.
127    /// - "Outside" means they were not produced within a show rule or, for page
128    ///   runs, that the show rule "broke free" to the root level by emitting
129    ///   page styles.
130    /// - "Initial" means they were active where the children start (efor pages,
131    ///   at the pagebreak that introduced the page). Since these are
132    ///   intuitively already active, they should be kept even if not liftable.
133    ///   (E.g. `text(red, page(..)`) makes the footer red.)
134    /// - "Liftable" means they can be lifted to the root  level even though
135    ///   they weren't yet active at the very beginning. Set rule styles are
136    ///   liftable as opposed to direct constructor calls:
137    ///   - For `set page(..); set text(red)` the red text is kept even though
138    ///     it comes after the weak pagebreak from set page.
139    ///   - For `set page(..); text(red)[..]` the red isn't kept because the
140    ///     constructor styles are not liftable.
141    pub fn root(children: &[(&Content, StyleChain)], initial: StyleChain) -> Styles {
142        // Determine the shared styles (excluding tags).
143        let base = StyleChain::trunk_from_pairs(children).unwrap_or(initial).to_map();
144
145        // Determine the initial styles that are also shared by everything. We can't
146        // use `StyleChain::trunk` because it currently doesn't deal with partially
147        // shared links (where a subslice matches).
148        let trunk_len = initial
149            .to_map()
150            .as_slice()
151            .iter()
152            .zip(base.as_slice())
153            .take_while(|&(a, b)| a == b)
154            .count();
155
156        // Filter the base styles according to our rules.
157        base.into_iter()
158            .enumerate()
159            .filter(|(i, style)| {
160                let initial = *i < trunk_len;
161                style.outside() && (initial || style.liftable())
162            })
163            .map(|(_, style)| style)
164            .collect()
165    }
166}
167
168impl From<LazyHash<Style>> for Styles {
169    fn from(style: LazyHash<Style>) -> Self {
170        Self(eco_vec![style])
171    }
172}
173
174impl<const N: usize> From<[LazyHash<Style>; N]> for Styles {
175    fn from(arr: [LazyHash<Style>; N]) -> Self {
176        Self(arr.into())
177    }
178}
179
180impl From<Style> for Styles {
181    fn from(style: Style) -> Self {
182        Self(eco_vec![LazyHash::new(style)])
183    }
184}
185
186impl IntoIterator for Styles {
187    type Item = LazyHash<Style>;
188    type IntoIter = ecow::vec::IntoIter<Self::Item>;
189
190    fn into_iter(self) -> Self::IntoIter {
191        self.0.into_iter()
192    }
193}
194
195impl FromIterator<LazyHash<Style>> for Styles {
196    fn from_iter<T: IntoIterator<Item = LazyHash<Style>>>(iter: T) -> Self {
197        Self(iter.into_iter().collect())
198    }
199}
200
201impl Debug for Styles {
202    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
203        f.write_str("Styles ")?;
204        f.debug_list().entries(&self.0).finish()
205    }
206}
207
208impl Repr for Styles {
209    fn repr(&self) -> EcoString {
210        "styles(..)".into()
211    }
212}
213
214/// A single style property or recipe.
215#[derive(Clone, Hash)]
216pub enum Style {
217    /// A style property originating from a set rule or constructor.
218    Property(Property),
219    /// A show rule recipe.
220    Recipe(Recipe),
221    /// Disables a specific show rule recipe.
222    ///
223    /// Note: This currently only works for regex recipes since it's the only
224    /// place we need it for the moment. Normal show rules use guards directly
225    /// on elements instead.
226    Revocation(RecipeIndex),
227}
228
229impl Style {
230    /// If this is a property, return it.
231    pub fn property(&self) -> Option<&Property> {
232        match self {
233            Self::Property(property) => Some(property),
234            _ => None,
235        }
236    }
237
238    /// If this is a recipe, return it.
239    pub fn recipe(&self) -> Option<&Recipe> {
240        match self {
241            Self::Recipe(recipe) => Some(recipe),
242            _ => None,
243        }
244    }
245
246    /// The style's span, if any.
247    pub fn span(&self) -> Span {
248        match self {
249            Self::Property(property) => property.span,
250            Self::Recipe(recipe) => recipe.span,
251            Self::Revocation(_) => Span::detached(),
252        }
253    }
254
255    /// Returns `Some(_)` with an optional span if this style is for
256    /// the given element.
257    pub fn element(&self) -> Option<Element> {
258        match self {
259            Style::Property(property) => Some(property.elem),
260            Style::Recipe(recipe) => match recipe.selector {
261                Some(Selector::Elem(elem, _)) => Some(elem),
262                _ => None,
263            },
264            Style::Revocation(_) => None,
265        }
266    }
267
268    /// Whether the style is allowed to be lifted up to the page level. Only
269    /// true for styles originating from set rules.
270    pub fn liftable(&self) -> bool {
271        match self {
272            Self::Property(property) => property.liftable,
273            Self::Recipe(_) => true,
274            Self::Revocation(_) => false,
275        }
276    }
277
278    /// Whether the style was applied outside of any show rule. This is set
279    /// during realization.
280    pub fn outside(&self) -> bool {
281        match self {
282            Self::Property(property) => property.outside,
283            Self::Recipe(recipe) => recipe.outside,
284            Self::Revocation(_) => false,
285        }
286    }
287
288    /// Turn this style into prehashed style.
289    pub fn wrap(self) -> LazyHash<Style> {
290        LazyHash::new(self)
291    }
292}
293
294impl Debug for Style {
295    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
296        match self {
297            Self::Property(property) => property.fmt(f),
298            Self::Recipe(recipe) => recipe.fmt(f),
299            Self::Revocation(guard) => guard.fmt(f),
300        }
301    }
302}
303
304impl From<Property> for Style {
305    fn from(property: Property) -> Self {
306        Self::Property(property)
307    }
308}
309
310impl From<Recipe> for Style {
311    fn from(recipe: Recipe) -> Self {
312        Self::Recipe(recipe)
313    }
314}
315
316/// A style property originating from a set rule or constructor.
317#[derive(Clone, Hash)]
318pub struct Property {
319    /// The element the property belongs to.
320    elem: Element,
321    /// The property's ID.
322    id: u8,
323    /// The property's value.
324    value: Block,
325    /// The span of the set rule the property stems from.
326    span: Span,
327    /// Whether the property is allowed to be lifted up to the page level.
328    liftable: bool,
329    /// Whether the property was applied outside of any show rule.
330    outside: bool,
331}
332
333impl Property {
334    /// Create a new property from a key-value pair.
335    pub fn new<E, const I: u8>(_: Field<E, I>, value: E::Type) -> Self
336    where
337        E: SettableProperty<I>,
338        E::Type: Debug + Clone + Hash + Send + Sync + 'static,
339    {
340        Self {
341            elem: E::ELEM,
342            id: I,
343            value: Block::new(value),
344            span: Span::detached(),
345            liftable: false,
346            outside: false,
347        }
348    }
349
350    /// Whether this property is the given one.
351    pub fn is(&self, elem: Element, id: u8) -> bool {
352        self.elem == elem && self.id == id
353    }
354
355    /// Whether this property belongs to the given element.
356    pub fn is_of(&self, elem: Element) -> bool {
357        self.elem == elem
358    }
359
360    /// Turn this property into prehashed style.
361    pub fn wrap(self) -> LazyHash<Style> {
362        Style::Property(self).wrap()
363    }
364}
365
366impl Debug for Property {
367    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
368        write!(
369            f,
370            "Set({}.{}: ",
371            self.elem.name(),
372            self.elem.field_name(self.id).unwrap_or("internal")
373        )?;
374        self.value.fmt(f)?;
375        write!(f, ")")
376    }
377}
378
379/// A block storage for storing style values.
380///
381/// We're using a `Box` since values will either be contained in an `Arc` and
382/// therefore already on the heap or they will be small enough that we can just
383/// clone them.
384#[derive(Hash)]
385struct Block(Box<dyn Blockable>);
386
387impl Block {
388    /// Creates a new block.
389    fn new<T: Blockable>(value: T) -> Self {
390        Self(Box::new(value))
391    }
392
393    /// Downcasts the block to the specified type.
394    fn downcast<T: 'static>(&self, func: Element, id: u8) -> &T {
395        let inner: &dyn Blockable = &*self.0;
396        (inner as &dyn Any)
397            .downcast_ref()
398            .unwrap_or_else(|| block_wrong_type(func, id, self))
399    }
400}
401
402impl Debug for Block {
403    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
404        self.0.fmt(f)
405    }
406}
407
408impl Clone for Block {
409    fn clone(&self) -> Self {
410        self.0.dyn_clone()
411    }
412}
413
414/// A value that can be stored in a block.
415///
416/// Auto derived for all types that implement [`Any`], [`Clone`], [`Hash`],
417/// [`Debug`], [`Send`] and [`Sync`].
418trait Blockable: Debug + Any + Send + Sync + 'static {
419    /// Equivalent to [`Hash`] for the block.
420    fn dyn_hash(&self, state: &mut dyn Hasher);
421
422    /// Equivalent to [`Clone`] for the block.
423    fn dyn_clone(&self) -> Block;
424}
425
426impl<T: Debug + Clone + Hash + Send + Sync + 'static> Blockable for T {
427    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
428        // Also hash the TypeId since values with different types but
429        // equal data should be different.
430        TypeId::of::<Self>().hash(&mut state);
431        self.hash(&mut state);
432    }
433
434    fn dyn_clone(&self) -> Block {
435        Block(Box::new(self.clone()))
436    }
437}
438
439impl Hash for dyn Blockable {
440    fn hash<H: Hasher>(&self, state: &mut H) {
441        self.dyn_hash(state);
442    }
443}
444
445/// A show rule recipe.
446#[derive(Clone, PartialEq, Hash)]
447pub struct Recipe {
448    /// Determines whether the recipe applies to an element.
449    ///
450    /// If this is `None`, then this recipe is from a show rule with
451    /// no selector (`show: rest => ...`), which is [eagerly applied][Content::styled_with_recipe]
452    /// to the rest of the content in the scope.
453    selector: Option<Selector>,
454    /// The transformation to perform on the match.
455    transform: Transformation,
456    /// The span that errors are reported with.
457    span: Span,
458    /// Relevant properties of the kind of construct the style originated from
459    /// and where it was applied.
460    outside: bool,
461}
462
463impl Recipe {
464    /// Create a new recipe from a key-value pair.
465    pub fn new(
466        selector: Option<Selector>,
467        transform: Transformation,
468        span: Span,
469    ) -> Self {
470        Self { selector, transform, span, outside: false }
471    }
472
473    /// The recipe's selector.
474    pub fn selector(&self) -> Option<&Selector> {
475        self.selector.as_ref()
476    }
477
478    /// The recipe's transformation.
479    pub fn transform(&self) -> &Transformation {
480        &self.transform
481    }
482
483    /// The recipe's span.
484    pub fn span(&self) -> Span {
485        self.span
486    }
487
488    /// Apply the recipe to the given content.
489    pub fn apply(
490        &self,
491        engine: &mut Engine,
492        context: Tracked<Context>,
493        content: Content,
494    ) -> SourceResult<Content> {
495        let mut content = match &self.transform {
496            Transformation::Content(content) => content.clone(),
497            Transformation::Func(func) => {
498                let mut result = func.call(engine, context, [content.clone()]);
499                if self.selector.is_some() {
500                    let point = || Tracepoint::Show(content.func().name().into());
501                    result = result.trace(engine.world, point, content.span());
502                }
503                result?.display()
504            }
505            Transformation::Style(styles) => content.styled_with_map(styles.clone()),
506        };
507        if content.span().is_detached() {
508            content = content.spanned(self.span);
509        }
510        Ok(content)
511    }
512}
513
514impl Debug for Recipe {
515    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
516        f.write_str("Show(")?;
517        if let Some(selector) = &self.selector {
518            selector.fmt(f)?;
519            f.write_str(", ")?;
520        }
521        self.transform.fmt(f)?;
522        f.write_str(")")
523    }
524}
525
526/// Identifies a show rule recipe from the top of the chain.
527#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
528pub struct RecipeIndex(pub usize);
529
530/// A show rule transformation that can be applied to a match.
531#[derive(Clone, PartialEq, Hash)]
532pub enum Transformation {
533    /// Replacement content.
534    Content(Content),
535    /// A function to apply to the match.
536    Func(Func),
537    /// Apply styles to the content.
538    Style(Styles),
539}
540
541impl Debug for Transformation {
542    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
543        match self {
544            Self::Content(content) => content.fmt(f),
545            Self::Func(func) => func.fmt(f),
546            Self::Style(styles) => styles.fmt(f),
547        }
548    }
549}
550
551cast! {
552    Transformation,
553    content: Content => Self::Content(content),
554    func: Func => Self::Func(func),
555}
556
557/// A chain of styles, similar to a linked list.
558///
559/// A style chain allows to combine properties from multiple style lists in a
560/// element hierarchy in a non-allocating way. Rather than eagerly merging the
561/// lists, each access walks the hierarchy from the innermost to the outermost
562/// map, trying to find a match and then folding it with matches further up the
563/// chain.
564#[derive(Default, Copy, Clone, Hash)]
565pub struct StyleChain<'a> {
566    /// The first link of this chain.
567    head: &'a [LazyHash<Style>],
568    /// The remaining links in the chain.
569    tail: Option<&'a Self>,
570}
571
572impl<'a> StyleChain<'a> {
573    /// Start a new style chain with root styles.
574    pub fn new(root: &'a Styles) -> Self {
575        Self { head: &root.0, tail: None }
576    }
577
578    /// Retrieves the value of the given field from the style chain.
579    ///
580    /// A `Field` value is a zero-sized value that specifies which field of an
581    /// element you want to retrieve on the type-system level. It also ensures
582    /// that Rust can infer the correct return type.
583    ///
584    /// Should be preferred over [`get_cloned`](Self::get_cloned) or
585    /// [`get_ref`](Self::get_ref), but is only available for [`Copy`] types.
586    /// For other types an explicit decision needs to be made whether cloning is
587    /// necessary.
588    pub fn get<E, const I: u8>(self, field: Field<E, I>) -> E::Type
589    where
590        E: SettableProperty<I>,
591        E::Type: Copy,
592    {
593        self.get_cloned(field)
594    }
595
596    /// Retrieves and clones the value from the style chain.
597    ///
598    /// Prefer [`get`](Self::get) if the type is `Copy` and
599    /// [`get_ref`](Self::get_ref) if a reference suffices.
600    pub fn get_cloned<E, const I: u8>(self, _: Field<E, I>) -> E::Type
601    where
602        E: SettableProperty<I>,
603    {
604        if let Some(fold) = E::FOLD {
605            self.get_folded::<E::Type>(E::ELEM, I, fold, E::default())
606        } else {
607            self.get_unfolded::<E::Type>(E::ELEM, I)
608                .cloned()
609                .unwrap_or_else(E::default)
610        }
611    }
612
613    /// Retrieves a reference to the value of the given field from the style
614    /// chain.
615    ///
616    /// Not possible if the value needs folding.
617    pub fn get_ref<E, const I: u8>(self, _: Field<E, I>) -> &'a E::Type
618    where
619        E: RefableProperty<I>,
620    {
621        self.get_unfolded(E::ELEM, I).unwrap_or_else(|| E::default_ref())
622    }
623
624    /// Retrieves the value and then immediately [resolves](Resolve) it.
625    pub fn resolve<E, const I: u8>(
626        self,
627        field: Field<E, I>,
628    ) -> <E::Type as Resolve>::Output
629    where
630        E: SettableProperty<I>,
631        E::Type: Resolve,
632    {
633        self.get_cloned(field).resolve(self)
634    }
635
636    /// Whether there is a style for the given field of the given element.
637    pub fn has<E: NativeElement, const I: u8>(&self, _: Field<E, I>) -> bool {
638        let elem = E::ELEM;
639        self.entries()
640            .filter_map(|style| style.property())
641            .any(|property| property.is_of(elem) && property.id == I)
642    }
643
644    /// Retrieves a reference to a field, also taking into account the
645    /// instance's value if any.
646    fn get_unfolded<T: 'static>(self, func: Element, id: u8) -> Option<&'a T> {
647        self.find(func, id).map(|block| block.downcast(func, id))
648    }
649
650    /// Retrieves a reference to a field, also taking into account the
651    /// instance's value if any.
652    fn get_folded<T: 'static + Clone>(
653        self,
654        func: Element,
655        id: u8,
656        fold: fn(T, T) -> T,
657        default: T,
658    ) -> T {
659        let iter = self
660            .properties(func, id)
661            .map(|block| block.downcast::<T>(func, id).clone());
662
663        if let Some(folded) = iter.reduce(fold) { fold(folded, default) } else { default }
664    }
665
666    /// Iterate over all values for the given property in the chain.
667    fn find(self, func: Element, id: u8) -> Option<&'a Block> {
668        self.properties(func, id).next()
669    }
670
671    /// Iterate over all values for the given property in the chain.
672    fn properties(self, func: Element, id: u8) -> impl Iterator<Item = &'a Block> {
673        self.entries()
674            .filter_map(|style| style.property())
675            .filter(move |property| property.is(func, id))
676            .map(|property| &property.value)
677    }
678
679    /// Make the given chainable the first link of this chain.
680    ///
681    /// The resulting style chain contains styles from `local` as well as
682    /// `self`. The ones from `local` take precedence over the ones from
683    /// `self`. For folded properties `local` contributes the inner value.
684    pub fn chain<'b, C>(&'b self, local: &'b C) -> StyleChain<'b>
685    where
686        C: Chainable + ?Sized,
687    {
688        Chainable::chain(local, self)
689    }
690
691    /// Iterate over the entries of the chain.
692    pub fn entries(self) -> Entries<'a> {
693        Entries { inner: [].as_slice().iter(), links: self.links() }
694    }
695
696    /// Iterate over the recipes in the chain.
697    pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
698        self.entries().filter_map(|style| style.recipe())
699    }
700
701    /// Iterate over the links of the chain.
702    pub fn links(self) -> Links<'a> {
703        Links(Some(self))
704    }
705
706    /// Convert to a style map.
707    pub fn to_map(self) -> Styles {
708        let mut styles: EcoVec<_> = self.entries().cloned().collect();
709        styles.make_mut().reverse();
710        Styles(styles)
711    }
712
713    /// Build owned styles from the suffix (all links beyond the `len`) of the
714    /// chain.
715    pub fn suffix(self, len: usize) -> Styles {
716        let mut styles = EcoVec::new();
717        let take = self.links().count().saturating_sub(len);
718        for link in self.links().take(take) {
719            styles.extend(link.iter().cloned().rev());
720        }
721        styles.make_mut().reverse();
722        Styles(styles)
723    }
724
725    /// Remove the last link from the chain.
726    pub fn pop(&mut self) {
727        *self = self.tail.copied().unwrap_or_default();
728    }
729
730    /// Determine the shared trunk of a collection of style chains.
731    pub fn trunk(iter: impl IntoIterator<Item = Self>) -> Option<Self> {
732        // Determine shared style depth and first span.
733        let mut iter = iter.into_iter();
734        let mut trunk = iter.next()?;
735        let mut depth = trunk.links().count();
736
737        for mut chain in iter {
738            let len = chain.links().count();
739            if len < depth {
740                for _ in 0..depth - len {
741                    trunk.pop();
742                }
743                depth = len;
744            } else if len > depth {
745                for _ in 0..len - depth {
746                    chain.pop();
747                }
748            }
749
750            while depth > 0 && chain != trunk {
751                trunk.pop();
752                chain.pop();
753                depth -= 1;
754            }
755        }
756
757        Some(trunk)
758    }
759
760    /// Determines the shared trunk of a list of elements.
761    ///
762    /// This will ignore styles for tags (conceptually, they just don't exist).
763    pub fn trunk_from_pairs(iter: &[(&Content, Self)]) -> Option<Self> {
764        Self::trunk(iter.iter().filter(|(c, _)| !c.is::<TagElem>()).map(|&(_, s)| s))
765    }
766}
767
768impl Debug for StyleChain<'_> {
769    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
770        f.write_str("StyleChain ")?;
771        f.debug_list()
772            .entries(self.entries().collect::<Vec<_>>().into_iter().rev())
773            .finish()
774    }
775}
776
777impl PartialEq for StyleChain<'_> {
778    fn eq(&self, other: &Self) -> bool {
779        ptr::eq(self.head, other.head)
780            && match (self.tail, other.tail) {
781                (Some(a), Some(b)) => ptr::eq(a, b),
782                (None, None) => true,
783                _ => false,
784            }
785    }
786}
787
788/// Things that can be attached to a style chain.
789pub trait Chainable {
790    /// Attach `self` as the first link of the chain.
791    fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a>;
792}
793
794impl Chainable for LazyHash<Style> {
795    fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
796        StyleChain {
797            head: std::slice::from_ref(self),
798            tail: Some(outer),
799        }
800    }
801}
802
803impl Chainable for [LazyHash<Style>] {
804    fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
805        if self.is_empty() {
806            *outer
807        } else {
808            StyleChain { head: self, tail: Some(outer) }
809        }
810    }
811}
812
813impl<const N: usize> Chainable for [LazyHash<Style>; N] {
814    fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
815        Chainable::chain(self.as_slice(), outer)
816    }
817}
818
819impl Chainable for Styles {
820    fn chain<'a>(&'a self, outer: &'a StyleChain<'_>) -> StyleChain<'a> {
821        Chainable::chain(self.0.as_slice(), outer)
822    }
823}
824
825/// An iterator over the entries in a style chain.
826pub struct Entries<'a> {
827    inner: std::slice::Iter<'a, LazyHash<Style>>,
828    links: Links<'a>,
829}
830
831impl<'a> Iterator for Entries<'a> {
832    type Item = &'a LazyHash<Style>;
833
834    fn next(&mut self) -> Option<Self::Item> {
835        loop {
836            if let Some(entry) = self.inner.next_back() {
837                return Some(entry);
838            }
839
840            match self.links.next() {
841                Some(next) => self.inner = next.iter(),
842                None => return None,
843            }
844        }
845    }
846}
847
848/// An iterator over the links of a style chain.
849pub struct Links<'a>(Option<StyleChain<'a>>);
850
851impl<'a> Iterator for Links<'a> {
852    type Item = &'a [LazyHash<Style>];
853
854    fn next(&mut self) -> Option<Self::Item> {
855        let StyleChain { head, tail } = self.0?;
856        self.0 = tail.copied();
857        Some(head)
858    }
859}
860
861/// A property that is resolved with other properties from the style chain.
862pub trait Resolve {
863    /// The type of the resolved output.
864    type Output;
865
866    /// Resolve the value using the style chain.
867    fn resolve(self, styles: StyleChain) -> Self::Output;
868}
869
870impl<T: Resolve> Resolve for Option<T> {
871    type Output = Option<T::Output>;
872
873    fn resolve(self, styles: StyleChain) -> Self::Output {
874        self.map(|v| v.resolve(styles))
875    }
876}
877
878/// A property that is folded to determine its final value.
879///
880/// In the example below, the chain of stroke values is folded into a single
881/// value: `4pt + red`.
882///
883/// ```example
884/// #set rect(stroke: red)
885/// #set rect(stroke: 4pt)
886/// #rect()
887/// ```
888///
889/// Note: Folding must be associative, i.e. any implementation must satisfy
890/// `fold(fold(a, b), c) == fold(a, fold(b, c))`.
891pub trait Fold {
892    /// Fold this inner value with an outer folded value.
893    fn fold(self, outer: Self) -> Self;
894}
895
896impl Fold for bool {
897    fn fold(self, _: Self) -> Self {
898        self
899    }
900}
901
902impl<T: Fold> Fold for Option<T> {
903    fn fold(self, outer: Self) -> Self {
904        match (self, outer) {
905            (Some(inner), Some(outer)) => Some(inner.fold(outer)),
906            // An explicit `None` should be respected, thus we don't do
907            // `inner.or(outer)`.
908            (inner, _) => inner,
909        }
910    }
911}
912
913impl<T> Fold for Vec<T> {
914    fn fold(self, mut outer: Self) -> Self {
915        outer.extend(self);
916        outer
917    }
918}
919
920impl<T, const N: usize> Fold for SmallVec<[T; N]> {
921    fn fold(self, mut outer: Self) -> Self {
922        outer.extend(self);
923        outer
924    }
925}
926
927impl<T> Fold for OneOrMultiple<T> {
928    fn fold(self, mut outer: Self) -> Self {
929        outer.0.extend(self.0);
930        outer
931    }
932}
933
934/// A [folding](Fold) function.
935pub type FoldFn<T> = fn(T, T) -> T;
936
937/// A variant of fold for foldable optional (`Option<T>`) values where an inner
938/// `None` value isn't respected (contrary to `Option`'s usual `Fold`
939/// implementation, with which folding with an inner `None` always returns
940/// `None`). Instead, when either of the `Option` objects is `None`, the other
941/// one is necessarily returned by `fold_or`. Normal folding still occurs when
942/// both values are `Some`, using `T`'s `Fold` implementation.
943///
944/// This is useful when `None` in a particular context means "unspecified"
945/// rather than "absent", in which case a specified value (`Some`) is chosen
946/// over an unspecified one (`None`), while two specified values are folded
947/// together.
948pub trait AlternativeFold {
949    /// Attempts to fold this inner value with an outer value. However, if
950    /// either value is `None`, returns the other one instead of folding.
951    fn fold_or(self, outer: Self) -> Self;
952}
953
954impl<T: Fold> AlternativeFold for Option<T> {
955    fn fold_or(self, outer: Self) -> Self {
956        match (self, outer) {
957            (Some(inner), Some(outer)) => Some(inner.fold(outer)),
958            // If one of values is `None`, return the other one instead of
959            // folding.
960            (inner, outer) => inner.or(outer),
961        }
962    }
963}
964
965/// A type that accumulates depth when folded.
966#[derive(Debug, Default, Copy, Clone, PartialEq, Hash)]
967pub struct Depth(pub usize);
968
969impl Fold for Depth {
970    fn fold(self, outer: Self) -> Self {
971        Self(outer.0 + self.0)
972    }
973}
974
975#[cold]
976fn block_wrong_type(func: Element, id: u8, value: &Block) -> ! {
977    panic!(
978        "attempted to read a value of a different type than was written {}.{}: {:?}",
979        func.name(),
980        func.field_name(id).unwrap(),
981        value
982    )
983}
984
985/// Holds native show rules.
986#[derive(Debug, Clone)]
987pub struct NativeRuleMap {
988    rules: IndexMap<(Element, Target), NativeShowRule, FxBuildHasher>,
989}
990
991/// The signature of a native show rule.
992pub type ShowFn<T> = fn(
993    elem: &Packed<T>,
994    engine: &mut Engine,
995    styles: StyleChain,
996) -> SourceResult<Content>;
997
998impl NativeRuleMap {
999    /// Creates a new rule map.
1000    ///
1001    /// Should be populated with rules for all target-element combinations that
1002    /// are supported.
1003    ///
1004    /// Contains built-in rules for a few special elements.
1005    pub fn new() -> Self {
1006        fn empty<T: NativeElement>() -> ShowFn<T> {
1007            |_, _, _| Ok(Content::empty())
1008        }
1009
1010        let mut rules = Self { rules: IndexMap::default() };
1011
1012        for target in [Target::Paged, Target::Html, Target::Bundle] {
1013            // ContextElem is as special as SequenceElem and StyledElem and
1014            // could, in theory, also be special cased in realization.
1015            rules.register(target, crate::foundations::CONTEXT_RULE);
1016
1017            // CounterDisplayElem only exists because the compiler can't
1018            // currently express the equivalent of `context
1019            // counter(..).display(..)` in native code (no native closures).
1020            rules.register(target, crate::introspection::COUNTER_DISPLAY_RULE);
1021
1022            // These are all only for introspection and empty on all targets.
1023            rules.register(target, empty::<crate::introspection::CounterUpdateElem>());
1024            rules.register(target, empty::<crate::introspection::StateUpdateElem>());
1025            rules.register(target, empty::<crate::introspection::MetadataElem>());
1026            rules.register(target, empty::<crate::model::PrefixInfo>());
1027        }
1028
1029        for target in [Target::Paged, Target::Html] {
1030            rules.register(target, crate::model::ASSET_UNSUPPORTED_RULE);
1031            rules.register(target, crate::model::DOCUMENT_UNSUPPORTED_RULE);
1032        }
1033
1034        rules
1035    }
1036
1037    /// Registers a rule for a target.
1038    ///
1039    /// Panics if a rule already exists for this target-element combination.
1040    #[track_caller]
1041    pub fn register<T: NativeElement>(&mut self, target: Target, f: ShowFn<T>) {
1042        let res = self.rules.insert((T::ELEM, target), NativeShowRule::new(f));
1043        if res.is_some() {
1044            panic!(
1045                "duplicate native show rule for `{}` on {target:?} target",
1046                T::ELEM.name()
1047            )
1048        }
1049    }
1050
1051    /// Replaces a rule for a target.
1052    ///
1053    /// Panics if no rule exists for this target-element combination.
1054    #[track_caller]
1055    pub fn replace<T: NativeElement>(&mut self, target: Target, f: ShowFn<T>) {
1056        let res = self.rules.insert((T::ELEM, target), NativeShowRule::new(f));
1057        if res.is_none() {
1058            panic!(
1059                "no existing native show rule for `{}` on {target:?} target",
1060                T::ELEM.name()
1061            )
1062        }
1063    }
1064
1065    /// Retrieves the rule that applies to the `content` on the current
1066    /// `target`.
1067    pub fn get(&self, target: Target, content: &Content) -> Option<NativeShowRule> {
1068        self.rules.get(&(content.func(), target)).copied()
1069    }
1070}
1071
1072impl Default for NativeRuleMap {
1073    fn default() -> Self {
1074        Self::new()
1075    }
1076}
1077
1078impl Hash for NativeRuleMap {
1079    fn hash<H: Hasher>(&self, state: &mut H) {
1080        state.write_usize(self.rules.len());
1081        for item in &self.rules {
1082            item.hash(state);
1083        }
1084    }
1085}
1086
1087pub use rule::NativeShowRule;
1088
1089mod rule {
1090    use super::*;
1091
1092    /// The show rule for a native element.
1093    #[derive(Copy, Clone, Hash)]
1094    pub struct NativeShowRule {
1095        /// The element to which this rule applies.
1096        elem: Element,
1097        /// Must only be called with content of the appropriate type.
1098        f: unsafe fn(
1099            elem: &Content,
1100            engine: &mut Engine,
1101            styles: StyleChain,
1102        ) -> SourceResult<Content>,
1103    }
1104
1105    impl NativeShowRule {
1106        /// Create a new type-erased show rule.
1107        pub fn new<T: NativeElement>(f: ShowFn<T>) -> Self {
1108            Self {
1109                elem: T::ELEM,
1110                // Safety: The two function pointer types only differ in the
1111                // first argument, which changes from `&Packed<T>` to
1112                // `&Content`. `Packed<T>` is a transparent wrapper around
1113                // `Content`. The resulting function is unsafe to call because
1114                // content of the correct type must be passed to it.
1115                #[allow(clippy::missing_transmute_annotations)]
1116                f: unsafe { std::mem::transmute(f) },
1117            }
1118        }
1119
1120        /// Applies the rule to content. Panics if the content is of the wrong
1121        /// type.
1122        pub fn apply(
1123            &self,
1124            content: &Content,
1125            engine: &mut Engine,
1126            styles: StyleChain,
1127        ) -> SourceResult<Content> {
1128            assert_eq!(content.elem(), self.elem);
1129
1130            // Safety: We just checked that the element is of the correct type.
1131            unsafe { (self.f)(content, engine, styles) }
1132        }
1133    }
1134
1135    impl Debug for NativeShowRule {
1136        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1137            f.pad("NativeShowRule(..)")
1138        }
1139    }
1140}