summaryrefslogtreecommitdiff
path: root/src/model/styles.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-07 15:17:13 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-07 15:17:13 +0100
commit25b5bd117529cd04bb789e1988eb3a3db8025a0e (patch)
tree2fbb4650903123da047a1f1f11a0abda95286e12 /src/model/styles.rs
parent6ab7760822ccd24b4ef126d4737d41f1be15fe19 (diff)
Fully untyped model
Diffstat (limited to 'src/model/styles.rs')
-rw-r--r--src/model/styles.rs337
1 files changed, 104 insertions, 233 deletions
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
- }
-}