typst_library/
routines.rs

1use std::fmt::{self, Debug, Formatter};
2use std::hash::{Hash, Hasher};
3
4use comemo::{Tracked, TrackedMut};
5use typst_syntax::{Span, SyntaxMode};
6use typst_utils::LazyHash;
7
8use crate::World;
9use crate::diag::SourceResult;
10use crate::engine::{Engine, Route, Sink, Traced};
11use crate::foundations::{
12    Args, Closure, Content, Context, Func, Module, NativeRuleMap, Scope, StyleChain,
13    Styles, Value,
14};
15use crate::introspection::{Introspector, Locator, SplitLocator};
16use crate::layout::{Frame, Region};
17use crate::model::DocumentInfo;
18use crate::visualize::Color;
19
20/// Defines the `Routines` struct.
21macro_rules! routines {
22    ($(
23        $(#[$attr:meta])*
24        fn $name:ident $(<$($time:lifetime),*>)? ($($args:tt)*) -> $ret:ty
25    )*) => {
26        /// Defines implementation of various Typst compiler routines as a table
27        /// of function pointers.
28        ///
29        /// This is essentially dynamic linking and done to allow for crate
30        /// splitting.
31        pub struct Routines {
32            /// Native show rules.
33            pub rules: NativeRuleMap,
34            $(
35                $(#[$attr])*
36                pub $name: $(for<$($time),*>)? fn ($($args)*) -> $ret
37            ),*
38        }
39
40        impl Hash for Routines {
41            fn hash<H: Hasher>(&self, _: &mut H) {}
42        }
43
44        impl Debug for Routines {
45            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
46                f.pad("Routines(..)")
47            }
48        }
49    };
50}
51
52routines! {
53    /// Evaluates a string as code and return the resulting value.
54    fn eval_string(
55        routines: &Routines,
56        world: Tracked<dyn World + '_>,
57        sink: TrackedMut<Sink>,
58        string: &str,
59        span: Span,
60        mode: SyntaxMode,
61        scope: Scope,
62    ) -> SourceResult<Value>
63
64    /// Call the closure in the context with the arguments.
65    fn eval_closure(
66        func: &Func,
67        closure: &LazyHash<Closure>,
68        routines: &Routines,
69        world: Tracked<dyn World + '_>,
70        introspector: Tracked<Introspector>,
71        traced: Tracked<Traced>,
72        sink: TrackedMut<Sink>,
73        route: Tracked<Route>,
74        context: Tracked<Context>,
75        args: Args,
76    ) -> SourceResult<Value>
77
78    /// Realizes content into a flat list of well-known, styled items.
79    fn realize<'a>(
80        kind: RealizationKind,
81        engine: &mut Engine,
82        locator: &mut SplitLocator,
83        arenas: &'a Arenas,
84        content: &'a Content,
85        styles: StyleChain<'a>,
86    ) -> SourceResult<Vec<Pair<'a>>>
87
88    /// Lays out content into a single region, producing a single frame.
89    fn layout_frame(
90        engine: &mut Engine,
91        content: &Content,
92        locator: Locator,
93        styles: StyleChain,
94        region: Region,
95    ) -> SourceResult<Frame>
96
97    /// Constructs the `html` module.
98    fn html_module() -> Module
99
100    /// Wraps content in a span with a color.
101    ///
102    /// This is a temporary workaround until `TextElem::fill` is supported in
103    /// HTML export.
104    fn html_span_filled(content: Content, color: Color) -> Content
105}
106
107/// Defines what kind of realization we are performing.
108pub enum RealizationKind<'a> {
109    /// This the root realization for layout. Requires a mutable reference
110    /// to document metadata that will be filled from `set document` rules.
111    LayoutDocument { info: &'a mut DocumentInfo },
112    /// A nested realization in a container (e.g. a `block`). Requires a mutable
113    /// reference to an enum that will be set to `FragmentKind::Inline` if the
114    /// fragment's content was fully inline.
115    LayoutFragment { kind: &'a mut FragmentKind },
116    /// A nested realization in a paragraph (i.e. a `par`)
117    LayoutPar,
118    /// This the root realization for HTML. Requires a mutable reference to
119    /// document metadata that will be filled from `set document` rules.
120    ///
121    /// The `is_inline` function checks whether content consists of an inline
122    /// HTML element. It's used by the `PAR` grouping rules. This is slightly
123    /// hacky and might be replaced by a mechanism to supply the grouping rules
124    /// as a realization user.
125    HtmlDocument { info: &'a mut DocumentInfo, is_inline: fn(&Content) -> bool },
126    /// A nested realization in a container (e.g. a `block`). Requires a mutable
127    /// reference to an enum that will be set to `FragmentKind::Inline` if the
128    /// fragment's content was fully inline.
129    HtmlFragment { kind: &'a mut FragmentKind, is_inline: fn(&Content) -> bool },
130    /// A realization within math.
131    Math,
132}
133
134impl RealizationKind<'_> {
135    /// It this a realization for HTML export?
136    pub fn is_html(&self) -> bool {
137        matches!(self, Self::HtmlDocument { .. } | Self::HtmlFragment { .. })
138    }
139
140    /// It this a realization for a container?
141    pub fn is_fragment(&self) -> bool {
142        matches!(self, Self::LayoutFragment { .. } | Self::HtmlFragment { .. })
143    }
144
145    /// It this a realization for the whole document?
146    pub fn is_document(&self) -> bool {
147        matches!(self, Self::LayoutDocument { .. } | Self::HtmlDocument { .. })
148    }
149
150    /// If this is a document-level realization, accesses the document info.
151    pub fn as_document_mut(&mut self) -> Option<&mut DocumentInfo> {
152        match self {
153            Self::LayoutDocument { info } | Self::HtmlDocument { info, .. } => {
154                Some(*info)
155            }
156            _ => None,
157        }
158    }
159
160    /// If this is a container-level realization, accesses the fragment kind.
161    pub fn as_fragment_mut(&mut self) -> Option<&mut FragmentKind> {
162        match self {
163            Self::LayoutFragment { kind } | Self::HtmlFragment { kind, .. } => {
164                Some(*kind)
165            }
166            _ => None,
167        }
168    }
169}
170
171/// The kind of fragment output that realization produced.
172#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
173pub enum FragmentKind {
174    /// The fragment's contents were fully inline, and as a result, the output
175    /// elements are too.
176    Inline,
177    /// The fragment contained non-inline content, so inline content was forced
178    /// into paragraphs, and as a result, the output elements are not inline.
179    Block,
180}
181
182/// Temporary storage arenas for lifetime extension during realization.
183///
184/// Must be kept live while the content returned from realization is processed.
185#[derive(Default)]
186pub struct Arenas {
187    /// A typed arena for owned content.
188    pub content: typed_arena::Arena<Content>,
189    /// A typed arena for owned styles.
190    pub styles: typed_arena::Arena<Styles>,
191    /// An untyped arena for everything that is `Copy`.
192    pub bump: bumpalo::Bump,
193}
194
195/// A pair of content and a style chain that applies to it.
196pub type Pair<'a> = (&'a Content, StyleChain<'a>);