Skip to main content

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