diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 8614a26ff..15925df73 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -249,12 +249,12 @@ fn process_const( const NAME: &'static str = #name; - fn node() -> TypeId { - TypeId::of::<#self_ty>() + fn node() -> model::NodeId { + model::NodeId::of::<#self_ty>() } fn get( - chain: StyleChain<'a>, + chain: model::StyleChain<'a>, mut values: impl Iterator, ) -> Self::Output { #get diff --git a/src/model/layout.rs b/src/model/layout.rs index da6290ae5..9a4d5df35 100644 --- a/src/model/layout.rs +++ b/src/model/layout.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::Hash; use std::sync::Arc; -use super::{Barrier, Resolve, StyleChain}; +use super::{Barrier, NodeId, Resolve, StyleChain, StyleSlot}; use crate::diag::TypResult; use crate::eval::{RawAlign, RawLength}; use crate::frame::{Element, Frame, Geometry}; @@ -148,9 +148,9 @@ impl LayoutNode { (**self.0).as_any().is::() } - /// A barrier for the node. - pub fn barrier(&self) -> Barrier { - (**self.0).barrier() + /// The id of this node. + pub fn id(&self) -> NodeId { + (**self.0).node_id() } /// Try to downcast to a specific layout node. @@ -220,7 +220,8 @@ impl Layout for LayoutNode { styles: StyleChain, ) -> TypResult>> { ctx.query((self, regions, styles), |ctx, (node, regions, styles)| { - node.0.layout(ctx, regions, node.barrier().chain(&styles)) + let slot = StyleSlot::from(Barrier::new(node.id())); + node.0.layout(ctx, regions, slot.chain(&styles)) }) .clone() } @@ -250,7 +251,7 @@ impl PartialEq for LayoutNode { trait Bounds: Layout + Debug + Sync + Send + 'static { fn as_any(&self) -> &dyn Any; - fn barrier(&self) -> Barrier; + fn node_id(&self) -> NodeId; } impl Bounds for T @@ -261,8 +262,8 @@ where self } - fn barrier(&self) -> Barrier { - Barrier::new::() + fn node_id(&self) -> NodeId { + NodeId::of::() } } diff --git a/src/model/show.rs b/src/model/show.rs index 5f76ba191..d1365eb20 100644 --- a/src/model/show.rs +++ b/src/model/show.rs @@ -1,9 +1,8 @@ -use std::any::{Any, TypeId}; use std::fmt::{self, Debug, Formatter}; use std::hash::Hash; use std::sync::Arc; -use super::{Content, StyleChain}; +use super::{Content, NodeId, StyleChain}; use crate::diag::TypResult; use crate::eval::Dict; use crate::util::Prehashed; @@ -57,9 +56,9 @@ impl ShowNode { Self(Arc::new(Prehashed::new(node))) } - /// The type id of this node. - pub fn id(&self) -> TypeId { - self.0.as_any().type_id() + /// The id of this node. + pub fn id(&self) -> NodeId { + (**self.0).node_id() } } @@ -99,14 +98,14 @@ impl PartialEq for ShowNode { } trait Bounds: Show + Debug + Sync + Send + 'static { - fn as_any(&self) -> &dyn Any; + fn node_id(&self) -> NodeId; } impl Bounds for T where T: Show + Debug + Hash + Sync + Send + 'static, { - fn as_any(&self) -> &dyn Any { - self + fn node_id(&self) -> NodeId { + NodeId::of::() } } diff --git a/src/model/styles.rs b/src/model/styles.rs index 2195568d6..8c9092a4d 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -1,17 +1,17 @@ -use std::any::{Any, TypeId}; +use std::any::Any; use std::fmt::{self, Debug, Formatter}; use std::hash::Hash; use std::marker::PhantomData; use std::sync::Arc; -use super::{Content, Layout, Show, ShowNode}; +use super::{Content, Show, ShowNode}; use crate::diag::{At, TypResult}; use crate::eval::{Args, Func, Node, Smart, Value}; use crate::geom::{Numeric, Relative, Sides, Spec}; use crate::library::layout::PageNode; use crate::library::text::{FontFamily, ParNode, TextNode}; use crate::syntax::Span; -use crate::util::Prehashed; +use crate::util::{Prehashed, ReadableTypeId}; use crate::Context; /// A map of style properties. @@ -73,7 +73,7 @@ impl StyleMap { self.0 .iter() .filter_map(|entry| entry.property()) - .any(|property| property.key == TypeId::of::()) + .any(|property| property.key == KeyId::of::()) } /// Make `self` the first link of the `tail` chain. @@ -141,13 +141,42 @@ impl Debug for StyleMap { } } +/// A stack-allocated slot for a single style property or barrier. +pub struct StyleSlot(Entry); + +impl StyleSlot { + /// Make this slot the first link of the `tail` chain. + pub fn chain<'a>(&'a self, tail: &'a StyleChain) -> StyleChain<'a> { + if let Entry::Barrier(barrier) = &self.0 { + if !tail + .entries() + .filter_map(Entry::property) + .any(|p| p.scoped && p.node == barrier.0) + { + return *tail; + } + } + + StyleChain { + head: std::slice::from_ref(&self.0), + tail: Some(tail), + } + } +} + +impl From for StyleSlot { + fn from(barrier: Barrier) -> Self { + Self(Entry::Barrier(barrier)) + } +} + /// An entry for a single style property, recipe or barrier. #[derive(Clone, PartialEq, Hash)] enum Entry { /// A style property originating from a set rule or constructor. Property(Property), /// A barrier for scoped styles. - Barrier(TypeId, &'static str), + Barrier(Barrier), /// A show rule recipe. Recipe(Recipe), } @@ -176,20 +205,55 @@ impl Debug for Entry { match self { Self::Property(property) => property.fmt(f)?, Self::Recipe(recipe) => recipe.fmt(f)?, - Self::Barrier(_, name) => write!(f, "Barrier for {name}")?, + Self::Barrier(barrier) => barrier.fmt(f)?, } f.write_str("]") } } +/// A unique identifier for a node. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct NodeId(ReadableTypeId); + +impl NodeId { + /// The id of the given node. + pub fn of() -> Self { + Self(ReadableTypeId::of::()) + } +} + +impl Debug for NodeId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +/// A unique identifier for a property key. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct KeyId(ReadableTypeId); + +impl KeyId { + /// The id of the given key. + pub fn of<'a, T: Key<'a>>() -> Self { + Self(ReadableTypeId::of::()) + } +} + +impl Debug for KeyId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + /// A style property originating from a set rule or constructor. #[derive(Clone, Hash)] struct Property { - /// The type id of the property's [key](Key). - key: TypeId, - /// The type id of the node the property belongs to. - node: TypeId, + /// The id of the property's [key](Key). + key: KeyId, + /// The id of the node the property belongs to. + node: NodeId, /// The name of the property. + #[cfg(debug_assertions)] name: &'static str, /// The property's value. value: Arc>, @@ -202,8 +266,9 @@ impl Property { /// Create a new property from a key-value pair. fn new<'a, K: Key<'a>>(_: K, value: K::Value) -> Self { Self { - key: TypeId::of::(), + key: KeyId::of::(), node: K::node(), + #[cfg(debug_assertions)] name: K::NAME, value: Arc::new(Prehashed::new(value)), scoped: false, @@ -223,7 +288,7 @@ impl Property { /// Access the property's value if it is of the given key. fn downcast<'a, K: Key<'a>>(&'a self) -> Option<&'a K::Value> { - if self.key == TypeId::of::() { + if self.key == KeyId::of::() { (**self.value).as_any().downcast_ref() } else { None @@ -232,13 +297,15 @@ impl Property { /// Whether this property belongs to the node `T`. fn is_of(&self) -> bool { - self.node == TypeId::of::() + self.node == NodeId::of::() } } impl Debug for Property { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} = {:?}", self.name, self.value)?; + #[cfg(debug_assertions)] + write!(f, "{} = ", self.name)?; + write!(f, "{:?}", self.value)?; if self.scoped { write!(f, " [scoped]")?; } @@ -286,8 +353,8 @@ pub trait Key<'a>: Copy + 'static { /// The name of the property, used for debug printing. const NAME: &'static str; - /// The type id of the node this property belongs to. - fn node() -> TypeId; + /// The ids of the key and of the node the key belongs to. + fn node() -> NodeId; /// Compute an output value from a sequence of values belong to this key, /// folding if necessary. @@ -388,13 +455,32 @@ where } } +/// A scoped property barrier. +/// +/// Barriers interact with [scoped](StyleMap::scoped) styles: A scoped style +/// can still be read through a single barrier (the one of the node it +/// _should_ apply to), but a second barrier will make it invisible. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct Barrier(NodeId); + +impl Barrier { + /// Create a new barrier for the given node. + pub fn new(node: NodeId) -> Self { + Self(node) + } +} + +impl Debug for Barrier { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "Barrier for {:?}", self.0) + } +} + /// A show rule recipe. #[derive(Clone, PartialEq, Hash)] struct Recipe { /// The affected node. - node: TypeId, - /// The name of the affected node. - name: &'static str, + node: NodeId, /// The function that defines the recipe. func: Func, /// The span to report all erros with. @@ -404,67 +490,13 @@ struct Recipe { impl Recipe { /// Create a new recipe for the node `T`. fn new(func: Func, span: Span) -> Self { - Self { - node: TypeId::of::(), - name: std::any::type_name::(), - func, - span, - } + Self { node: NodeId::of::(), func, span } } } impl Debug for Recipe { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Recipe for {} from {:?}", self.name, self.span) - } -} - -/// A style chain barrier. -/// -/// Barriers interact with [scoped](StyleMap::scoped) styles: A scoped style -/// can still be read through a single barrier (the one of the node it -/// _should_ apply to), but a second barrier will make it invisible. -#[derive(Clone, PartialEq, Hash)] -pub struct Barrier(Entry); - -impl Barrier { - /// Create a new barrier for the layout node `T`. - pub fn new() -> Self { - Self(Entry::Barrier( - TypeId::of::(), - std::any::type_name::(), - )) - } - - /// Make this barrier the first link of the `tail` chain. - pub fn chain<'a>(&'a self, tail: &'a StyleChain) -> StyleChain<'a> { - // We have to store a full `Entry` enum inside the barrier because - // otherwise the `slice::from_ref` trick below won't work. - // Unfortunately, that also means we have to somehow extract the id - // here. - let id = match self.0 { - Entry::Barrier(id, _) => id, - _ => unreachable!(), - }; - - if tail - .entries() - .filter_map(Entry::property) - .any(|p| p.scoped && p.node == id) - { - StyleChain { - head: std::slice::from_ref(&self.0), - tail: Some(tail), - } - } else { - *tail - } - } -} - -impl Debug for Barrier { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) + write!(f, "Recipe for {:?} from {:?}", self.node, self.span) } } @@ -536,7 +568,7 @@ impl<'a> StyleChain<'a> { impl<'a> StyleChain<'a> { /// Return the chain, but without the trailing scoped property for the given /// `node`. This is a 90% hack fix for show node constructor scoping. - pub(super) fn unscoped(mut self, node: TypeId) -> Self { + pub(super) fn unscoped(mut self, node: NodeId) -> Self { while self .head .last() @@ -626,8 +658,8 @@ impl<'a, K: Key<'a>> Iterator for Values<'a, K> { } } } - Entry::Barrier(id, _) => { - self.depth += (*id == K::node()) as usize; + Entry::Barrier(barrier) => { + self.depth += (barrier.0 == K::node()) as usize; } Entry::Recipe(_) => {} } diff --git a/src/util/mod.rs b/src/util/mod.rs index 3bc13bac2..f762d88d5 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -9,6 +9,7 @@ pub use eco_string::EcoString; pub use mac_roman::decode_mac_roman; pub use prehashed::Prehashed; +use std::any::TypeId; use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; use std::ops::{Deref, Range}; @@ -34,7 +35,61 @@ where Wrapper(f) } -/// Additional methods for strings. +/// An alternative type id that prints as something readable in debug mode. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct ReadableTypeId { + id: TypeId, + #[cfg(debug_assertions)] + name: &'static str, +} + +impl ReadableTypeId { + /// The type id of the given type. + pub fn of() -> Self { + Self { + id: TypeId::of::(), + #[cfg(debug_assertions)] + name: std::any::type_name::(), + } + } +} + +impl Debug for ReadableTypeId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + #[cfg(debug_assertions)] + f.pad(self.name)?; + #[cfg(not(debug_assertions))] + f.pad("ReadableTypeId")?; + Ok(()) + } +} + +/// Either owned or shared. +pub enum MaybeShared { + /// Owned data. + Owned(T), + /// Shared data. + Shared(Arc), +} + +impl AsRef for MaybeShared { + fn as_ref(&self) -> &T { + self + } +} + +impl Deref for MaybeShared { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + Self::Owned(owned) => owned, + Self::Shared(shared) => shared, + } + } +} + +/// Extra methods for [`str`]. pub trait StrExt { /// The number of code units this string would use if it was encoded in /// UTF16. This runs in linear time. @@ -47,7 +102,7 @@ impl StrExt for str { } } -/// Additional methods for options. +/// Extra methods for [`Option`]. pub trait OptionExt { /// Sets `other` as the value if `self` is `None` or if it contains a value /// larger than `other`. @@ -82,7 +137,7 @@ impl OptionExt for Option { } } -/// Additional methods for reference-counted pointers. +/// Extra methods for [`Arc`]. pub trait ArcExt { /// Takes the inner value if there is exactly one strong reference and /// clones it otherwise. @@ -101,32 +156,7 @@ where } } -/// Either owned or shared. -pub enum MaybeShared { - /// Owned data. - Owned(T), - /// Shared data. - Shared(Arc), -} - -impl AsRef for MaybeShared { - fn as_ref(&self) -> &T { - self - } -} - -impl Deref for MaybeShared { - type Target = T; - - fn deref(&self) -> &Self::Target { - match self { - Self::Owned(owned) => owned, - Self::Shared(shared) => shared, - } - } -} - -/// Additional methods for slices. +/// Extra methods for `[T]`. pub trait SliceExt { /// Split a slice into consecutive runs with the same key and yield for /// each such run the key and the slice of elements with that key. @@ -165,7 +195,7 @@ where } } -/// Additional methods for [`Range`]. +/// Extra methods for [`Range`]. pub trait RangeExt { /// Locate a position relative to a range. /// @@ -193,7 +223,7 @@ impl RangeExt for Range { } } -/// Additional methods for [`Path`]. +/// Extra methods for [`Path`]. pub trait PathExt { /// Lexically normalize a path. fn normalize(&self) -> PathBuf;