mirror of
https://github.com/typst/typst
synced 2025-07-27 22:37:54 +08:00
83 lines
2.5 KiB
Rust
83 lines
2.5 KiB
Rust
use comemo::Track;
|
|
|
|
use crate::diag::{Hint, HintedStrResult, SourceResult, bail};
|
|
use crate::engine::Engine;
|
|
use crate::foundations::{
|
|
Args, Construct, Content, Func, ShowFn, StyleChain, Value, elem,
|
|
};
|
|
use crate::introspection::{Locatable, Location};
|
|
|
|
/// Data that is contextually made available to code.
|
|
///
|
|
/// _Contextual_ functions and expressions require the presence of certain
|
|
/// pieces of context to be evaluated. This includes things like `text.lang`,
|
|
/// `measure`, or `counter(heading).get()`.
|
|
#[derive(Debug, Default, Clone, Hash)]
|
|
pub struct Context<'a> {
|
|
/// The location in the document.
|
|
pub location: Option<Location>,
|
|
/// The active styles.
|
|
pub styles: Option<StyleChain<'a>>,
|
|
}
|
|
|
|
impl<'a> Context<'a> {
|
|
/// An empty context.
|
|
pub fn none() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// Create a new context from its parts.
|
|
pub fn new(location: Option<Location>, styles: Option<StyleChain<'a>>) -> Self {
|
|
Self { location, styles }
|
|
}
|
|
}
|
|
|
|
#[comemo::track]
|
|
impl<'a> Context<'a> {
|
|
/// Try to extract the location.
|
|
pub fn location(&self) -> HintedStrResult<Location> {
|
|
require(self.location)
|
|
}
|
|
|
|
/// Try to extract the styles.
|
|
pub fn styles(&self) -> HintedStrResult<StyleChain<'a>> {
|
|
require(self.styles)
|
|
}
|
|
|
|
/// Guard access to the introspector by requiring at least some piece of context.
|
|
pub fn introspect(&self) -> HintedStrResult<()> {
|
|
require(self.location.map(|_| ()).or(self.styles.map(|_| ())))
|
|
}
|
|
}
|
|
|
|
/// Extracts an optional piece of context, yielding an error with hints if
|
|
/// it isn't available.
|
|
fn require<T>(val: Option<T>) -> HintedStrResult<T> {
|
|
val.ok_or("can only be used when context is known")
|
|
.hint("try wrapping this in a `context` expression")
|
|
.hint(
|
|
"the `context` expression should wrap everything that depends on this function",
|
|
)
|
|
}
|
|
|
|
/// Executes a `context` block.
|
|
#[elem(Construct, Locatable)]
|
|
pub struct ContextElem {
|
|
/// The function to call with the context.
|
|
#[required]
|
|
#[internal]
|
|
func: Func,
|
|
}
|
|
|
|
impl Construct for ContextElem {
|
|
fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
|
|
bail!(args.span, "cannot be constructed manually");
|
|
}
|
|
}
|
|
|
|
pub const CONTEXT_RULE: ShowFn<ContextElem> = |elem, engine, styles| {
|
|
let loc = elem.location().unwrap();
|
|
let context = Context::new(Some(loc), Some(styles));
|
|
Ok(elem.func.call::<[Value; 0]>(engine, context.track(), [])?.display())
|
|
};
|