typst_library/foundations/
context.rs

1use comemo::Track;
2
3use crate::diag::{bail, Hint, HintedStrResult, SourceResult};
4use crate::engine::Engine;
5use crate::foundations::{
6    elem, Args, Construct, Content, Func, Packed, Show, StyleChain, Value,
7};
8use crate::introspection::{Locatable, Location};
9
10/// Data that is contextually made available to code.
11///
12/// _Contextual_ functions and expressions require the presence of certain
13/// pieces of context to be evaluated. This includes things like `text.lang`,
14/// `measure`, or `counter(heading).get()`.
15#[derive(Debug, Default, Clone, Hash)]
16pub struct Context<'a> {
17    /// The location in the document.
18    pub location: Option<Location>,
19    /// The active styles.
20    pub styles: Option<StyleChain<'a>>,
21}
22
23impl<'a> Context<'a> {
24    /// An empty context.
25    pub fn none() -> Self {
26        Self::default()
27    }
28
29    /// Create a new context from its parts.
30    pub fn new(location: Option<Location>, styles: Option<StyleChain<'a>>) -> Self {
31        Self { location, styles }
32    }
33}
34
35#[comemo::track]
36impl<'a> Context<'a> {
37    /// Try to extract the location.
38    pub fn location(&self) -> HintedStrResult<Location> {
39        require(self.location)
40    }
41
42    /// Try to extract the styles.
43    pub fn styles(&self) -> HintedStrResult<StyleChain<'a>> {
44        require(self.styles)
45    }
46
47    /// Guard access to the introspector by requiring at least some piece of context.
48    pub fn introspect(&self) -> HintedStrResult<()> {
49        require(self.location.map(|_| ()).or(self.styles.map(|_| ())))
50    }
51}
52
53/// Extracts an optional piece of context, yielding an error with hints if
54/// it isn't available.
55fn require<T>(val: Option<T>) -> HintedStrResult<T> {
56    val.ok_or("can only be used when context is known")
57    .hint("try wrapping this in a `context` expression")
58    .hint(
59        "the `context` expression should wrap everything that depends on this function",
60    )
61}
62
63/// Executes a `context` block.
64#[elem(Construct, Locatable, Show)]
65pub struct ContextElem {
66    /// The function to call with the context.
67    #[required]
68    #[internal]
69    func: Func,
70}
71
72impl Construct for ContextElem {
73    fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
74        bail!(args.span, "cannot be constructed manually");
75    }
76}
77
78impl Show for Packed<ContextElem> {
79    #[typst_macros::time(name = "context", span = self.span())]
80    fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
81        let loc = self.location().unwrap();
82        let context = Context::new(Some(loc), Some(styles));
83        Ok(self.func.call::<[Value; 0]>(engine, context.track(), [])?.display())
84    }
85}