summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-30 16:47:43 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-30 16:47:43 +0200
commitf77f1f61bf05ae506689be3c40252c5807276280 (patch)
tree1d6d6ff64f9747ed422dbabb2de58b4267b0e6f2 /src
parentaa10ea8470763afe98d5ff558381f0a0beb0c017 (diff)
Split up style module
Diffstat (limited to 'src')
-rw-r--r--src/model/mod.rs4
-rw-r--r--src/model/property.rs299
-rw-r--r--src/model/recipe.rs29
-rw-r--r--src/model/styles.rs338
4 files changed, 348 insertions, 322 deletions
diff --git a/src/model/mod.rs b/src/model/mod.rs
index df39207f..a9d1344a 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -5,10 +5,14 @@ mod styles;
mod collapse;
mod content;
mod layout;
+mod property;
+mod recipe;
mod show;
pub use collapse::*;
pub use content::*;
pub use layout::*;
+pub use property::*;
+pub use recipe::*;
pub use show::*;
pub use styles::*;
diff --git a/src/model/property.rs b/src/model/property.rs
new file mode 100644
index 00000000..b35ffba9
--- /dev/null
+++ b/src/model/property.rs
@@ -0,0 +1,299 @@
+use std::any::Any;
+use std::fmt::{self, Debug, Formatter};
+use std::hash::Hash;
+use std::sync::Arc;
+
+use super::{Interruption, NodeId, StyleChain};
+use crate::eval::{RawLength, Smart};
+use crate::geom::{Length, Numeric, Relative, Sides, Spec};
+use crate::library::layout::PageNode;
+use crate::library::structure::{EnumNode, ListNode};
+use crate::library::text::ParNode;
+use crate::util::{Prehashed, ReadableTypeId};
+
+/// A style property originating from a set rule or constructor.
+#[derive(Clone, Hash)]
+pub struct Property {
+ /// The id of the property's [key](Key).
+ pub key: KeyId,
+ /// The id of the node the property belongs to.
+ pub node: NodeId,
+ /// Whether the property should only affects the first node down the
+ /// hierarchy. Used by constructors.
+ pub scoped: bool,
+ /// The property's value.
+ value: Arc<Prehashed<dyn Bounds>>,
+ /// The name of the property.
+ #[cfg(debug_assertions)]
+ name: &'static str,
+}
+
+impl Property {
+ /// Create a new property from a key-value pair.
+ pub fn new<'a, K: Key<'a>>(_: K, value: K::Value) -> Self {
+ Self {
+ key: KeyId::of::<K>(),
+ node: K::node(),
+ value: Arc::new(Prehashed::new(value)),
+ scoped: false,
+ #[cfg(debug_assertions)]
+ name: K::NAME,
+ }
+ }
+
+ /// Whether this property has the given key.
+ pub fn is<'a, K: Key<'a>>(&self) -> bool {
+ self.key == KeyId::of::<K>()
+ }
+
+ /// Whether this property belongs to the node `T`.
+ pub fn is_of<T: 'static>(&self) -> bool {
+ self.node == NodeId::of::<T>()
+ }
+
+ /// Access the property's value if it is of the given key.
+ pub fn downcast<'a, K: Key<'a>>(&'a self) -> Option<&'a K::Value> {
+ if self.key == KeyId::of::<K>() {
+ (**self.value).as_any().downcast_ref()
+ } else {
+ None
+ }
+ }
+
+ /// What kind of structure the property interrupts.
+ pub fn interruption(&self) -> Option<Interruption> {
+ if self.is_of::<PageNode>() {
+ Some(Interruption::Page)
+ } else if self.is_of::<ParNode>() {
+ Some(Interruption::Par)
+ } else if self.is_of::<ListNode>() || self.is_of::<EnumNode>() {
+ Some(Interruption::List)
+ } else {
+ None
+ }
+ }
+}
+
+impl Debug for Property {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ #[cfg(debug_assertions)]
+ write!(f, "{} = ", self.name)?;
+ write!(f, "{:?}", self.value)?;
+ if self.scoped {
+ write!(f, " [scoped]")?;
+ }
+ Ok(())
+ }
+}
+
+impl PartialEq for Property {
+ fn eq(&self, other: &Self) -> bool {
+ self.key == other.key
+ && self.value.eq(&other.value)
+ && self.scoped == other.scoped
+ }
+}
+
+trait Bounds: Debug + Sync + Send + 'static {
+ fn as_any(&self) -> &dyn Any;
+}
+
+impl<T> Bounds for T
+where
+ T: Debug + Sync + Send + 'static,
+{
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+}
+
+/// 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::<T>())
+ }
+}
+
+impl Debug for KeyId {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+/// Style property keys.
+///
+/// This trait is not intended to be implemented manually, but rather through
+/// the `#[node]` proc-macro.
+pub trait Key<'a>: Copy + 'static {
+ /// The unfolded type which this property is stored as in a style map. For
+ /// example, this is [`Toggle`](crate::geom::Length) for the
+ /// [`STRONG`](TextNode::STRONG) property.
+ type Value: Debug + Clone + Hash + Sync + Send + 'static;
+
+ /// The folded type of value that is returned when reading this property
+ /// from a style chain. For example, this is [`bool`] for the
+ /// [`STRONG`](TextNode::STRONG) property. For non-copy, non-folding
+ /// properties this is a reference type.
+ type Output;
+
+ /// The name of the property, used for debug printing.
+ const NAME: &'static str;
+
+ /// 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.
+ fn get(
+ chain: StyleChain<'a>,
+ values: impl Iterator<Item = &'a Self::Value>,
+ ) -> Self::Output;
+}
+
+/// A property that is resolved with other properties from the style chain.
+pub trait Resolve {
+ /// The type of the resolved output.
+ type Output;
+
+ /// Resolve the value using the style chain.
+ fn resolve(self, styles: StyleChain) -> Self::Output;
+}
+
+impl<T: Resolve> Resolve for Option<T> {
+ type Output = Option<T::Output>;
+
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ self.map(|v| v.resolve(styles))
+ }
+}
+
+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 Spec<T> {
+ type Output = Spec<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 {
+ Sides {
+ left: self.left.resolve(styles),
+ right: self.right.resolve(styles),
+ top: self.top.resolve(styles),
+ bottom: self.bottom.resolve(styles),
+ }
+ }
+}
+
+impl<T> Resolve for Relative<T>
+where
+ T: Resolve + Numeric,
+ <T as Resolve>::Output: Numeric,
+{
+ type Output = Relative<<T as Resolve>::Output>;
+
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ self.map(|abs| abs.resolve(styles))
+ }
+}
+
+/// A property that is folded to determine its final value.
+pub trait Fold {
+ /// The type of the folded output.
+ type Output;
+
+ /// Fold this inner value with an outer folded value.
+ fn fold(self, outer: Self::Output) -> Self::Output;
+}
+
+impl<T> Fold for Option<T>
+where
+ T: Fold,
+ T::Output: Default,
+{
+ type Output = Option<T::Output>;
+
+ fn fold(self, outer: Self::Output) -> Self::Output {
+ 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 Sides<T>
+where
+ T: Fold,
+{
+ type Output = Sides<T::Output>;
+
+ fn fold(self, outer: Self::Output) -> Self::Output {
+ self.zip(outer, |inner, outer, _| inner.fold(outer))
+ }
+}
+
+impl Fold for Sides<Option<Relative<Length>>> {
+ type Output = Sides<Relative<Length>>;
+
+ fn fold(self, outer: Self::Output) -> Self::Output {
+ self.zip(outer, |inner, outer, _| inner.unwrap_or(outer))
+ }
+}
+
+impl Fold for Sides<Option<Smart<Relative<RawLength>>>> {
+ type Output = Sides<Smart<Relative<RawLength>>>;
+
+ fn fold(self, outer: Self::Output) -> Self::Output {
+ self.zip(outer, |inner, outer, _| inner.unwrap_or(outer))
+ }
+}
+
+/// 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)
+ }
+
+ /// Whether this barrier is for the node `T`.
+ pub fn is_for(&self, node: NodeId) -> bool {
+ self.0 == node
+ }
+}
+
+impl Debug for Barrier {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "Barrier for {:?}", self.0)
+ }
+}
diff --git a/src/model/recipe.rs b/src/model/recipe.rs
new file mode 100644
index 00000000..2f5f7c09
--- /dev/null
+++ b/src/model/recipe.rs
@@ -0,0 +1,29 @@
+use std::fmt::{self, Debug, Formatter};
+
+use super::NodeId;
+use crate::eval::{Func, Node};
+use crate::syntax::Span;
+
+/// A show rule recipe.
+#[derive(Clone, PartialEq, Hash)]
+pub struct Recipe {
+ /// The affected node.
+ pub node: NodeId,
+ /// The function that defines the recipe.
+ pub func: Func,
+ /// The span to report all erros with.
+ pub span: Span,
+}
+
+impl Recipe {
+ /// Create a new recipe for the node `T`.
+ pub fn new<T: Node>(func: Func, span: Span) -> Self {
+ Self { node: NodeId::of::<T>(), func, span }
+ }
+}
+
+impl Debug for Recipe {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "Recipe for {:?} from {:?}", self.node, self.span)
+ }
+}
diff --git a/src/model/styles.rs b/src/model/styles.rs
index ae4c1586..b3020c88 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -1,19 +1,14 @@
-use std::any::Any;
use std::fmt::{self, Debug, Formatter};
use std::hash::Hash;
use std::iter;
use std::marker::PhantomData;
-use std::sync::Arc;
-use super::{Content, Show, ShowNode};
+use super::{Barrier, Content, Key, Property, Recipe, Show, ShowNode};
use crate::diag::{At, TypResult};
-use crate::eval::{Args, Func, Node, RawLength, Smart, Value};
-use crate::geom::{Length, Numeric, Relative, Sides, Spec};
-use crate::library::layout::PageNode;
-use crate::library::structure::{EnumNode, ListNode};
-use crate::library::text::{FontFamily, ParNode, TextNode};
+use crate::eval::{Args, Func, Node, Value};
+use crate::library::text::{FontFamily, TextNode};
use crate::syntax::Span;
-use crate::util::{Prehashed, ReadableTypeId};
+use crate::util::ReadableTypeId;
use crate::Context;
/// A map of style properties.
@@ -80,7 +75,7 @@ impl StyleMap {
self.0
.iter()
.filter_map(|entry| entry.property())
- .any(|property| property.key == KeyId::of::<K>())
+ .any(|property| property.is::<K>())
}
/// Make `self` the first link of the `tail` chain.
@@ -171,21 +166,15 @@ impl Debug for NodeId {
}
}
-/// 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::<T>())
- }
-}
-
-impl Debug for KeyId {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
+/// Determines whether a style could interrupt some composable structure.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum Interruption {
+ /// The style forces a list break.
+ List,
+ /// The style forces a paragraph break.
+ Par,
+ /// The style forces a page break.
+ Page,
}
/// An entry for a single style property, recipe or barrier.
@@ -222,7 +211,7 @@ impl StyleEntry {
if !tail
.entries()
.filter_map(StyleEntry::property)
- .any(|p| p.scoped && p.node == barrier.0)
+ .any(|p| p.scoped && barrier.is_for(p.node))
{
return *tail;
}
@@ -247,301 +236,6 @@ impl Debug for StyleEntry {
}
}
-/// A style property originating from a set rule or constructor.
-#[derive(Clone, Hash)]
-pub struct Property {
- /// 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<Prehashed<dyn Bounds>>,
- /// Whether the property should only affects the first node down the
- /// hierarchy. Used by constructors.
- scoped: bool,
-}
-
-impl Property {
- /// Create a new property from a key-value pair.
- pub fn new<'a, K: Key<'a>>(_: K, value: K::Value) -> Self {
- Self {
- key: KeyId::of::<K>(),
- node: K::node(),
- #[cfg(debug_assertions)]
- name: K::NAME,
- value: Arc::new(Prehashed::new(value)),
- scoped: false,
- }
- }
-
- /// What kind of structure the property interrupts.
- pub fn interruption(&self) -> Option<Interruption> {
- if self.is_of::<PageNode>() {
- Some(Interruption::Page)
- } else if self.is_of::<ParNode>() {
- Some(Interruption::Par)
- } else if self.is_of::<ListNode>() || self.is_of::<EnumNode>() {
- Some(Interruption::List)
- } else {
- None
- }
- }
-
- /// Access the property's value if it is of the given key.
- pub fn downcast<'a, K: Key<'a>>(&'a self) -> Option<&'a K::Value> {
- if self.key == KeyId::of::<K>() {
- (**self.value).as_any().downcast_ref()
- } else {
- None
- }
- }
-
- /// Whether this property belongs to the node `T`.
- pub fn is_of<T: 'static>(&self) -> bool {
- self.node == NodeId::of::<T>()
- }
-}
-
-impl Debug for Property {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- #[cfg(debug_assertions)]
- write!(f, "{} = ", self.name)?;
- write!(f, "{:?}", self.value)?;
- if self.scoped {
- write!(f, " [scoped]")?;
- }
- Ok(())
- }
-}
-
-impl PartialEq for Property {
- fn eq(&self, other: &Self) -> bool {
- self.key == other.key
- && self.value.eq(&other.value)
- && self.scoped == other.scoped
- }
-}
-
-trait Bounds: Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
-}
-
-impl<T> Bounds for T
-where
- T: Debug + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
-
-/// Style property keys.
-///
-/// This trait is not intended to be implemented manually, but rather through
-/// the `#[node]` proc-macro.
-pub trait Key<'a>: Copy + 'static {
- /// The unfolded type which this property is stored as in a style map. For
- /// example, this is [`Toggle`](crate::geom::Length) for the
- /// [`STRONG`](TextNode::STRONG) property.
- type Value: Debug + Clone + Hash + Sync + Send + 'static;
-
- /// The folded type of value that is returned when reading this property
- /// from a style chain. For example, this is [`bool`] for the
- /// [`STRONG`](TextNode::STRONG) property. For non-copy, non-folding
- /// properties this is a reference type.
- type Output;
-
- /// The name of the property, used for debug printing.
- const NAME: &'static str;
-
- /// 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.
- fn get(
- chain: StyleChain<'a>,
- values: impl Iterator<Item = &'a Self::Value>,
- ) -> Self::Output;
-}
-
-/// A property that is resolved with other properties from the style chain.
-pub trait Resolve {
- /// The type of the resolved output.
- type Output;
-
- /// Resolve the value using the style chain.
- fn resolve(self, styles: StyleChain) -> Self::Output;
-}
-
-impl<T: Resolve> Resolve for Option<T> {
- type Output = Option<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-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 Spec<T> {
- type Output = Spec<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 {
- Sides {
- left: self.left.resolve(styles),
- right: self.right.resolve(styles),
- top: self.top.resolve(styles),
- bottom: self.bottom.resolve(styles),
- }
- }
-}
-
-impl<T> Resolve for Relative<T>
-where
- T: Resolve + Numeric,
- <T as Resolve>::Output: Numeric,
-{
- type Output = Relative<<T as Resolve>::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|abs| abs.resolve(styles))
- }
-}
-
-/// A property that is folded to determine its final value.
-pub trait Fold {
- /// The type of the folded output.
- type Output;
-
- /// Fold this inner value with an outer folded value.
- fn fold(self, outer: Self::Output) -> Self::Output;
-}
-
-impl<T> Fold for Option<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Option<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- 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 Sides<T>
-where
- T: Fold,
-{
- type Output = Sides<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer, |inner, outer, _| inner.fold(outer))
- }
-}
-
-impl Fold for Sides<Option<Relative<Length>>> {
- type Output = Sides<Relative<Length>>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer, |inner, outer, _| inner.unwrap_or(outer))
- }
-}
-
-impl Fold for Sides<Option<Smart<Relative<RawLength>>>> {
- type Output = Sides<Smart<Relative<RawLength>>>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer, |inner, outer, _| inner.unwrap_or(outer))
- }
-}
-
-/// 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)]
-pub struct Recipe {
- /// The affected node.
- node: NodeId,
- /// The function that defines the recipe.
- func: Func,
- /// The span to report all erros with.
- span: Span,
-}
-
-impl Recipe {
- /// Create a new recipe for the node `T`.
- pub fn new<T: Node>(func: Func, span: Span) -> Self {
- Self { node: NodeId::of::<T>(), func, span }
- }
-}
-
-impl Debug for Recipe {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Recipe for {:?} from {:?}", self.node, self.span)
- }
-}
-
-/// Determines whether a style could interrupt some composable structure.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum Interruption {
- /// The style forces a list break.
- List,
- /// The style forces a paragraph break.
- Par,
- /// The style forces a page break.
- Page,
-}
-
/// A chain of style maps, similar to a linked list.
///
/// A style chain allows to combine properties from multiple style maps in a
@@ -690,7 +384,7 @@ impl<'a, K: Key<'a>> Iterator for Values<'a, K> {
}
}
StyleEntry::Barrier(barrier) => {
- self.depth += (barrier.0 == K::node()) as usize;
+ self.depth += barrier.is_for(K::node()) as usize;
}
StyleEntry::Recipe(_) => {}
}