diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-07 15:17:13 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-07 15:17:13 +0100 |
| commit | 25b5bd117529cd04bb789e1988eb3a3db8025a0e (patch) | |
| tree | 2fbb4650903123da047a1f1f11a0abda95286e12 /src/model | |
| parent | 6ab7760822ccd24b4ef126d4737d41f1be15fe19 (diff) | |
Fully untyped model
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 412 | ||||
| -rw-r--r-- | src/model/mod.rs | 2 | ||||
| -rw-r--r-- | src/model/realize.rs | 7 | ||||
| -rw-r--r-- | src/model/styles.rs | 337 | ||||
| -rw-r--r-- | src/model/typeset.rs | 3 |
5 files changed, 300 insertions, 461 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index b10a3409..2af4ae72 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -1,27 +1,24 @@ -use std::any::{Any, TypeId}; -use std::fmt::{self, Debug, Formatter}; +use std::any::TypeId; +use std::fmt::{self, Debug, Formatter, Write}; use std::hash::{Hash, Hasher}; use std::iter::{self, Sum}; use std::ops::{Add, AddAssign}; -use std::sync::Arc; use comemo::Tracked; use ecow::{EcoString, EcoVec}; -use siphasher::sip128::{Hasher128, SipHasher}; -use typst_macros::node; -use super::{capability, capable, Guard, Key, Property, Recipe, Style, StyleMap}; +use super::{node, Guard, Key, Property, Recipe, Style, StyleMap}; use crate::diag::{SourceResult, StrResult}; -use crate::eval::{Args, ParamInfo, Value, Vm}; +use crate::eval::{cast_from_value, Args, Cast, ParamInfo, Value, Vm}; use crate::syntax::Span; -use crate::util::ReadableTypeId; use crate::World; /// Composable representation of styled content. #[derive(Clone, Hash)] pub struct Content { - obj: Arc<dyn Bounds>, + id: NodeId, span: Option<Span>, + fields: EcoVec<(EcoString, Value)>, modifiers: EcoVec<Modifier>, } @@ -30,55 +27,43 @@ pub struct Content { enum Modifier { Prepared, Guard(Guard), - Label(Label), - Field(EcoString, Value), } impl Content { + pub fn new<T: Node>() -> Self { + Self { + id: T::id(), + span: None, + fields: EcoVec::new(), + modifiers: EcoVec::new(), + } + } + /// Create empty content. pub fn empty() -> Self { - SequenceNode(vec![]).pack() + SequenceNode::new(vec![]).pack() } /// Create a new sequence node from multiples nodes. pub fn sequence(seq: Vec<Self>) -> Self { match seq.as_slice() { [_] => seq.into_iter().next().unwrap(), - _ => SequenceNode(seq).pack(), + _ => SequenceNode::new(seq).pack(), } } /// Attach a span to the content. pub fn spanned(mut self, span: Span) -> Self { - if let Some(styled) = self.to_mut::<StyledNode>() { - styled.sub.span = Some(span); - } else if let Some(styled) = self.to::<StyledNode>() { - self = StyledNode { - sub: styled.sub.clone().spanned(span), - map: styled.map.clone(), - } - .pack(); + if let Some(styled) = self.to::<StyledNode>() { + self = StyledNode::new(styled.sub().spanned(span), styled.map()).pack(); } self.span = Some(span); self } /// Attach a label to the content. - pub fn labelled(mut self, label: Label) -> Self { - for (i, modifier) in self.modifiers.iter().enumerate() { - if matches!(modifier, Modifier::Label(_)) { - self.modifiers.make_mut()[i] = Modifier::Label(label); - return self; - } - } - - self.modifiers.push(Modifier::Label(label)); - self - } - - /// Attach a field to the content. - pub fn push_field(&mut self, name: impl Into<EcoString>, value: Value) { - self.modifiers.push(Modifier::Field(name.into(), value)); + pub fn labelled(self, label: Label) -> Self { + self.with_field("label", label) } /// Style this content with a single style property. @@ -87,31 +72,21 @@ impl Content { } /// Style this content with a style entry. - pub fn styled_with_entry(mut self, style: Style) -> Self { - if let Some(styled) = self.to_mut::<StyledNode>() { - styled.map.apply_one(style); - self - } else if let Some(styled) = self.to::<StyledNode>() { - let mut map = styled.map.clone(); - map.apply_one(style); - StyledNode { sub: styled.sub.clone(), map }.pack() - } else { - StyledNode { sub: self, map: style.into() }.pack() - } + pub fn styled_with_entry(self, style: Style) -> Self { + self.styled_with_map(style.into()) } /// Style this content with a full style map. - pub fn styled_with_map(mut self, styles: StyleMap) -> Self { + pub fn styled_with_map(self, styles: StyleMap) -> Self { if styles.is_empty() { - return self; - } - - if let Some(styled) = self.to_mut::<StyledNode>() { - styled.map.apply(styles); - return self; + self + } else if let Some(styled) = self.to::<StyledNode>() { + let mut map = styled.map(); + map.apply(styles); + StyledNode::new(styled.sub(), map).pack() + } else { + StyledNode::new(self, styles).pack() } - - StyledNode { sub: self, map: styles }.pack() } /// Style this content with a recipe, eagerly applying it if possible. @@ -139,12 +114,12 @@ impl Content { impl Content { /// The id of the contained node. pub fn id(&self) -> NodeId { - (*self.obj).id() + self.id } /// The node's human-readable name. pub fn name(&self) -> &'static str { - (*self.obj).name() + self.id.name() } /// The node's span. @@ -154,72 +129,86 @@ impl Content { /// The content's label. pub fn label(&self) -> Option<&Label> { - self.modifiers.iter().find_map(|modifier| match modifier { - Modifier::Label(label) => Some(label), + match self.field("label")? { + Value::Label(label) => Some(label), _ => None, - }) + } } - /// Access a field on this content. - pub fn field(&self, name: &str) -> Option<Value> { - if name == "label" { - return Some(match self.label() { - Some(label) => Value::Label(label.clone()), - None => Value::None, - }); - } + pub fn with_field( + mut self, + name: impl Into<EcoString>, + value: impl Into<Value>, + ) -> Self { + self.push_field(name, value); + self + } - for modifier in &self.modifiers { - if let Modifier::Field(other, value) = modifier { - if name == other { - return Some(value.clone()); - } - } + /// Attach a field to the content. + pub fn push_field(&mut self, name: impl Into<EcoString>, value: impl Into<Value>) { + let name = name.into(); + if let Some(i) = self.fields.iter().position(|(field, _)| *field == name) { + self.fields.make_mut()[i] = (name, value.into()); + } else { + self.fields.push((name, value.into())); } + } - self.obj.field(name) + pub fn field(&self, name: &str) -> Option<&Value> { + static NONE: Value = Value::None; + self.fields + .iter() + .find(|(field, _)| field == name) + .map(|(_, value)| value) + .or_else(|| (name == "label").then(|| &NONE)) + } + + pub fn fields(&self) -> &[(EcoString, Value)] { + &self.fields + } + + #[track_caller] + pub fn cast_field<T: Cast>(&self, name: &str) -> T { + match self.field(name) { + Some(value) => value.clone().cast().unwrap(), + None => field_is_missing(name), + } } /// Whether the contained node is of type `T`. pub fn is<T>(&self) -> bool where - T: Capable + 'static, + T: Node + 'static, { - (*self.obj).as_any().is::<T>() + self.id == NodeId::of::<T>() } /// Cast to `T` if the contained node is of type `T`. pub fn to<T>(&self) -> Option<&T> where - T: Capable + 'static, + T: Node + 'static, { - (*self.obj).as_any().downcast_ref::<T>() + self.is::<T>().then(|| unsafe { std::mem::transmute(self) }) } /// Whether this content has the given capability. pub fn has<C>(&self) -> bool where - C: Capability + ?Sized, + C: ?Sized + 'static, { - self.obj.vtable(TypeId::of::<C>()).is_some() + (self.id.0.vtable)(TypeId::of::<C>()).is_some() } /// Cast to a trait object if this content has the given capability. pub fn with<C>(&self) -> Option<&C> where - C: Capability + ?Sized, + C: ?Sized + 'static, { - let node: &dyn Bounds = &*self.obj; - let vtable = node.vtable(TypeId::of::<C>())?; - let data = node as *const dyn Bounds as *const (); + let vtable = (self.id.0.vtable)(TypeId::of::<C>())?; + let data = self as *const Self as *const (); Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) } - /// Try to cast to a mutable instance of `T`. - fn to_mut<T: 'static>(&mut self) -> Option<&mut T> { - Arc::get_mut(&mut self.obj)?.as_any_mut().downcast_mut::<T>() - } - /// Disable a show rule recipe. #[doc(hidden)] pub fn guarded(mut self, id: Guard) -> Self { @@ -262,12 +251,40 @@ impl Content { pub(super) fn copy_modifiers(&mut self, from: &Content) { self.span = from.span; self.modifiers = from.modifiers.clone(); + if let Some(label) = from.label() { + self.push_field("label", label.clone()) + } } } impl Debug for Content { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.obj.fmt(f) + struct Pad<'a>(&'a str); + impl Debug for Pad<'_> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad(self.0) + } + } + + if let Some(styled) = self.to::<StyledNode>() { + styled.map().fmt(f)?; + styled.sub().fmt(f) + } else if let Some(seq) = self.to::<SequenceNode>() { + f.debug_list().entries(&seq.children()).finish() + } else if self.id.name() == "space" { + ' '.fmt(f) + } else if self.id.name() == "text" { + self.field("text").unwrap().fmt(f) + } else { + f.write_str(self.name())?; + if self.fields.is_empty() { + return Ok(()); + } + f.write_char(' ')?; + f.debug_map() + .entries(self.fields.iter().map(|(name, value)| (Pad(name), value))) + .finish() + } } } @@ -280,27 +297,19 @@ impl Default for Content { impl Add for Content { type Output = Self; - fn add(self, mut rhs: Self) -> Self::Output { - let mut lhs = self; - if let Some(lhs_mut) = lhs.to_mut::<SequenceNode>() { - if let Some(rhs_mut) = rhs.to_mut::<SequenceNode>() { - lhs_mut.0.append(&mut rhs_mut.0); - } else if let Some(rhs) = rhs.to::<SequenceNode>() { - lhs_mut.0.extend(rhs.0.iter().cloned()); - } else { - lhs_mut.0.push(rhs); - } - return lhs; - } - + fn add(self, rhs: Self) -> Self::Output { + let lhs = self; let seq = match (lhs.to::<SequenceNode>(), rhs.to::<SequenceNode>()) { - (Some(lhs), Some(rhs)) => lhs.0.iter().chain(&rhs.0).cloned().collect(), - (Some(lhs), None) => lhs.0.iter().cloned().chain(iter::once(rhs)).collect(), - (None, Some(rhs)) => iter::once(lhs).chain(rhs.0.iter().cloned()).collect(), + (Some(lhs), Some(rhs)) => { + lhs.children().into_iter().chain(rhs.children()).collect() + } + (Some(lhs), None) => { + lhs.children().into_iter().chain(iter::once(rhs)).collect() + } + (None, Some(rhs)) => iter::once(lhs).chain(rhs.children()).collect(), (None, None) => vec![lhs, rhs], }; - - SequenceNode(seq).pack() + SequenceNode::new(seq).pack() } } @@ -316,73 +325,33 @@ impl Sum for Content { } } -trait Bounds: Node + Debug + Sync + Send + 'static { - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&mut self) -> &mut dyn Any; - fn hash128(&self) -> u128; -} - -impl<T> Bounds for T -where - T: Node + Debug + Hash + Sync + Send + 'static, -{ - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn hash128(&self) -> u128 { - let mut state = SipHasher::new(); - self.type_id().hash(&mut state); - self.hash(&mut state); - state.finish128().as_u128() - } -} - -impl Hash for dyn Bounds { - fn hash<H: Hasher>(&self, state: &mut H) { - state.write_u128(self.hash128()); - } -} - /// A node with applied styles. -#[capable] -#[derive(Clone, Hash)] +#[node] pub struct StyledNode { /// The styled content. + #[positional] + #[required] pub sub: Content, + /// The styles. + #[positional] + #[required] pub map: StyleMap, } -#[node] -impl StyledNode {} - -impl Debug for StyledNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.map.fmt(f)?; - self.sub.fmt(f) - } +cast_from_value! { + StyleMap: "style map", } /// A sequence of nodes. /// /// Combines other arbitrary content. So, when you write `[Hi] + [you]` in /// Typst, the two text nodes are combined into a single sequence node. -#[capable] -#[derive(Clone, Hash)] -pub struct SequenceNode(pub Vec<Content>); - #[node] -impl SequenceNode {} - -impl Debug for SequenceNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_list().entries(self.0.iter()).finish() - } +pub struct SequenceNode { + #[variadic] + #[required] + pub children: Vec<Content>, } /// A label for a node. @@ -396,80 +365,83 @@ impl Debug for Label { } /// A constructable, stylable content node. -pub trait Node: 'static + Capable { +pub trait Node: Construct + Set + Sized + 'static { + /// The node's ID. + fn id() -> NodeId; + /// Pack a node into type-erased content. - fn pack(self) -> Content - where - Self: Node + Debug + Hash + Sync + Send + Sized + 'static, - { - Content { - obj: Arc::new(self), - span: None, - modifiers: EcoVec::new(), - } - } + fn pack(self) -> Content; +} - /// A unique identifier of the node type. - fn id(&self) -> NodeId; +/// A unique identifier for a node. +#[derive(Copy, Clone)] +pub struct NodeId(&'static NodeMeta); - /// The node's name. - fn name(&self) -> &'static str; +impl NodeId { + pub fn of<T: Node>() -> Self { + T::id() + } - /// Construct a node from the arguments. - /// - /// This is passed only the arguments that remain after execution of the - /// node's set rule. - fn construct(vm: &Vm, args: &mut Args) -> SourceResult<Content> - where - Self: Sized; + pub fn from_meta(meta: &'static NodeMeta) -> Self { + Self(meta) + } - /// Parse relevant arguments into style properties for this node. - /// - /// When `constructor` is true, [`construct`](Self::construct) will run - /// after this invocation of `set` with the remaining arguments. - fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap> - where - Self: Sized; + /// The name of the identified node. + pub fn name(self) -> &'static str { + self.0.name + } +} - /// List the settable properties. - fn properties() -> Vec<ParamInfo> - where - Self: Sized; +impl Debug for NodeId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad(self.name()) + } +} - /// Access a field on this node. - fn field(&self, name: &str) -> Option<Value>; +impl Hash for NodeId { + fn hash<H: Hasher>(&self, state: &mut H) { + state.write_usize(self.0 as *const _ as usize); + } } -/// A unique identifier for a node type. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct NodeId(ReadableTypeId); +impl Eq for NodeId {} -impl NodeId { - /// The id of the given node type. - pub fn of<T: 'static>() -> Self { - Self(ReadableTypeId::of::<T>()) +impl PartialEq for NodeId { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self.0, other.0) } } -impl Debug for NodeId { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) - } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct NodeMeta { + pub name: &'static str, + pub vtable: fn(of: TypeId) -> Option<*const ()>, } -/// A capability a node can have. -/// -/// Should be implemented by trait objects that are accessible through -/// [`Capable`]. -pub trait Capability: 'static {} - -/// Dynamically access a trait implementation at runtime. -pub unsafe trait Capable { - /// Return the vtable pointer of the trait object with given type `id` - /// if `self` implements the trait. - fn vtable(&self, of: TypeId) -> Option<*const ()>; +pub trait Construct { + /// Construct a node from the arguments. + /// + /// This is passed only the arguments that remain after execution of the + /// node's set rule. + fn construct(vm: &Vm, args: &mut Args) -> SourceResult<Content>; +} + +pub trait Set { + /// Parse relevant arguments into style properties for this node. + /// + /// When `constructor` is true, [`construct`](Construct::construct) will run + /// after this invocation of `set` with the remaining arguments. + fn set(args: &mut Args, constructor: bool) -> SourceResult<StyleMap>; + + /// List the settable properties. + fn properties() -> Vec<ParamInfo>; } /// Indicates that a node cannot be labelled. -#[capability] pub trait Unlabellable {} + +#[cold] +#[track_caller] +fn field_is_missing(name: &str) -> ! { + panic!("required field `{name}` is missing") +} diff --git a/src/model/mod.rs b/src/model/mod.rs index 692d18d5..07329e3f 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -13,4 +13,4 @@ pub use self::typeset::*; #[doc(hidden)] pub use once_cell; -pub use typst_macros::{capability, capable, node}; +pub use typst_macros::node; diff --git a/src/model/realize.rs b/src/model/realize.rs index b33cc0bb..2f38df51 100644 --- a/src/model/realize.rs +++ b/src/model/realize.rs @@ -1,4 +1,4 @@ -use super::{capability, Content, NodeId, Recipe, Selector, StyleChain, Vt}; +use super::{Content, NodeId, Recipe, Selector, StyleChain, Vt}; use crate::diag::SourceResult; /// Whether the target is affected by show rules in the given style chain. @@ -105,7 +105,7 @@ fn try_apply( let mut result = vec![]; let mut cursor = 0; - for m in regex.find_iter(text) { + for m in regex.find_iter(&text) { let start = m.start(); if cursor < start { result.push(make(text[cursor..start].into())); @@ -133,7 +133,6 @@ fn try_apply( } /// Preparations before execution of any show rule. -#[capability] pub trait Prepare { /// Prepare the node for show rule application. fn prepare( @@ -145,7 +144,6 @@ pub trait Prepare { } /// The base recipe for a node. -#[capability] pub trait Show { /// Execute the base recipe for this node. fn show( @@ -157,7 +155,6 @@ pub trait Show { } /// Post-process a node after it was realized. -#[capability] pub trait Finalize { /// Finalize the fully realized form of the node. Use this for effects that /// should work even in the face of a user-defined show rule, for example diff --git a/src/model/styles.rs b/src/model/styles.rs index 18507491..cbf4cfb2 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -1,21 +1,16 @@ use std::any::Any; use std::fmt::{self, Debug, Formatter}; -use std::hash::Hash; +use std::hash::{Hash, Hasher}; use std::iter; use std::marker::PhantomData; -use std::sync::Arc; -use comemo::{Prehashed, Tracked}; +use comemo::Tracked; +use ecow::EcoString; -use super::{Content, Label, NodeId}; +use super::{Content, Label, Node, NodeId}; use crate::diag::{SourceResult, Trace, Tracepoint}; -use crate::eval::{Args, Dict, Func, Regex, Value}; -use crate::geom::{ - Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides, - Smart, -}; +use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value}; use crate::syntax::Span; -use crate::util::ReadableTypeId; use crate::World; /// A map of style properties. @@ -76,7 +71,7 @@ impl StyleMap { /// Mark all contained properties as _scoped_. This means that they only /// apply to the first descendant node (of their type) in the hierarchy and /// not its children, too. This is used by - /// [constructors](super::Node::construct). + /// [constructors](super::Construct::construct). pub fn scoped(mut self) -> Self { for entry in &mut self.0 { if let Style::Property(property) = entry { @@ -98,7 +93,7 @@ impl StyleMap { /// Returns `Some(_)` with an optional span if this map contains styles for /// the given `node`. - pub fn interruption<T: 'static>(&self) -> Option<Option<Span>> { + pub fn interruption<T: Node>(&self) -> Option<Option<Span>> { let node = NodeId::of::<T>(); self.0.iter().find_map(|entry| match entry { Style::Property(property) => property.is_of(node).then(|| property.origin), @@ -114,6 +109,12 @@ impl From<Style> for StyleMap { } } +impl PartialEq for StyleMap { + fn eq(&self, other: &Self) -> bool { + crate::util::hash128(self) == crate::util::hash128(other) + } +} + impl Debug for StyleMap { fn fmt(&self, f: &mut Formatter) -> fmt::Result { for entry in self.0.iter() { @@ -154,13 +155,11 @@ impl Style { impl Debug for Style { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str("#[")?; match self { - Self::Property(property) => property.fmt(f)?, - Self::Recipe(recipe) => recipe.fmt(f)?, - Self::Barrier(id) => write!(f, "Barrier for {id:?}")?, + Self::Property(property) => property.fmt(f), + Self::Recipe(recipe) => recipe.fmt(f), + Self::Barrier(id) => write!(f, "#[Barrier for {id:?}]"), } - f.write_str("]") } } @@ -175,12 +174,9 @@ pub struct Property { /// hierarchy. Used by constructors. scoped: bool, /// The property's value. - value: Arc<Prehashed<dyn Bounds>>, + value: Value, /// The span of the set rule the property stems from. origin: Option<Span>, - /// The name of the property. - #[cfg(debug_assertions)] - name: &'static str, } impl Property { @@ -189,11 +185,9 @@ impl Property { Self { key: KeyId::of::<K>(), node: K::node(), - value: Arc::new(Prehashed::new(value)), + value: value.into(), scoped: false, origin: None, - #[cfg(debug_assertions)] - name: K::NAME, } } @@ -208,9 +202,12 @@ impl Property { } /// Access the property's value if it is of the given key. - pub fn downcast<K: Key>(&self) -> Option<&K::Value> { + #[track_caller] + pub fn cast<K: Key>(&self) -> Option<K::Value> { if self.key == KeyId::of::<K>() { - (**self.value).as_any().downcast_ref() + Some(self.value.clone().cast().unwrap_or_else(|err| { + panic!("{} (for {} with value {:?})", err, self.key.name(), self.value) + })) } else { None } @@ -234,9 +231,7 @@ impl Property { impl Debug for Property { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - #[cfg(debug_assertions)] - write!(f, "{} = ", self.name)?; - write!(f, "{:?}", self.value)?; + write!(f, "#set {}({}: {:?})", self.node.name(), self.key.name(), self.value)?; if self.scoped { write!(f, " [scoped]")?; } @@ -267,47 +262,69 @@ where /// A style property key. /// -/// This trait is not intended to be implemented manually, but rather through -/// the `#[node]` proc-macro. +/// This trait is not intended to be implemented manually. pub trait Key: Copy + 'static { /// The unfolded type which this property is stored as in a style map. - type Value: Debug + Clone + Hash + Sync + Send + 'static; + type Value: Cast + Into<Value>; /// The folded type of value that is returned when reading this property /// from a style chain. - type Output<'a>; + type Output; - /// The name of the property, used for debug printing. - const NAME: &'static str; + /// The id of the property. + fn id() -> KeyId; /// The id of the node the key belongs to. fn node() -> NodeId; /// Compute an output value from a sequence of values belonging to this key, /// folding if necessary. - fn get<'a>( - chain: StyleChain<'a>, - values: impl Iterator<Item = &'a Self::Value>, - ) -> Self::Output<'a>; + fn get(chain: StyleChain, values: impl Iterator<Item = Self::Value>) -> Self::Output; } -/// A unique identifier for a property key. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -struct KeyId(ReadableTypeId); +/// A unique identifier for a style key. +#[derive(Copy, Clone)] +pub struct KeyId(&'static KeyMeta); impl KeyId { - /// The id of the given key. pub fn of<T: Key>() -> Self { - Self(ReadableTypeId::of::<T>()) + T::id() + } + + pub fn from_meta(meta: &'static KeyMeta) -> Self { + Self(meta) + } + + pub fn name(self) -> &'static str { + self.0.name } } impl Debug for KeyId { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) + f.pad(self.name()) + } +} + +impl Hash for KeyId { + fn hash<H: Hasher>(&self, state: &mut H) { + state.write_usize(self.0 as *const _ as usize); + } +} + +impl Eq for KeyId {} + +impl PartialEq for KeyId { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self.0, other.0) } } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct KeyMeta { + pub name: &'static str, +} + /// A show rule recipe. #[derive(Clone, Hash)] pub struct Recipe { @@ -362,7 +379,7 @@ impl Recipe { impl Debug for Recipe { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Recipe matching {:?}", self.selector) + write!(f, "#show {:?}: {:?}", self.selector, self.transform) } } @@ -382,7 +399,7 @@ pub enum Selector { impl Selector { /// Define a simple node selector. - pub fn node<T: 'static>() -> Self { + pub fn node<T: Node>() -> Self { Self::Node(NodeId::of::<T>(), None) } @@ -399,17 +416,25 @@ impl Selector { && dict .iter() .flat_map(|dict| dict.iter()) - .all(|(name, value)| target.field(name).as_ref() == Some(value)) + .all(|(name, value)| target.field(name) == Some(value)) } Self::Label(label) => target.label() == Some(label), Self::Regex(regex) => { target.id() == item!(text_id) - && item!(text_str)(target).map_or(false, |text| regex.is_match(text)) + && item!(text_str)(target).map_or(false, |text| regex.is_match(&text)) } } } } +cast_from_value! { + Selector: "selector", + text: EcoString => Self::text(&text), + label: Label => Self::Label(label), + func: Func => func.select(None)?, + regex: Regex => Self::Regex(regex), +} + /// A show rule transformation that can be applied to a match. #[derive(Debug, Clone, Hash)] pub enum Transform { @@ -421,6 +446,17 @@ pub enum Transform { Style(StyleMap), } +cast_from_value! { + Transform, + content: Content => Self::Content(content), + func: Func => { + if func.argc().map_or(false, |count| count != 1) { + Err("function must have exactly one parameter")? + } + Self::Func(func) + }, +} + /// A chain of style maps, similar to a linked list. /// /// A style chain allows to combine properties from multiple style maps in a @@ -478,7 +514,7 @@ impl<'a> StyleChain<'a> { /// Returns the property's default value if no map in the chain contains an /// entry for it. Also takes care of resolving and folding and returns /// references where applicable. - pub fn get<K: Key>(self, key: K) -> K::Output<'a> { + pub fn get<K: Key>(self, key: K) -> K::Output { K::get(self, self.values(key)) } @@ -534,10 +570,7 @@ impl Debug for StyleChain<'_> { impl PartialEq for StyleChain<'_> { fn eq(&self, other: &Self) -> bool { - let as_ptr = |s| s as *const _; - self.head.as_ptr() == other.head.as_ptr() - && self.head.len() == other.head.len() - && self.tail.map(as_ptr) == other.tail.map(as_ptr) + crate::util::hash128(self) == crate::util::hash128(other) } } @@ -585,13 +618,14 @@ struct Values<'a, K> { } impl<'a, K: Key> Iterator for Values<'a, K> { - type Item = &'a K::Value; + type Item = K::Value; + #[track_caller] fn next(&mut self) -> Option<Self::Item> { for entry in &mut self.entries { match entry { Style::Property(property) => { - if let Some(value) = property.downcast::<K>() { + if let Some(value) = property.cast::<K>() { if !property.scoped() || self.barriers <= 1 { return Some(value); } @@ -672,6 +706,20 @@ impl<T> StyleVec<T> { } } +impl StyleVec<Content> { + pub fn to_vec(self) -> Vec<Content> { + self.items + .into_iter() + .zip( + self.maps + .iter() + .flat_map(|(map, count)| iter::repeat(map).take(*count)), + ) + .map(|(content, map)| content.styled_with_map(map.clone())) + .collect() + } +} + impl<T> Default for StyleVec<T> { fn default() -> Self { Self { items: vec![], maps: vec![] } @@ -791,26 +839,6 @@ pub trait Resolve { fn resolve(self, styles: StyleChain) -> Self::Output; } -impl Resolve for Em { - type Output = Abs; - - fn resolve(self, styles: StyleChain) -> Self::Output { - if self.is_zero() { - Abs::zero() - } else { - self.at(item!(em)(styles)) - } - } -} - -impl Resolve for Length { - type Output = Abs; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.abs + self.em.resolve(styles) - } -} - impl<T: Resolve> Resolve for Option<T> { type Output = Option<T::Output>; @@ -819,74 +847,6 @@ impl<T: Resolve> Resolve for Option<T> { } } -impl<T: Resolve> Resolve for Smart<T> { - type Output = Smart<T::Output>; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.map(|v| v.resolve(styles)) - } -} - -impl<T: Resolve> Resolve for Axes<T> { - type Output = Axes<T::Output>; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.map(|v| v.resolve(styles)) - } -} - -impl<T: Resolve> Resolve for Sides<T> { - type Output = Sides<T::Output>; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.map(|v| v.resolve(styles)) - } -} - -impl<T: Resolve> Resolve for Corners<T> { - type Output = Corners<T::Output>; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.map(|v| v.resolve(styles)) - } -} - -impl<T> Resolve for Rel<T> -where - T: Resolve + Numeric, - <T as Resolve>::Output: Numeric, -{ - type Output = Rel<<T as Resolve>::Output>; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.map(|abs| abs.resolve(styles)) - } -} - -impl Resolve for GenAlign { - type Output = Align; - - fn resolve(self, styles: StyleChain) -> Self::Output { - let dir = item!(dir)(styles); - match self { - Self::Start => dir.start().into(), - Self::End => dir.end().into(), - Self::Specific(align) => align, - } - } -} - -impl Resolve for PartialStroke { - type Output = PartialStroke<Abs>; - - fn resolve(self, styles: StyleChain) -> Self::Output { - PartialStroke { - paint: self.paint, - thickness: self.thickness.resolve(styles), - } - } -} - /// A property that is folded to determine its final value. pub trait Fold { /// The type of the folded output. @@ -907,92 +867,3 @@ where self.map(|inner| inner.fold(outer.unwrap_or_default())) } } - -impl<T> Fold for Smart<T> -where - T: Fold, - T::Output: Default, -{ - type Output = Smart<T::Output>; - - fn fold(self, outer: Self::Output) -> Self::Output { - self.map(|inner| inner.fold(outer.unwrap_or_default())) - } -} - -impl<T> Fold for Axes<Option<T>> -where - T: Fold, -{ - type Output = Axes<T::Output>; - - fn fold(self, outer: Self::Output) -> Self::Output { - self.zip(outer).map(|(inner, outer)| match inner { - Some(value) => value.fold(outer), - None => outer, - }) - } -} - -impl<T> Fold for Sides<Option<T>> -where - T: Fold, -{ - type Output = Sides<T::Output>; - - fn fold(self, outer: Self::Output) -> Self::Output { - self.zip(outer).map(|(inner, outer)| match inner { - Some(value) => value.fold(outer), - None => outer, - }) - } -} - -impl<T> Fold for Corners<Option<T>> -where - T: Fold, -{ - type Output = Corners<T::Output>; - - fn fold(self, outer: Self::Output) -> Self::Output { - self.zip(outer).map(|(inner, outer)| match inner { - Some(value) => value.fold(outer), - None => outer, - }) - } -} - -impl Fold for PartialStroke<Abs> { - type Output = Self; - - fn fold(self, outer: Self::Output) -> Self::Output { - Self { - paint: self.paint.or(outer.paint), - thickness: self.thickness.or(outer.thickness), - } - } -} - -impl Fold for Rel<Length> { - type Output = Self; - - fn fold(self, _: Self::Output) -> Self::Output { - self - } -} - -impl Fold for Rel<Abs> { - type Output = Self; - - fn fold(self, _: Self::Output) -> Self::Output { - self - } -} - -impl Fold for GenAlign { - type Output = Self; - - fn fold(self, _: Self::Output) -> Self::Output { - self - } -} diff --git a/src/model/typeset.rs b/src/model/typeset.rs index f8b5e012..6361e6ce 100644 --- a/src/model/typeset.rs +++ b/src/model/typeset.rs @@ -8,7 +8,6 @@ use comemo::{Track, Tracked, TrackedMut}; use super::{Content, Selector, StyleChain}; use crate::diag::SourceResult; use crate::doc::{Document, Element, Frame, Location, Meta}; -use crate::eval::Value; use crate::geom::Transform; use crate::util::hash128; use crate::World; @@ -162,7 +161,7 @@ impl Introspector { let pos = pos.transform(ts); let mut node = content.clone(); let loc = Location { page, pos }; - node.push_field("loc", Value::Dict(loc.encode())); + node.push_field("loc", loc); self.nodes.push((id, node)); } } |
