From f2971ceffc9a489002047e816bbb71c71095ef1d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 7 Jul 2025 11:51:17 +0200 Subject: [PATCH] New `Content` implementation --- .../src/foundations/content/element.rs | 269 +++++ .../src/foundations/content/field.rs | 564 +++++++++++ .../{content.rs => content/mod.rs} | 476 ++------- .../src/foundations/content/packed.rs | 147 +++ .../src/foundations/content/raw.rs | 387 +++++++ .../src/foundations/content/vtable.rs | 391 +++++++ .../typst-library/src/foundations/element.rs | 952 ------------------ crates/typst-library/src/foundations/mod.rs | 2 - .../typst-library/src/foundations/styles.rs | 182 ++-- crates/typst-macros/src/elem.rs | 101 +- 10 files changed, 1979 insertions(+), 1492 deletions(-) create mode 100644 crates/typst-library/src/foundations/content/element.rs create mode 100644 crates/typst-library/src/foundations/content/field.rs rename crates/typst-library/src/foundations/{content.rs => content/mod.rs} (62%) create mode 100644 crates/typst-library/src/foundations/content/packed.rs create mode 100644 crates/typst-library/src/foundations/content/raw.rs create mode 100644 crates/typst-library/src/foundations/content/vtable.rs delete mode 100644 crates/typst-library/src/foundations/element.rs diff --git a/crates/typst-library/src/foundations/content/element.rs b/crates/typst-library/src/foundations/content/element.rs new file mode 100644 index 000000000..49b0b0f9b --- /dev/null +++ b/crates/typst-library/src/foundations/content/element.rs @@ -0,0 +1,269 @@ +use std::any::TypeId; +use std::cmp::Ordering; +use std::fmt::{self, Debug}; +use std::hash::Hash; +use std::sync::OnceLock; + +use ecow::EcoString; +use smallvec::SmallVec; +use typst_utils::Static; + +use crate::diag::SourceResult; +use crate::engine::Engine; +use crate::foundations::{ + cast, Args, Content, ContentVtable, 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 const fn of() -> Self { + T::ELEM + } + + /// Get the element for `T`. + pub const fn from_vtable(vtable: &'static ContentVtable) -> Self { + Self(Static(vtable)) + } + + /// The element's normal name (e.g. `enum`). + pub fn name(self) -> &'static str { + self.vtable().name + } + + /// The element's title case name, for use in documentation + /// (e.g. `Numbered List`). + pub fn title(&self) -> &'static str { + self.vtable().title + } + + /// Documentation for the element (as Markdown). + pub fn docs(&self) -> &'static str { + self.vtable().docs + } + + /// Search keywords for the element. + pub fn keywords(&self) -> &'static [&'static str] { + self.vtable().keywords + } + + /// Construct an instance of this element. + pub fn construct( + self, + engine: &mut Engine, + args: &mut Args, + ) -> SourceResult { + (self.vtable().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.vtable().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.vtable().capability)(type_id).is_some() + } + + /// 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.vtable().store)().scope.get_or_init(|| (self.vtable().scope)()) + } + + /// Details about the element's fields. + pub fn params(&self) -> &'static [ParamInfo] { + (self.vtable().store)().params.get_or_init(|| { + self.vtable() + .fields + .iter() + .filter(|field| !field.synthesized) + .map(|field| ParamInfo { + name: field.name, + docs: field.docs, + input: (field.input)(), + default: field.default, + positional: field.positional, + named: !field.positional, + variadic: field.variadic, + required: field.required, + settable: field.settable, + }) + .collect() + }) + } + + /// Extract the field ID for the given field name. + pub fn field_id(&self, name: &str) -> Option { + if name == "label" { + return Some(255); + } + (self.vtable().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.vtable().field(id).map(|data| data.name) + } + + /// 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.vtable() + .field(id) + .and_then(|field| (field.get_from_styles)(styles)) + .ok_or(FieldAccessError::Unknown) + } + + /// The element's local name, if any. + pub fn local_name(&self, lang: Lang, region: Option) -> Option<&'static str> { + self.vtable().local_name.map(|f| f(lang, region)) + } + + /// Retrieves the element's vtable for dynamic dispatch. + pub(super) fn vtable(&self) -> &'static ContentVtable { + (self.0).0 + } +} + +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")?, +} + +/// Lazily initialized data for an element. +#[derive(Default)] +pub struct LazyElementStore { + pub scope: OnceLock, + pub params: OnceLock>, +} + +impl LazyElementStore { + /// Create an empty store. + pub const fn new() -> Self { + Self { scope: OnceLock::new(), params: OnceLock::new() } + } +} + +/// A Typst element that is defined by a native Rust type. +/// +/// # Safety +/// `ELEM` must hold the correct `Element` for `Self`. +pub unsafe trait NativeElement: + Debug + Clone + Hash + Construct + Set + Send + Sync + 'static +{ + /// The associated element. + const ELEM: Element; + + /// Pack the element into type-erased content. + fn pack(self) -> Content { + Content::new(self) + } +} + +/// 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; +} + +/// 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; +} + +/// Tries to extract the plain-text representation of the element. +pub trait PlainText { + /// Write this element's plain text into the given buffer. + fn plain_text(&self, text: &mut EcoString); +} diff --git a/crates/typst-library/src/foundations/content/field.rs b/crates/typst-library/src/foundations/content/field.rs new file mode 100644 index 000000000..8d0fe529c --- /dev/null +++ b/crates/typst-library/src/foundations/content/field.rs @@ -0,0 +1,564 @@ +use std::fmt::{self, Debug}; +use std::hash::Hash; +use std::marker::PhantomData; +use std::sync::OnceLock; + +use ecow::{eco_format, EcoString}; + +use crate::foundations::{ + Container, Content, FieldVtable, Fold, FoldFn, IntoValue, NativeElement, Packed, + Property, Reflect, Repr, Resolve, StyleChain, +}; + +/// An accessor for the `I`-th field of the element `E`. Values of this type are +/// generated for each field of an element can be used to interact with this +/// field programmatically, for example to access the style chain, as in +/// `styles.get(TextElem::size)`. +#[derive(Copy, Clone)] +pub struct Field(pub PhantomData); + +impl Field { + /// Creates a new zero-sized accessor. + pub const fn new() -> Self { + Self(PhantomData) + } + + /// The index of the projected field. + pub const fn index(self) -> u8 { + I + } + + /// Creates a dynamic property instance for this field. + /// + /// Prefer [`Content::set`] or + /// [`Styles::set`](crate::foundations::Styles::set) when working with + /// existing content or style value. + pub fn set(self, value: E::Type) -> Property + where + E: SettableProperty, + E::Type: Debug + Clone + Hash + Send + Sync + 'static, + { + Property::new(self, value) + } +} + +impl Default for Field { + fn default() -> Self { + Self::new() + } +} + +/// A field that is present on every instance of the element. +pub trait RequiredField: NativeElement { + type Type: Clone; + + const FIELD: RequiredFieldData; +} + +/// Metadata and routines for a [`RequiredField`]. +pub struct RequiredFieldData, const I: u8> { + name: &'static str, + docs: &'static str, + get: fn(&E) -> &E::Type, +} + +impl, const I: u8> RequiredFieldData { + /// Creates the data from its parts. This is called in the `#[elem]` macro. + pub const fn new( + name: &'static str, + docs: &'static str, + get: fn(&E) -> &E::Type, + ) -> Self { + Self { name, docs, get } + } + + /// Creates the vtable for a `#[required]` field. + pub const fn vtable() -> FieldVtable> + where + E: RequiredField, + E::Type: Reflect + IntoValue + PartialEq, + { + FieldVtable { + name: E::FIELD.name, + docs: E::FIELD.docs, + positional: true, + required: true, + variadic: false, + settable: false, + synthesized: false, + input: || ::input(), + default: None, + has: |_| true, + get: |elem| Some((E::FIELD.get)(elem).clone().into_value()), + get_with_styles: |elem, _| Some((E::FIELD.get)(elem).clone().into_value()), + get_from_styles: |_| None, + materialize: |_, _| {}, + eq: |a, b| (E::FIELD.get)(a) == (E::FIELD.get)(b), + } + } + + /// Creates the vtable for a `#[variadic]` field. + pub const fn vtable_variadic() -> FieldVtable> + where + E: RequiredField, + E::Type: Container + IntoValue + PartialEq, + ::Inner: Reflect, + { + FieldVtable { + name: E::FIELD.name, + docs: E::FIELD.docs, + positional: true, + required: true, + variadic: true, + settable: false, + synthesized: false, + input: || <::Inner as Reflect>::input(), + default: None, + has: |_| true, + get: |elem| Some((E::FIELD.get)(elem).clone().into_value()), + get_with_styles: |elem, _| Some((E::FIELD.get)(elem).clone().into_value()), + get_from_styles: |_| None, + materialize: |_, _| {}, + eq: |a, b| (E::FIELD.get)(a) == (E::FIELD.get)(b), + } + } +} + +/// A field that is initially unset, but may be set through a +/// [`Synthesize`](crate::foundations::Synthesize) implementation. +pub trait SynthesizedField: NativeElement { + type Type: Clone; + + const FIELD: SynthesizedFieldData; +} + +/// Metadata and routines for a [`SynthesizedField`]. +pub struct SynthesizedFieldData, const I: u8> { + name: &'static str, + docs: &'static str, + get: fn(&E) -> &Option, +} + +impl, const I: u8> SynthesizedFieldData { + /// Creates the data from its parts. This is called in the `#[elem]` macro. + pub const fn new( + name: &'static str, + docs: &'static str, + get: fn(&E) -> &Option, + ) -> Self { + Self { name, docs, get } + } + + /// Creates type-erased metadata and routines for a `#[synthesized]` field. + pub const fn vtable() -> FieldVtable> + where + E: SynthesizedField, + E::Type: Reflect + IntoValue + PartialEq, + { + FieldVtable { + name: E::FIELD.name, + docs: E::FIELD.docs, + positional: false, + required: false, + variadic: false, + settable: false, + synthesized: true, + input: || ::input(), + default: None, + has: |elem| (E::FIELD.get)(elem).is_some(), + get: |elem| (E::FIELD.get)(elem).clone().map(|v| v.into_value()), + get_with_styles: |elem, _| { + (E::FIELD.get)(elem).clone().map(|v| v.into_value()) + }, + get_from_styles: |_| None, + materialize: |_, _| {}, + // Synthesized fields don't affect equality. + eq: |_, _| true, + } + } +} + +/// A field that is not actually there. It's only visible in the docs. +pub trait ExternalField: NativeElement { + type Type; + + const FIELD: ExternalFieldData; +} + +/// Metadata for an [`ExternalField`]. +pub struct ExternalFieldData, const I: u8> { + name: &'static str, + docs: &'static str, + default: fn() -> E::Type, +} + +impl, const I: u8> ExternalFieldData { + /// Creates the data from its parts. This is called in the `#[elem]` macro. + pub const fn new( + name: &'static str, + docs: &'static str, + default: fn() -> E::Type, + ) -> Self { + Self { name, docs, default } + } + + /// Creates type-erased metadata and routines for an `#[external]` field. + pub const fn vtable() -> FieldVtable> + where + E: ExternalField, + E::Type: Reflect + IntoValue, + { + FieldVtable { + name: E::FIELD.name, + docs: E::FIELD.docs, + positional: false, + required: false, + variadic: false, + settable: false, + synthesized: false, + input: || ::input(), + default: Some(|| (E::FIELD.default)().into_value()), + has: |_| false, + get: |_| None, + get_with_styles: |_, _| None, + get_from_styles: |_| None, + materialize: |_, _| {}, + eq: |_, _| true, + } + } +} + +/// A field that has a default value and can be configured via a set rule, but +/// can also present on elements and be present in the constructor. +pub trait SettableField: NativeElement { + type Type: Clone; + + const FIELD: SettableFieldData; +} + +/// Metadata and routines for a [`SettableField`]. +pub struct SettableFieldData, const I: u8> { + get: fn(&E) -> &Settable, + get_mut: fn(&mut E) -> &mut Settable, + property: SettablePropertyData, +} + +impl, const I: u8> SettableFieldData { + /// Creates the data from its parts. This is called in the `#[elem]` macro. + pub const fn new( + name: &'static str, + docs: &'static str, + positional: bool, + get: fn(&E) -> &Settable, + get_mut: fn(&mut E) -> &mut Settable, + default: fn() -> E::Type, + slot: fn() -> &'static OnceLock, + ) -> Self { + Self { + get, + get_mut, + property: SettablePropertyData::new(name, docs, positional, default, slot), + } + } + + /// Ensures that the property is folded on every access. See the + /// documentation of the [`Fold`] trait for more details. + pub const fn with_fold(mut self) -> Self + where + E::Type: Fold, + { + self.property.fold = Some(E::Type::fold); + self + } + + /// Creates type-erased metadata and routines for a normal settable field. + pub const fn vtable() -> FieldVtable> + where + E: SettableField, + E::Type: Reflect + IntoValue + PartialEq, + { + FieldVtable { + name: E::FIELD.property.name, + docs: E::FIELD.property.docs, + positional: E::FIELD.property.positional, + required: false, + variadic: false, + settable: true, + synthesized: false, + input: || ::input(), + default: Some(|| E::default().into_value()), + has: |elem| (E::FIELD.get)(elem).is_set(), + get: |elem| (E::FIELD.get)(elem).as_option().clone().map(|v| v.into_value()), + get_with_styles: |elem, styles| { + Some((E::FIELD.get)(elem).get_cloned(styles).into_value()) + }, + get_from_styles: |styles| { + Some(styles.get_cloned::(Field::new()).into_value()) + }, + materialize: |elem, styles| { + if !(E::FIELD.get)(elem).is_set() { + (E::FIELD.get_mut)(elem).set(styles.get_cloned::(Field::new())); + } + }, + eq: |a, b| (E::FIELD.get)(a).as_option() == (E::FIELD.get)(b).as_option(), + } + } +} + +/// A field that has a default value and can be configured via a set rule, but +/// is never present on elements. +/// +/// This is provided for all `SettableField` impls through a blanket impl. In +/// the case of `#[ghost]` fields, which only live in the style chain and not in +/// elements, it is also implemented manually. +pub trait SettableProperty: NativeElement { + type Type: Clone; + + const FIELD: SettablePropertyData; + const FOLD: Option> = Self::FIELD.fold; + + /// Produces an instance of the property's default value. + fn default() -> Self::Type { + // Avoid recreating an expensive instance over and over, but also + // avoid unnecessary lazy initialization for cheap types. + if std::mem::needs_drop::() { + Self::default_ref().clone() + } else { + (Self::FIELD.default)() + } + } + + /// Produces a static reference to this property's default value. + fn default_ref() -> &'static Self::Type { + (Self::FIELD.slot)().get_or_init(Self::FIELD.default) + } +} + +impl SettableProperty for T +where + T: SettableField, +{ + type Type = >::Type; + + const FIELD: SettablePropertyData = + >::FIELD.property; +} + +/// Metadata and routines for a [`SettableProperty`]. +pub struct SettablePropertyData, const I: u8> { + name: &'static str, + docs: &'static str, + positional: bool, + default: fn() -> E::Type, + slot: fn() -> &'static OnceLock, + fold: Option>, +} + +impl, const I: u8> SettablePropertyData { + /// Creates the data from its parts. This is called in the `#[elem]` macro. + pub const fn new( + name: &'static str, + docs: &'static str, + positional: bool, + default: fn() -> E::Type, + slot: fn() -> &'static OnceLock, + ) -> Self { + Self { name, docs, positional, default, slot, fold: None } + } + + /// Ensures that the property is folded on every access. See the + /// documentation of the [`Fold`] trait for more details. + pub const fn with_fold(self) -> Self + where + E::Type: Fold, + { + Self { fold: Some(E::Type::fold), ..self } + } + + /// Creates type-erased metadata and routines for a `#[ghost]` field. + pub const fn vtable() -> FieldVtable> + where + E: SettableProperty, + E::Type: Reflect + IntoValue + PartialEq, + { + FieldVtable { + name: E::FIELD.name, + docs: E::FIELD.docs, + positional: E::FIELD.positional, + required: false, + variadic: false, + settable: true, + synthesized: false, + input: || ::input(), + default: Some(|| E::default().into_value()), + has: |_| false, + get: |_| None, + get_with_styles: |_, styles| { + Some(styles.get_cloned::(Field::new()).into_value()) + }, + get_from_styles: |styles| { + Some(styles.get_cloned::(Field::new()).into_value()) + }, + materialize: |_, _| {}, + eq: |_, _| true, + } + } +} + +/// A settable property that can be accessed by reference (because it is not +/// folded). +pub trait RefableProperty: SettableProperty {} + +/// A settable field of an element. +/// +/// The field can be in two states: Unset or present. +/// +/// See [`StyleChain`] for more details about the available accessor methods. +#[derive(Copy, Clone, Hash)] +pub struct Settable(Option) +where + E: SettableProperty; + +impl Settable +where + E: SettableProperty, +{ + /// Creates a new unset instance. + pub fn new() -> Self { + Self(None) + } + + /// Sets the instance to a value. + pub fn set(&mut self, value: E::Type) { + self.0 = Some(value); + } + + /// Clears the value from the instance. + pub fn unset(&mut self) { + self.0 = None; + } + + /// Views the type as an [`Option`] which is `Some` if the type is set + /// and `None` if it is unset. + pub fn as_option(&self) -> &Option { + &self.0 + } + + /// Views the type as a mutable [`Option`]. + pub fn as_option_mut(&mut self) -> &mut Option { + &mut self.0 + } + + /// Whether the field is set. + pub fn is_set(&self) -> bool { + self.0.is_some() + } + + /// Retrieves the value given styles. The styles are used if the value is + /// unset. + pub fn get<'a>(&'a self, styles: StyleChain<'a>) -> E::Type + where + E::Type: Copy, + { + self.get_cloned(styles) + } + + /// Retrieves and clones the value given styles. The styles are used if the + /// value is unset or if it needs folding. + pub fn get_cloned<'a>(&'a self, styles: StyleChain<'a>) -> E::Type { + if let Some(fold) = E::FOLD { + let mut res = styles.get_cloned::(Field::new()); + if let Some(value) = &self.0 { + res = fold(value.clone(), res); + } + res + } else if let Some(value) = &self.0 { + value.clone() + } else { + styles.get_cloned::(Field::new()) + } + } + + /// Retrieves a reference to the value given styles. The styles are used if + /// the value is unset. + pub fn get_ref<'a>(&'a self, styles: StyleChain<'a>) -> &'a E::Type + where + E: RefableProperty, + { + if let Some(value) = &self.0 { + value + } else { + styles.get_ref::(Field::new()) + } + } + + /// Retrieves the value and then immediately [resolves](Resolve) it. + pub fn resolve<'a>(&'a self, styles: StyleChain<'a>) -> ::Output + where + E::Type: Resolve, + { + self.get_cloned(styles).resolve(styles) + } +} + +impl Debug for Settable +where + E: SettableProperty, + E::Type: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Default for Settable +where + E: SettableProperty, +{ + fn default() -> Self { + Self(None) + } +} + +impl From> for Settable +where + E: SettableProperty, +{ + fn from(value: Option) -> Self { + Self(value) + } +} + +/// An error arising when trying to access a field of content. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum FieldAccessError { + Unknown, + Unset, +} + +impl FieldAccessError { + /// Formats the error message given the content and the field name. + #[cold] + pub fn message(self, content: &Content, field: &str) -> EcoString { + let elem_name = content.elem().name(); + match self { + FieldAccessError::Unknown => { + eco_format!("{elem_name} does not have field {}", field.repr()) + } + FieldAccessError::Unset => { + eco_format!( + "field {} in {elem_name} is not known at this point", + field.repr() + ) + } + } + } + + /// Formats the error message for an `at` calls without a default value. + #[cold] + pub fn message_no_default(self, content: &Content, field: &str) -> EcoString { + let mut msg = self.message(content, field); + msg.push_str(" and no default was specified"); + msg + } +} diff --git a/crates/typst-library/src/foundations/content.rs b/crates/typst-library/src/foundations/content/mod.rs similarity index 62% rename from crates/typst-library/src/foundations/content.rs rename to crates/typst-library/src/foundations/content/mod.rs index e10ef7bb6..7ba790d88 100644 --- a/crates/typst-library/src/foundations/content.rs +++ b/crates/typst-library/src/foundations/content/mod.rs @@ -1,23 +1,33 @@ -use std::any::TypeId; +mod element; +mod field; +mod packed; +mod raw; +mod vtable; + +pub use self::element::*; +pub use self::field::*; +pub use self::packed::Packed; +pub use self::vtable::{ContentVtable, FieldVtable}; +#[doc(inline)] +pub use typst_macros::elem; + use std::fmt::{self, Debug, Formatter}; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::iter::{self, Sum}; -use std::marker::PhantomData; -use std::ops::{Add, AddAssign, ControlFlow, Deref, DerefMut}; -use std::sync::Arc; +use std::ops::{Add, AddAssign, ControlFlow}; use comemo::Tracked; use ecow::{eco_format, EcoString}; use serde::{Serialize, Serializer}; + use typst_syntax::Span; -use typst_utils::{fat, singleton, LazyHash, SmallBitSet}; +use typst_utils::singleton; use crate::diag::{SourceResult, StrResult}; use crate::engine::Engine; use crate::foundations::{ - elem, func, repr, scope, ty, Context, Dict, Element, Field, IntoValue, Label, - NativeElement, Property, Recipe, RecipeIndex, Repr, Selector, SettableProperty, Str, - Style, StyleChain, Styles, Value, + func, repr, scope, ty, Context, Dict, IntoValue, Label, Property, Recipe, + RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles, Value, }; use crate::introspection::Location; use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides}; @@ -68,43 +78,14 @@ use crate::text::UnderlineElem; /// elements the content is composed of and what fields they have. /// Alternatively, you can inspect the output of the [`repr`] function. #[ty(scope, cast)] -#[derive(Clone, Hash)] -#[allow(clippy::derived_hash_with_manual_eq)] -pub struct Content { - /// The partially element-dependent inner data. - inner: Arc>, - /// The element's source code location. - span: Span, -} - -/// The inner representation behind the `Arc`. -#[derive(Hash)] -struct Inner { - /// An optional label attached to the element. - label: Option