typst_library/foundations/content/
element.rs

1use std::any::TypeId;
2use std::cmp::Ordering;
3use std::fmt::{self, Debug};
4use std::hash::Hash;
5use std::sync::OnceLock;
6
7use ecow::EcoString;
8use smallvec::SmallVec;
9use typst_utils::Static;
10
11use crate::diag::SourceResult;
12use crate::engine::Engine;
13use crate::foundations::{
14    Args, Content, ContentVtable, FieldAccessError, Func, ParamInfo, Repr, Scope,
15    Selector, StyleChain, Styles, Value, cast,
16};
17use crate::text::{Lang, Region};
18
19/// A document element.
20#[derive(Copy, Clone, Eq, PartialEq, Hash)]
21pub struct Element(Static<ContentVtable>);
22
23impl Element {
24    /// Get the element for `T`.
25    pub const fn of<T: NativeElement>() -> Self {
26        T::ELEM
27    }
28
29    /// Get the element for `T`.
30    pub const fn from_vtable(vtable: &'static ContentVtable) -> Self {
31        Self(Static(vtable))
32    }
33
34    /// The element's normal name (e.g. `enum`).
35    pub fn name(self) -> &'static str {
36        self.vtable().name
37    }
38
39    /// The element's title case name, for use in documentation
40    /// (e.g. `Numbered List`).
41    pub fn title(&self) -> &'static str {
42        self.vtable().title
43    }
44
45    /// Documentation for the element (as Markdown).
46    pub fn docs(&self) -> &'static str {
47        self.vtable().docs
48    }
49
50    /// Search keywords for the element.
51    pub fn keywords(&self) -> &'static [&'static str] {
52        self.vtable().keywords
53    }
54
55    /// Construct an instance of this element.
56    pub fn construct(
57        self,
58        engine: &mut Engine,
59        args: &mut Args,
60    ) -> SourceResult<Content> {
61        (self.vtable().construct)(engine, args)
62    }
63
64    /// Execute the set rule for the element and return the resulting style map.
65    pub fn set(self, engine: &mut Engine, mut args: Args) -> SourceResult<Styles> {
66        let styles = (self.vtable().set)(engine, &mut args)?;
67        args.finish()?;
68        Ok(styles)
69    }
70
71    /// Whether the element has the given capability.
72    pub fn can<C>(self) -> bool
73    where
74        C: ?Sized + 'static,
75    {
76        self.can_type_id(TypeId::of::<C>())
77    }
78
79    /// Whether the element has the given capability where the capability is
80    /// given by a `TypeId`.
81    pub fn can_type_id(self, type_id: TypeId) -> bool {
82        (self.vtable().capability)(type_id).is_some()
83    }
84
85    /// Create a selector for this element.
86    pub fn select(self) -> Selector {
87        Selector::Elem(self, None)
88    }
89
90    /// Create a selector for this element, filtering for those that
91    /// [fields](crate::foundations::Content::field) match the given argument.
92    pub fn where_(self, fields: SmallVec<[(u8, Value); 1]>) -> Selector {
93        Selector::Elem(self, Some(fields))
94    }
95
96    /// The element's associated scope of sub-definition.
97    pub fn scope(&self) -> &'static Scope {
98        (self.vtable().store)().scope.get_or_init(|| (self.vtable().scope)())
99    }
100
101    /// Details about the element's fields.
102    pub fn params(&self) -> &'static [ParamInfo] {
103        (self.vtable().store)().params.get_or_init(|| {
104            self.vtable()
105                .fields
106                .iter()
107                .filter(|field| !field.synthesized)
108                .map(|field| ParamInfo {
109                    name: field.name,
110                    docs: field.docs,
111                    input: (field.input)(),
112                    default: field.default,
113                    positional: field.positional,
114                    named: !field.positional,
115                    variadic: field.variadic,
116                    required: field.required,
117                    settable: field.settable,
118                })
119                .collect()
120        })
121    }
122
123    /// Extract the field ID for the given field name.
124    pub fn field_id(&self, name: &str) -> Option<u8> {
125        if name == "label" {
126            return Some(255);
127        }
128        (self.vtable().field_id)(name)
129    }
130
131    /// Extract the field name for the given field ID.
132    pub fn field_name(&self, id: u8) -> Option<&'static str> {
133        if id == 255 {
134            return Some("label");
135        }
136        self.vtable().field(id).map(|data| data.name)
137    }
138
139    /// Extract the value of the field for the given field ID and style chain.
140    pub fn field_from_styles(
141        &self,
142        id: u8,
143        styles: StyleChain,
144    ) -> Result<Value, FieldAccessError> {
145        self.vtable()
146            .field(id)
147            .and_then(|field| (field.get_from_styles)(styles))
148            .ok_or(FieldAccessError::Unknown)
149    }
150
151    /// The element's local name, if any.
152    pub fn local_name(&self, lang: Lang, region: Option<Region>) -> Option<&'static str> {
153        self.vtable().local_name.map(|f| f(lang, region))
154    }
155
156    /// Retrieves the element's vtable for dynamic dispatch.
157    pub(super) fn vtable(&self) -> &'static ContentVtable {
158        (self.0).0
159    }
160}
161
162impl Debug for Element {
163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164        write!(f, "Element({})", self.name())
165    }
166}
167
168impl Repr for Element {
169    fn repr(&self) -> EcoString {
170        self.name().into()
171    }
172}
173
174impl Ord for Element {
175    fn cmp(&self, other: &Self) -> Ordering {
176        self.name().cmp(other.name())
177    }
178}
179
180impl PartialOrd for Element {
181    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
182        Some(self.cmp(other))
183    }
184}
185
186cast! {
187    Element,
188    self => Value::Func(self.into()),
189    v: Func => v.element().ok_or("expected element")?,
190}
191
192/// Lazily initialized data for an element.
193#[derive(Default)]
194pub struct LazyElementStore {
195    pub scope: OnceLock<Scope>,
196    pub params: OnceLock<Vec<ParamInfo>>,
197}
198
199impl LazyElementStore {
200    /// Create an empty store.
201    pub const fn new() -> Self {
202        Self { scope: OnceLock::new(), params: OnceLock::new() }
203    }
204}
205
206/// A Typst element that is defined by a native Rust type.
207///
208/// # Safety
209/// `ELEM` must hold the correct `Element` for `Self`.
210pub unsafe trait NativeElement:
211    Debug + Clone + Hash + Construct + Set + Send + Sync + 'static
212{
213    /// The associated element.
214    const ELEM: Element;
215
216    /// Pack the element into type-erased content.
217    fn pack(self) -> Content {
218        Content::new(self)
219    }
220}
221
222/// An element's constructor function.
223pub trait Construct {
224    /// Construct an element from the arguments.
225    ///
226    /// This is passed only the arguments that remain after execution of the
227    /// element's set rule.
228    fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content>
229    where
230        Self: Sized;
231}
232
233/// An element's set rule.
234pub trait Set {
235    /// Parse relevant arguments into style properties for this element.
236    fn set(engine: &mut Engine, args: &mut Args) -> SourceResult<Styles>
237    where
238        Self: Sized;
239}
240
241/// Synthesize fields on an element. This happens before execution of any show
242/// rule.
243pub trait Synthesize {
244    /// Prepare the element for show rule application.
245    fn synthesize(&mut self, engine: &mut Engine, styles: StyleChain)
246    -> SourceResult<()>;
247}
248
249/// Defines built-in show set rules for an element.
250///
251/// This is a bit more powerful than a user-defined show-set because it can
252/// access the element's fields.
253pub trait ShowSet {
254    /// Finalize the fully realized form of the element. Use this for effects
255    /// that should work even in the face of a user-defined show rule.
256    fn show_set(&self, styles: StyleChain) -> Styles;
257}
258
259/// Tries to extract the plain-text representation of the element.
260pub trait PlainText {
261    /// Write this element's plain text into the given buffer.
262    fn plain_text(&self, text: &mut EcoString);
263}