mirror of
https://github.com/typst/typst
synced 2025-05-19 11:35:27 +08:00
334 lines
9.5 KiB
Rust
334 lines
9.5 KiB
Rust
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<NativeElementData>);
|
|
|
|
impl Element {
|
|
/// Get the element for `T`.
|
|
pub fn of<T: NativeElement>() -> 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<Content> {
|
|
(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<Styles> {
|
|
let styles = (self.0.set)(engine, &mut args)?;
|
|
args.finish()?;
|
|
Ok(styles)
|
|
}
|
|
|
|
/// Whether the element has the given capability.
|
|
pub fn can<C>(self) -> bool
|
|
where
|
|
C: ?Sized + 'static,
|
|
{
|
|
self.can_type_id(TypeId::of::<C>())
|
|
}
|
|
|
|
/// 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<NonNull<()>> {
|
|
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<u8> {
|
|
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<Value, FieldAccessError> {
|
|
(self.0.field_from_styles)(id, styles)
|
|
}
|
|
|
|
/// The element's local name, if any.
|
|
pub fn local_name(&self, lang: Lang, region: Option<Region>) -> 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<Ordering> {
|
|
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<Self>` w.r.t to the trait `C` where `capability` is
|
|
/// `TypeId::of::<dyn C>()`.
|
|
pub unsafe trait Capable {
|
|
/// Get the pointer to the vtable for the given capability / trait.
|
|
fn vtable(capability: TypeId) -> Option<NonNull<()>>;
|
|
}
|
|
|
|
/// 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<Value, FieldAccessError>;
|
|
|
|
/// Get the field with the given ID in the presence of styles.
|
|
fn field_with_styles(
|
|
&self,
|
|
id: u8,
|
|
styles: StyleChain,
|
|
) -> Result<Value, FieldAccessError>;
|
|
|
|
/// Get the field with the given ID from the styles.
|
|
fn field_from_styles(id: u8, styles: StyleChain) -> Result<Value, FieldAccessError>
|
|
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<Content>
|
|
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<Styles>
|
|
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<Content>,
|
|
/// Executes this element's set rule (see [`Set`]).
|
|
pub set: fn(&mut Engine, &mut Args) -> SourceResult<Styles>,
|
|
/// Gets the vtable for one of this element's capabilities
|
|
/// (see [`Capable`]).
|
|
pub vtable: fn(capability: TypeId) -> Option<NonNull<()>>,
|
|
/// Gets the numeric index of this field by its name.
|
|
pub field_id: fn(name: &str) -> Option<u8>,
|
|
/// 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<Value, FieldAccessError>,
|
|
/// Gets the localized name for this element (see [`LocalName`][crate::text::LocalName]).
|
|
pub local_name: Option<fn(Lang, Option<Region>) -> &'static str>,
|
|
pub scope: LazyLock<Scope>,
|
|
/// A list of parameter information for each field.
|
|
pub params: LazyLock<Vec<ParamInfo>>,
|
|
}
|
|
|
|
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<Content>;
|
|
}
|
|
|
|
/// 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;
|
|
}
|