typst_library/foundations/
func.rs

1#[doc(inline)]
2pub use typst_macros::func;
3
4use std::fmt::{self, Debug, Formatter};
5use std::sync::{Arc, LazyLock};
6
7use comemo::{Tracked, TrackedMut};
8use ecow::{EcoString, eco_format};
9use typst_syntax::{Span, SyntaxNode, ast};
10use typst_utils::{LazyHash, Static, singleton};
11
12use crate::diag::{At, DeprecationSink, SourceResult, StrResult, bail};
13use crate::engine::Engine;
14use crate::foundations::{
15    Args, Bytes, CastInfo, Content, Context, Element, IntoArgs, PluginFunc, Scope,
16    Selector, Type, Value, cast, repr, scope, ty,
17};
18
19/// A mapping from argument values to a return value.
20///
21/// You can call a function by writing a comma-separated list of function
22/// _arguments_ enclosed in parentheses directly after the function name.
23/// Additionally, you can pass any number of trailing content block arguments
24/// to a function _after_ the normal argument list. If the normal argument list
25/// would become empty, it can be omitted. Typst supports positional and named
26/// arguments. The former are identified by position and type, while the latter
27/// are written as `name: value`.
28///
29/// Within math mode, function calls have special behaviour. See the
30/// [math documentation]($category/math) for more details.
31///
32/// # Example
33/// ```example
34/// // Call a function.
35/// #list([A], [B])
36///
37/// // Named arguments and trailing
38/// // content blocks.
39/// #enum(start: 2)[A][B]
40///
41/// // Version without parentheses.
42/// #list[A][B]
43/// ```
44///
45/// Functions are a fundamental building block of Typst. Typst provides
46/// functions for a variety of typesetting tasks. Moreover, the markup you write
47/// is backed by functions and all styling happens through functions. This
48/// reference lists all available functions and how you can use them. Please
49/// also refer to the documentation about [set]($styling/#set-rules) and
50/// [show]($styling/#show-rules) rules to learn about additional ways you can
51/// work with functions in Typst.
52///
53/// # Element functions
54/// Some functions are associated with _elements_ like [headings]($heading) or
55/// [tables]($table). When called, these create an element of their respective
56/// kind. In contrast to normal functions, they can further be used in [set
57/// rules]($styling/#set-rules), [show rules]($styling/#show-rules), and
58/// [selectors]($selector).
59///
60/// # Function scopes
61/// Functions can hold related definitions in their own scope, similar to a
62/// [module]($scripting/#modules). Examples of this are [`assert.eq`] or
63/// [`list.item`]. However, this feature is currently only available for
64/// built-in functions.
65///
66/// # Defining functions
67/// You can define your own function with a [let binding]($scripting/#bindings)
68/// that has a parameter list after the binding's name. The parameter list can
69/// contain mandatory positional parameters, named parameters with default
70/// values and [argument sinks]($arguments).
71///
72/// The right-hand side of a function binding is the function body, which can be
73/// a block or any other expression. It defines the function's return value and
74/// can depend on the parameters. If the function body is a [code
75/// block]($scripting/#blocks), the return value is the result of joining the
76/// values of each expression in the block.
77///
78/// Within a function body, the `return` keyword can be used to exit early and
79/// optionally specify a return value. If no explicit return value is given, the
80/// body evaluates to the result of joining all expressions preceding the
81/// `return`.
82///
83/// Functions that don't return any meaningful value return [`none`] instead.
84/// The return type of such functions is not explicitly specified in the
85/// documentation. (An example of this is [`array.push`]).
86///
87/// ```example
88/// #let alert(body, fill: red) = {
89///   set text(white)
90///   set align(center)
91///   rect(
92///     fill: fill,
93///     inset: 8pt,
94///     radius: 4pt,
95///     [*Warning:\ #body*],
96///   )
97/// }
98///
99/// #alert[
100///   Danger is imminent!
101/// ]
102///
103/// #alert(fill: blue)[
104///   KEEP OFF TRACKS
105/// ]
106/// ```
107///
108/// # Importing functions
109/// Functions can be imported from one file ([`module`]($scripting/#modules)) into
110/// another using `{import}`. For example, assume that we have defined the `alert`
111/// function from the previous example in a file called `foo.typ`. We can import
112/// it into another file by writing `{import "foo.typ": alert}`.
113///
114/// # Unnamed functions { #unnamed }
115/// You can also create an unnamed function without creating a binding by
116/// specifying a parameter list followed by `=>` and the function body. If your
117/// function has just one parameter, the parentheses around the parameter list
118/// are optional. Unnamed functions are mainly useful for show rules, but also
119/// for settable properties that take functions like the page function's
120/// [`footer`]($page.footer) property.
121///
122/// ```example
123/// #show "once?": it => [#it #it]
124/// once?
125/// ```
126///
127/// # Note on function purity
128/// In Typst, all functions are _pure._ This means that for the same
129/// arguments, they always return the same result. They cannot "remember" things to
130/// produce another value when they are called a second time.
131///
132/// The only exception are built-in methods like
133/// [`array.push(value)`]($array.push). These can modify the values they are
134/// called on.
135#[ty(scope, cast, name = "function")]
136#[derive(Clone, Hash)]
137#[allow(clippy::derived_hash_with_manual_eq)]
138pub struct Func {
139    /// The internal representation.
140    repr: Repr,
141    /// The span with which errors are reported when this function is called.
142    span: Span,
143}
144
145/// The different kinds of function representations.
146#[derive(Clone, PartialEq, Hash)]
147enum Repr {
148    /// A native Rust function.
149    Native(Static<NativeFuncData>),
150    /// A function for an element.
151    Element(Element),
152    /// A user-defined closure.
153    Closure(Arc<LazyHash<Closure>>),
154    /// A plugin WebAssembly function.
155    Plugin(Arc<PluginFunc>),
156    /// A nested function with pre-applied arguments.
157    With(Arc<(Func, Args)>),
158}
159
160impl Func {
161    /// The function's name (e.g. `min`).
162    ///
163    /// Returns `None` if this is an anonymous closure.
164    pub fn name(&self) -> Option<&str> {
165        match &self.repr {
166            Repr::Native(native) => Some(native.name),
167            Repr::Element(elem) => Some(elem.name()),
168            Repr::Closure(closure) => closure.name(),
169            Repr::Plugin(func) => Some(func.name()),
170            Repr::With(with) => with.0.name(),
171        }
172    }
173
174    /// The function's title case name, for use in documentation (e.g. `Minimum`).
175    ///
176    /// Returns `None` if this is a closure.
177    pub fn title(&self) -> Option<&'static str> {
178        match &self.repr {
179            Repr::Native(native) => Some(native.title),
180            Repr::Element(elem) => Some(elem.title()),
181            Repr::Closure(_) => None,
182            Repr::Plugin(_) => None,
183            Repr::With(with) => with.0.title(),
184        }
185    }
186
187    /// Documentation for the function (as Markdown).
188    pub fn docs(&self) -> Option<&'static str> {
189        match &self.repr {
190            Repr::Native(native) => Some(native.docs),
191            Repr::Element(elem) => Some(elem.docs()),
192            Repr::Closure(_) => None,
193            Repr::Plugin(_) => None,
194            Repr::With(with) => with.0.docs(),
195        }
196    }
197
198    /// Whether the function is known to be contextual.
199    pub fn contextual(&self) -> Option<bool> {
200        match &self.repr {
201            Repr::Native(native) => Some(native.contextual),
202            _ => None,
203        }
204    }
205
206    /// Get details about this function's parameters if available.
207    pub fn params(&self) -> Option<&'static [ParamInfo]> {
208        match &self.repr {
209            Repr::Native(native) => Some(&native.0.params),
210            Repr::Element(elem) => Some(elem.params()),
211            Repr::Closure(_) => None,
212            Repr::Plugin(_) => None,
213            Repr::With(with) => with.0.params(),
214        }
215    }
216
217    /// Get the parameter info for a parameter with the given name if it exist.
218    pub fn param(&self, name: &str) -> Option<&'static ParamInfo> {
219        self.params()?.iter().find(|param| param.name == name)
220    }
221
222    /// Get details about the function's return type.
223    pub fn returns(&self) -> Option<&'static CastInfo> {
224        match &self.repr {
225            Repr::Native(native) => Some(&native.0.returns),
226            Repr::Element(_) => {
227                Some(singleton!(CastInfo, CastInfo::Type(Type::of::<Content>())))
228            }
229            Repr::Closure(_) => None,
230            Repr::Plugin(_) => None,
231            Repr::With(with) => with.0.returns(),
232        }
233    }
234
235    /// Search keywords for the function.
236    pub fn keywords(&self) -> &'static [&'static str] {
237        match &self.repr {
238            Repr::Native(native) => native.keywords,
239            Repr::Element(elem) => elem.keywords(),
240            Repr::Closure(_) => &[],
241            Repr::Plugin(_) => &[],
242            Repr::With(with) => with.0.keywords(),
243        }
244    }
245
246    /// The function's associated scope of sub-definition.
247    pub fn scope(&self) -> Option<&'static Scope> {
248        match &self.repr {
249            Repr::Native(native) => Some(&native.0.scope),
250            Repr::Element(elem) => Some(elem.scope()),
251            Repr::Closure(_) => None,
252            Repr::Plugin(_) => None,
253            Repr::With(with) => with.0.scope(),
254        }
255    }
256
257    /// Get a field from this function's scope, if possible.
258    pub fn field(
259        &self,
260        field: &str,
261        sink: impl DeprecationSink,
262    ) -> StrResult<&'static Value> {
263        let scope =
264            self.scope().ok_or("cannot access fields on user-defined functions")?;
265        match scope.get(field) {
266            Some(binding) => Ok(binding.read_checked(sink)),
267            None => match self.name() {
268                Some(name) => bail!("function `{name}` does not contain field `{field}`"),
269                None => bail!("function does not contain field `{field}`"),
270            },
271        }
272    }
273
274    /// Extract the element function, if it is one.
275    pub fn element(&self) -> Option<Element> {
276        match self.repr {
277            Repr::Element(func) => Some(func),
278            _ => None,
279        }
280    }
281
282    /// Extract the plugin function, if it is one.
283    pub fn to_plugin(&self) -> Option<&PluginFunc> {
284        match &self.repr {
285            Repr::Plugin(func) => Some(func),
286            _ => None,
287        }
288    }
289
290    /// Call the function with the given context and arguments.
291    pub fn call<A: IntoArgs>(
292        &self,
293        engine: &mut Engine,
294        context: Tracked<Context>,
295        args: A,
296    ) -> SourceResult<Value> {
297        self.call_impl(engine, context, args.into_args(self.span))
298    }
299
300    /// Non-generic implementation of `call`.
301    #[typst_macros::time(name = "func call", span = self.span())]
302    fn call_impl(
303        &self,
304        engine: &mut Engine,
305        context: Tracked<Context>,
306        mut args: Args,
307    ) -> SourceResult<Value> {
308        match &self.repr {
309            Repr::Native(native) => {
310                let value = (native.function.0)(engine, context, &mut args)?;
311                args.finish()?;
312                Ok(value)
313            }
314            Repr::Element(func) => {
315                let value = func.construct(engine, &mut args)?;
316                args.finish()?;
317                Ok(Value::Content(value))
318            }
319            Repr::Closure(closure) => (engine.routines.eval_closure)(
320                self,
321                closure,
322                engine.routines,
323                engine.world,
324                engine.introspector,
325                engine.traced,
326                TrackedMut::reborrow_mut(&mut engine.sink),
327                engine.route.track(),
328                context,
329                args,
330            ),
331            Repr::Plugin(func) => {
332                let inputs = args.all::<Bytes>()?;
333                let output = func.call(inputs).at(args.span)?;
334                args.finish()?;
335                Ok(Value::Bytes(output))
336            }
337            Repr::With(with) => {
338                args.items = with.1.items.iter().cloned().chain(args.items).collect();
339                with.0.call(engine, context, args)
340            }
341        }
342    }
343
344    /// The function's span.
345    pub fn span(&self) -> Span {
346        self.span
347    }
348
349    /// Attach a span to this function if it doesn't already have one.
350    pub fn spanned(mut self, span: Span) -> Self {
351        if self.span.is_detached() {
352            self.span = span;
353        }
354        self
355    }
356}
357
358#[scope]
359impl Func {
360    /// Returns a new function that has the given arguments pre-applied.
361    #[func]
362    pub fn with(
363        self,
364        args: &mut Args,
365        /// The arguments to apply to the function.
366        #[external]
367        #[variadic]
368        arguments: Vec<Value>,
369    ) -> Func {
370        let span = self.span;
371        Self {
372            repr: Repr::With(Arc::new((self, args.take()))),
373            span,
374        }
375    }
376
377    /// Returns a selector that filters for elements belonging to this function
378    /// whose fields have the values of the given arguments.
379    ///
380    /// ```example
381    /// #show heading.where(level: 2): set text(blue)
382    /// = Section
383    /// == Subsection
384    /// === Sub-subsection
385    /// ```
386    #[func]
387    pub fn where_(
388        self,
389        args: &mut Args,
390        /// The fields to filter for.
391        #[variadic]
392        #[external]
393        fields: Vec<Value>,
394    ) -> StrResult<Selector> {
395        let fields = args.to_named();
396        args.items.retain(|arg| arg.name.is_none());
397
398        let element = self
399            .element()
400            .ok_or("`where()` can only be called on element functions")?;
401
402        let fields = fields
403            .into_iter()
404            .map(|(key, value)| {
405                element.field_id(&key).map(|id| (id, value)).ok_or_else(|| {
406                    eco_format!(
407                        "element `{}` does not have field `{}`",
408                        element.name(),
409                        key
410                    )
411                })
412            })
413            .collect::<StrResult<smallvec::SmallVec<_>>>()?;
414
415        Ok(element.where_(fields))
416    }
417}
418
419impl Debug for Func {
420    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
421        write!(f, "Func({})", self.name().unwrap_or(".."))
422    }
423}
424
425impl repr::Repr for Func {
426    fn repr(&self) -> EcoString {
427        const DEFAULT: &str = "(..) => ..";
428        match &self.repr {
429            Repr::Native(native) => native.name.into(),
430            Repr::Element(elem) => elem.name().into(),
431            Repr::Closure(closure) => closure.name().unwrap_or(DEFAULT).into(),
432            Repr::Plugin(func) => func.name().clone(),
433            Repr::With(_) => DEFAULT.into(),
434        }
435    }
436}
437
438impl PartialEq for Func {
439    fn eq(&self, other: &Self) -> bool {
440        self.repr == other.repr
441    }
442}
443
444impl PartialEq<&'static NativeFuncData> for Func {
445    fn eq(&self, other: &&'static NativeFuncData) -> bool {
446        match &self.repr {
447            Repr::Native(native) => *native == Static(*other),
448            _ => false,
449        }
450    }
451}
452
453impl PartialEq<Element> for Func {
454    fn eq(&self, other: &Element) -> bool {
455        match &self.repr {
456            Repr::Element(elem) => elem == other,
457            _ => false,
458        }
459    }
460}
461
462impl From<Repr> for Func {
463    fn from(repr: Repr) -> Self {
464        Self { repr, span: Span::detached() }
465    }
466}
467
468impl From<&'static NativeFuncData> for Func {
469    fn from(data: &'static NativeFuncData) -> Self {
470        Repr::Native(Static(data)).into()
471    }
472}
473
474impl From<Element> for Func {
475    fn from(func: Element) -> Self {
476        Repr::Element(func).into()
477    }
478}
479
480impl From<Closure> for Func {
481    fn from(closure: Closure) -> Self {
482        Repr::Closure(Arc::new(LazyHash::new(closure))).into()
483    }
484}
485
486impl From<PluginFunc> for Func {
487    fn from(func: PluginFunc) -> Self {
488        Repr::Plugin(Arc::new(func)).into()
489    }
490}
491
492/// A Typst function that is defined by a native Rust type that shadows a
493/// native Rust function.
494pub trait NativeFunc {
495    /// Get the function for the native Rust type.
496    fn func() -> Func {
497        Func::from(Self::data())
498    }
499
500    /// Get the function data for the native Rust function.
501    fn data() -> &'static NativeFuncData;
502}
503
504/// Defines a native function.
505#[derive(Debug)]
506pub struct NativeFuncData {
507    /// The implementation of the function.
508    pub function: NativeFuncPtr,
509    /// The function's normal name (e.g. `align`), as exposed to Typst.
510    pub name: &'static str,
511    /// The function's title case name (e.g. `Align`).
512    pub title: &'static str,
513    /// The documentation for this function as a string.
514    pub docs: &'static str,
515    /// A list of alternate search terms for this function.
516    pub keywords: &'static [&'static str],
517    /// Whether this function makes use of context.
518    pub contextual: bool,
519    /// Definitions in the scope of the function.
520    pub scope: DynLazyLock<Scope>,
521    /// A list of parameter information for each parameter.
522    pub params: DynLazyLock<Vec<ParamInfo>>,
523    /// Information about the return value of this function.
524    pub returns: DynLazyLock<CastInfo>,
525}
526
527cast! {
528    &'static NativeFuncData,
529    self => Func::from(self).into_value(),
530}
531
532/// A pointer to a native function's implementation.
533pub struct NativeFuncPtr(pub &'static NativeFuncSignature);
534
535/// The signature of a native function's implementation.
536type NativeFuncSignature =
537    dyn Fn(&mut Engine, Tracked<Context>, &mut Args) -> SourceResult<Value> + Send + Sync;
538
539impl Debug for NativeFuncPtr {
540    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
541        f.pad("NativeFuncPtr(..)")
542    }
543}
544
545/// A `LazyLock` that uses a static closure for initialization instead of only
546/// working with function pointers.
547///
548/// Can be created from a normal function or closure by prepending with a `&`,
549/// e.g. `LazyLock::new(&|| "hello")`. Can be created from a dynamic closure
550/// by allocating and then leaking it. This is equivalent to having it
551/// statically allocated, but allows for it to be generated at runtime.
552type DynLazyLock<T> = LazyLock<T, &'static (dyn Fn() -> T + Send + Sync)>;
553
554/// Describes a function parameter.
555#[derive(Debug, Clone)]
556pub struct ParamInfo {
557    /// The parameter's name.
558    pub name: &'static str,
559    /// Documentation for the parameter.
560    pub docs: &'static str,
561    /// Describe what values this parameter accepts.
562    pub input: CastInfo,
563    /// Creates an instance of the parameter's default value.
564    pub default: Option<fn() -> Value>,
565    /// Is the parameter positional?
566    pub positional: bool,
567    /// Is the parameter named?
568    ///
569    /// Can be true even if `positional` is true if the parameter can be given
570    /// in both variants.
571    pub named: bool,
572    /// Can the parameter be given any number of times?
573    pub variadic: bool,
574    /// Is the parameter required?
575    pub required: bool,
576    /// Is the parameter settable with a set rule?
577    pub settable: bool,
578}
579
580/// Distinguishes between variants of closures.
581#[derive(Debug, Hash)]
582pub enum ClosureNode {
583    /// A regular closure. Must always be castable to a `ast::Closure`.
584    Closure(SyntaxNode),
585    /// Synthetic closure used for `context` expressions. Can be any `ast::Expr`
586    /// and has no parameters.
587    Context(SyntaxNode),
588}
589
590/// A user-defined closure.
591#[derive(Debug, Hash)]
592pub struct Closure {
593    /// The closure's syntax node.
594    pub node: ClosureNode,
595    /// Default values of named parameters.
596    pub defaults: Vec<Value>,
597    /// Captured values from outer scopes.
598    pub captured: Scope,
599    /// The number of positional parameters in the closure.
600    pub num_pos_params: usize,
601}
602
603impl Closure {
604    /// The name of the closure.
605    pub fn name(&self) -> Option<&str> {
606        match self.node {
607            ClosureNode::Closure(ref node) => {
608                node.cast::<ast::Closure>()?.name().map(|ident| ident.as_str())
609            }
610            _ => None,
611        }
612    }
613}
614
615cast! {
616    Closure,
617    self => Value::Func(self.into()),
618}