summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/foundations/context.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-10-27 19:04:55 +0100
committerGitHub <noreply@github.com>2024-10-27 18:04:55 +0000
commitbe7cfc85d08c545abfac08098b7b33b4bd71f37e (patch)
treef4137fa2aaa57babae1f7603a9b2ed7e688f43d8 /crates/typst-library/src/foundations/context.rs
parentb8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff)
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-library/src/foundations/context.rs')
-rw-r--r--crates/typst-library/src/foundations/context.rs85
1 files changed, 85 insertions, 0 deletions
diff --git a/crates/typst-library/src/foundations/context.rs b/crates/typst-library/src/foundations/context.rs
new file mode 100644
index 00000000..bf4bdcd2
--- /dev/null
+++ b/crates/typst-library/src/foundations/context.rs
@@ -0,0 +1,85 @@
+use comemo::Track;
+
+use crate::diag::{bail, Hint, HintedStrResult, SourceResult};
+use crate::engine::Engine;
+use crate::foundations::{
+ elem, Args, Construct, Content, Func, Packed, Show, StyleChain, Value,
+};
+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, Show)]
+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");
+ }
+}
+
+impl Show for Packed<ContextElem> {
+ #[typst_macros::time(name = "context", span = self.span())]
+ fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
+ let loc = self.location().unwrap();
+ let context = Context::new(Some(loc), Some(styles));
+ Ok(self.func.call::<[Value; 0]>(engine, context.track(), [])?.display())
+ }
+}