use std::any::TypeId; use std::cmp::Ordering; use std::fmt::{self, Debug}; use std::hash::Hash; use std::ptr::NonNull; use std::sync::LazyLock; use ecow::EcoString; use smallvec::SmallVec; #[doc(inline)] pub use typst_macros::elem; use typst_utils::Static; use crate::diag::SourceResult; use crate::engine::Engine; use crate::foundations::{ cast, Args, Content, Dict, FieldAccessError, Func, ParamInfo, Repr, Scope, Selector, StyleChain, Styles, Value, }; use crate::text::{Lang, Region}; /// A document element. #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Element(Static); impl Element { /// Get the element for `T`. pub fn of() -> Self { T::elem() } /// The element's normal name (e.g. `enum`). pub fn name(self) -> &'static str { self.0.name } /// The element's title case name, for use in documentation /// (e.g. `Numbered List`). pub fn title(&self) -> &'static str { self.0.title } /// Documentation for the element (as Markdown). pub fn docs(&self) -> &'static str { self.0.docs } /// Search keywords for the element. pub fn keywords(&self) -> &'static [&'static str] { self.0.keywords } /// Construct an instance of this element. pub fn construct( self, engine: &mut Engine, args: &mut Args, ) -> SourceResult { (self.0.construct)(engine, args) } /// Execute the set rule for the element and return the resulting style map. pub fn set(self, engine: &mut Engine, mut args: Args) -> SourceResult { let styles = (self.0.set)(engine, &mut args)?; args.finish()?; Ok(styles) } /// Whether the element has the given capability. pub fn can(self) -> bool where C: ?Sized + 'static, { self.can_type_id(TypeId::of::()) } /// Whether the element has the given capability where the capability is /// given by a `TypeId`. pub fn can_type_id(self, type_id: TypeId) -> bool { (self.0.vtable)(type_id).is_some() } /// The VTable for capabilities dispatch. pub fn vtable(self) -> fn(of: TypeId) -> Option> { self.0.vtable } /// Create a selector for this element. pub fn select(self) -> Selector { Selector::Elem(self, None) } /// Create a selector for this element, filtering for those that /// [fields](crate::foundations::Content::field) match the given argument. pub fn where_(self, fields: SmallVec<[(u8, Value); 1]>) -> Selector { Selector::Elem(self, Some(fields)) } /// The element's associated scope of sub-definition. pub fn scope(&self) -> &'static Scope { &(self.0).0.scope } /// Details about the element's fields. pub fn params(&self) -> &'static [ParamInfo] { &(self.0).0.params } /// Extract the field ID for the given field name. pub fn field_id(&self, name: &str) -> Option { if name == "label" { return Some(255); } (self.0.field_id)(name) } /// Extract the field name for the given field ID. pub fn field_name(&self, id: u8) -> Option<&'static str> { if id == 255 { return Some("label"); } (self.0.field_name)(id) } /// Extract the value of the field for the given field ID and style chain. pub fn field_from_styles( &self, id: u8, styles: StyleChain, ) -> Result { (self.0.field_from_styles)(id, styles) } /// The element's local name, if any. pub fn local_name(&self, lang: Lang, region: Option) -> Option<&'static str> { (self.0).0.local_name.map(|f| f(lang, region)) } } impl Debug for Element { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Element({})", self.name()) } } impl Repr for Element { fn repr(&self) -> EcoString { self.name().into() } } impl Ord for Element { fn cmp(&self, other: &Self) -> Ordering { self.name().cmp(other.name()) } } impl PartialOrd for Element { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } cast! { Element, self => Value::Func(self.into()), v: Func => v.element().ok_or("expected element")?, } /// A Typst element that is defined by a native Rust type. pub trait NativeElement: Debug + Clone + PartialEq + Hash + Construct + Set + Capable + Fields + Repr + Send + Sync + 'static { /// Get the element for the native Rust element. fn elem() -> Element where Self: Sized, { Element::from(Self::data()) } /// Pack the element into type-erased content. fn pack(self) -> Content where Self: Sized, { Content::new(self) } /// Get the element data for the native Rust element. fn data() -> &'static NativeElementData where Self: Sized; } /// Used to cast an element to a trait object for a trait it implements. /// /// # Safety /// If the `vtable` function returns `Some(p)`, then `p` must be a valid pointer /// to a vtable of `Packed` w.r.t to the trait `C` where `capability` is /// `TypeId::of::()`. pub unsafe trait Capable { /// Get the pointer to the vtable for the given capability / trait. fn vtable(capability: TypeId) -> Option>; } /// Defines how fields of an element are accessed. pub trait Fields { /// An enum with the fields of the element. type Enum where Self: Sized; /// Whether the element has the given field set. fn has(&self, id: u8) -> bool; /// Get the field with the given field ID. fn field(&self, id: u8) -> Result; /// Get the field with the given ID in the presence of styles. fn field_with_styles( &self, id: u8, styles: StyleChain, ) -> Result; /// Get the field with the given ID from the styles. fn field_from_styles(id: u8, styles: StyleChain) -> Result where Self: Sized; /// Resolve all fields with the styles and save them in-place. fn materialize(&mut self, styles: StyleChain); /// Get the fields of the element. fn fields(&self) -> Dict; } /// An element's constructor function. pub trait Construct { /// Construct an element from the arguments. /// /// This is passed only the arguments that remain after execution of the /// element's set rule. fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult where Self: Sized; } /// An element's set rule. pub trait Set { /// Parse relevant arguments into style properties for this element. fn set(engine: &mut Engine, args: &mut Args) -> SourceResult where Self: Sized; } /// Defines a native element. #[derive(Debug)] pub struct NativeElementData { /// The element's normal name (e.g. `align`), as exposed to Typst. pub name: &'static str, /// The element's title case name (e.g. `Align`). pub title: &'static str, /// The documentation for this element as a string. pub docs: &'static str, /// A list of alternate search terms for this element. pub keywords: &'static [&'static str], /// The constructor for this element (see [`Construct`]). pub construct: fn(&mut Engine, &mut Args) -> SourceResult, /// Executes this element's set rule (see [`Set`]). pub set: fn(&mut Engine, &mut Args) -> SourceResult, /// Gets the vtable for one of this element's capabilities /// (see [`Capable`]). pub vtable: fn(capability: TypeId) -> Option>, /// Gets the numeric index of this field by its name. pub field_id: fn(name: &str) -> Option, /// Gets the name of a field by its numeric index. pub field_name: fn(u8) -> Option<&'static str>, /// Get the field with the given ID in the presence of styles (see [`Fields`]). pub field_from_styles: fn(u8, StyleChain) -> Result, /// Gets the localized name for this element (see [`LocalName`][crate::text::LocalName]). pub local_name: Option) -> &'static str>, pub scope: LazyLock, /// A list of parameter information for each field. pub params: LazyLock>, } impl From<&'static NativeElementData> for Element { fn from(data: &'static NativeElementData) -> Self { Self(Static(data)) } } cast! { &'static NativeElementData, self => Element::from(self).into_value(), } /// Synthesize fields on an element. This happens before execution of any show /// rule. pub trait Synthesize { /// Prepare the element for show rule application. fn synthesize(&mut self, engine: &mut Engine, styles: StyleChain) -> SourceResult<()>; } /// Defines a built-in show rule for an element. pub trait Show { /// Execute the base recipe for this element. fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult; } /// Defines built-in show set rules for an element. /// /// This is a bit more powerful than a user-defined show-set because it can /// access the element's fields. pub trait ShowSet { /// Finalize the fully realized form of the element. Use this for effects /// that should work even in the face of a user-defined show rule. fn show_set(&self, styles: StyleChain) -> Styles; }