diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-24 15:42:56 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-24 15:47:42 +0200 |
| commit | 8fbb11fc05b3313bf102c1f23693290661d00863 (patch) | |
| tree | a198db77338f1cc1304fe50f55020b08e22bca60 /src/eval/styles.rs | |
| parent | e4ee14e54fb87961096856c7ea105435b7cc3c45 (diff) | |
Extract `model` module
Diffstat (limited to 'src/eval/styles.rs')
| -rw-r--r-- | src/eval/styles.rs | 829 |
1 files changed, 0 insertions, 829 deletions
diff --git a/src/eval/styles.rs b/src/eval/styles.rs deleted file mode 100644 index b666c85d..00000000 --- a/src/eval/styles.rs +++ /dev/null @@ -1,829 +0,0 @@ -use std::any::{Any, TypeId}; -use std::fmt::{self, Debug, Formatter}; -use std::hash::Hash; -use std::marker::PhantomData; -use std::sync::Arc; - -use super::{Args, Content, Func, Layout, Node, Show, ShowNode, Smart, Span, Value}; -use crate::diag::{At, TypResult}; -use crate::geom::{Numeric, Relative, Sides, Spec}; -use crate::library::layout::PageNode; -use crate::library::text::{FontFamily, ParNode, TextNode}; -use crate::util::Prehashed; -use crate::Context; - -/// A map of style properties. -#[derive(Default, Clone, PartialEq, Hash)] -pub struct StyleMap(Vec<Entry>); - -impl StyleMap { - /// Create a new, empty style map. - pub fn new() -> Self { - Self::default() - } - - /// Whether this map contains no styles. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Create a style map from a single property-value pair. - pub fn with<'a, K: Key<'a>>(key: K, value: K::Value) -> Self { - let mut styles = Self::new(); - styles.set(key, value); - styles - } - - /// Set an inner value for a style property. - /// - /// If the property needs folding and the value is already contained in the - /// style map, `self` contributes the outer values and `value` is the inner - /// one. - pub fn set<'a, K: Key<'a>>(&mut self, key: K, value: K::Value) { - self.0.push(Entry::Property(Property::new(key, value))); - } - - /// Set an inner value for a style property if it is `Some(_)`. - pub fn set_opt<'a, K: Key<'a>>(&mut self, key: K, value: Option<K::Value>) { - if let Some(value) = value { - self.set(key, value); - } - } - - /// Set a font family composed of a preferred family and existing families - /// from a style chain. - pub fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) { - self.set( - TextNode::FAMILY, - std::iter::once(preferred) - .chain(existing.get(TextNode::FAMILY).iter().cloned()) - .collect(), - ); - } - - /// Set a show rule recipe for a node. - pub fn set_recipe<T: Node>(&mut self, func: Func, span: Span) { - self.0.push(Entry::Recipe(Recipe::new::<T>(func, span))); - } - - /// Whether the map contains a style property for the given key. - pub fn contains<'a, K: Key<'a>>(&self, _: K) -> bool { - self.0 - .iter() - .filter_map(|entry| entry.property()) - .any(|property| property.key == TypeId::of::<K>()) - } - - /// Make `self` the first link of the `tail` chain. - /// - /// The resulting style chain contains styles from `self` as well as - /// `tail`. The ones from `self` take precedence over the ones from - /// `tail`. For folded properties `self` contributes the inner value. - pub fn chain<'a>(&'a self, tail: &'a StyleChain<'a>) -> StyleChain<'a> { - if self.is_empty() { - *tail - } else { - StyleChain { head: &self.0, tail: Some(tail) } - } - } - - /// Set an outer value for a style property. - /// - /// If the property needs folding and the value is already contained in the - /// style map, `self` contributes the inner values and `value` is the outer - /// one. - /// - /// Like [`chain`](Self::chain) or [`apply_map`](Self::apply_map), but with - /// only a single property. - pub fn apply<'a, K: Key<'a>>(&mut self, key: K, value: K::Value) { - self.0.insert(0, Entry::Property(Property::new(key, value))); - } - - /// Apply styles from `tail` in-place. The resulting style map is equivalent - /// to the style chain created by `self.chain(StyleChain::new(tail))`. - /// - /// This is useful over `chain` when you want to combine two maps, but you - /// still need an owned map without a lifetime. - pub fn apply_map(&mut self, tail: &Self) { - self.0.splice(0 .. 0, tail.0.iter().cloned()); - } - - /// 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](Node::construct). - pub fn scoped(mut self) -> Self { - for entry in &mut self.0 { - if let Entry::Property(property) = entry { - property.scoped = true; - } - } - self - } - - /// The highest-level kind of of structure the map interrupts. - pub fn interruption(&self) -> Option<Interruption> { - self.0 - .iter() - .filter_map(|entry| entry.property()) - .filter_map(|property| property.interruption()) - .max() - } -} - -impl Debug for StyleMap { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - for entry in self.0.iter().rev() { - writeln!(f, "{:?}", entry)?; - } - Ok(()) - } -} - -/// 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), - /// A show rule recipe. - Recipe(Recipe), -} - -impl Entry { - /// If this is a property, return it. - fn property(&self) -> Option<&Property> { - match self { - Self::Property(property) => Some(property), - _ => None, - } - } - - /// If this is a recipe, return it. - fn recipe(&self) -> Option<&Recipe> { - match self { - Self::Recipe(recipe) => Some(recipe), - _ => None, - } - } -} - -impl Debug for Entry { - 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(_, name) => write!(f, "Barrier for {name}")?, - } - f.write_str("]") - } -} - -/// 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 name of the property. - 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. - fn new<'a, K: Key<'a>>(_: K, value: K::Value) -> Self { - Self { - key: TypeId::of::<K>(), - node: K::node(), - name: K::NAME, - value: Arc::new(Prehashed::new(value)), - scoped: false, - } - } - - /// What kind of structure the property interrupts. - fn interruption(&self) -> Option<Interruption> { - if self.is_of::<PageNode>() { - Some(Interruption::Page) - } else if self.is_of::<ParNode>() { - Some(Interruption::Par) - } else { - None - } - } - - /// 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::<K>() { - (**self.value).as_any().downcast_ref() - } else { - None - } - } - - /// Whether this property belongs to the node `T`. - fn is_of<T: Node>(&self) -> bool { - self.node == TypeId::of::<T>() - } -} - -impl Debug for Property { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} = {:?}", self.name, 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 type id of the node this property belongs to. - fn node() -> TypeId; - - /// 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())) - } -} - -/// A show rule recipe. -#[derive(Clone, PartialEq, Hash)] -struct Recipe { - /// The affected node. - node: TypeId, - /// The name of the affected node. - name: &'static str, - /// 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`. - fn new<T: Node>(func: Func, span: Span) -> Self { - Self { - node: TypeId::of::<T>(), - name: std::any::type_name::<T>(), - 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<T: Layout>() -> Self { - Self(Entry::Barrier( - TypeId::of::<T>(), - std::any::type_name::<T>(), - )) - } - - /// 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) - } -} - -/// Determines whether a style could interrupt some composable structure. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub enum Interruption { - /// 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 -/// node hierarchy in a non-allocating way. Rather than eagerly merging the -/// maps, each access walks the hierarchy from the innermost to the outermost -/// map, trying to find a match and then folding it with matches further up the -/// chain. -#[derive(Default, Clone, Copy, Hash)] -pub struct StyleChain<'a> { - /// The first link of this chain. - head: &'a [Entry], - /// The remaining links in the chain. - tail: Option<&'a Self>, -} - -impl<'a> StyleChain<'a> { - /// Create a new, empty style chain. - pub fn new() -> Self { - Self::default() - } - - /// Start a new style chain with a root map. - pub fn with_root(root: &'a StyleMap) -> Self { - Self { head: &root.0, tail: None } - } - - /// Get the output value of a style property. - /// - /// 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<'a>>(self, key: K) -> K::Output { - K::get(self, self.values(key)) - } - - /// Realize a node with a user recipe. - pub fn realize( - self, - ctx: &mut Context, - node: &ShowNode, - ) -> TypResult<Option<Content>> { - let id = node.id(); - if let Some(recipe) = self - .entries() - .filter_map(Entry::recipe) - .find(|recipe| recipe.node == id) - { - let dict = node.encode(); - let args = Args::from_values(recipe.span, [Value::Dict(dict)]); - Ok(Some(recipe.func.call(ctx, args)?.cast().at(recipe.span)?)) - } else { - Ok(None) - } - } -} - -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 { - while self - .head - .last() - .and_then(Entry::property) - .map_or(false, |p| p.scoped && p.node == node) - { - let len = self.head.len(); - self.head = &self.head[.. len - 1] - } - self - } - - /// Remove the last link from the chain. - fn pop(&mut self) { - *self = self.tail.copied().unwrap_or_default(); - } - - /// Build a style map from the suffix (all links beyond the `len`) of the - /// chain. - fn suffix(self, len: usize) -> StyleMap { - let mut suffix = StyleMap::new(); - let take = self.links().count().saturating_sub(len); - for link in self.links().take(take) { - suffix.0.splice(0 .. 0, link.iter().cloned()); - } - suffix - } - - /// Iterate over all values for the given property in the chain. - fn values<K: Key<'a>>(self, _: K) -> Values<'a, K> { - Values { - entries: self.entries(), - depth: 0, - key: PhantomData, - } - } - - /// Iterate over the entries of the chain. - fn entries(self) -> Entries<'a> { - Entries { - inner: [].as_slice().iter(), - links: self.links(), - } - } - - /// Iterate over the links of the chain. - fn links(self) -> Links<'a> { - Links(Some(self)) - } -} - -impl Debug for StyleChain<'_> { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - for entry in self.entries() { - writeln!(f, "{:?}", entry)?; - } - Ok(()) - } -} - -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) - } -} - -/// An iterator over the values in a style chain. -struct Values<'a, K> { - entries: Entries<'a>, - depth: usize, - key: PhantomData<K>, -} - -impl<'a, K: Key<'a>> Iterator for Values<'a, K> { - type Item = &'a K::Value; - - fn next(&mut self) -> Option<Self::Item> { - while let Some(entry) = self.entries.next() { - match entry { - Entry::Property(property) => { - if let Some(value) = property.downcast::<K>() { - if !property.scoped || self.depth <= 1 { - return Some(value); - } - } - } - Entry::Barrier(id, _) => { - self.depth += (*id == K::node()) as usize; - } - Entry::Recipe(_) => {} - } - } - - None - } -} - -/// An iterator over the entries in a style chain. -struct Entries<'a> { - inner: std::slice::Iter<'a, Entry>, - links: Links<'a>, -} - -impl<'a> Iterator for Entries<'a> { - type Item = &'a Entry; - - fn next(&mut self) -> Option<Self::Item> { - loop { - if let Some(entry) = self.inner.next_back() { - return Some(entry); - } - - match self.links.next() { - Some(next) => self.inner = next.iter(), - None => return None, - } - } - } -} - -/// An iterator over the links of a style chain. -struct Links<'a>(Option<StyleChain<'a>>); - -impl<'a> Iterator for Links<'a> { - type Item = &'a [Entry]; - - fn next(&mut self) -> Option<Self::Item> { - let StyleChain { head, tail } = self.0?; - self.0 = tail.copied(); - Some(head) - } -} - -/// A sequence of items with associated styles. -#[derive(Hash)] -pub struct StyleVec<T> { - items: Vec<T>, - maps: Vec<(StyleMap, usize)>, -} - -impl<T> StyleVec<T> { - /// Whether there are any items in the sequence. - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - - /// Number of items in the sequence. - pub fn len(&self) -> usize { - self.items.len() - } - - /// Iterate over the contained maps. Note that zipping this with `items()` - /// does not yield the same result as calling `iter()` because this method - /// only returns maps once that are shared by consecutive items. This method - /// is designed for use cases where you want to check, for example, whether - /// any of the maps fulfills a specific property. - pub fn maps(&self) -> impl Iterator<Item = &StyleMap> { - self.maps.iter().map(|(map, _)| map) - } - - /// Iterate over the contained items. - pub fn items(&self) -> std::slice::Iter<'_, T> { - self.items.iter() - } - - /// Iterate over the contained items and associated style maps. - pub fn iter(&self) -> impl Iterator<Item = (&T, &StyleMap)> + '_ { - let styles = self - .maps - .iter() - .flat_map(|(map, count)| std::iter::repeat(map).take(*count)); - self.items().zip(styles) - } - - /// Insert an element in the front. The element will share the style of the - /// current first element. - /// - /// This method has no effect if the vector is empty. - pub fn push_front(&mut self, item: T) { - if !self.maps.is_empty() { - self.items.insert(0, item); - self.maps[0].1 += 1; - } - } -} - -impl<T> Default for StyleVec<T> { - fn default() -> Self { - Self { items: vec![], maps: vec![] } - } -} - -impl<T: Debug> Debug for StyleVec<T> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_list() - .entries(self.iter().map(|(item, map)| { - crate::util::debug(|f| { - map.fmt(f)?; - item.fmt(f) - }) - })) - .finish() - } -} - -/// Assists in the construction of a [`StyleVec`]. -pub struct StyleVecBuilder<'a, T> { - items: Vec<T>, - chains: Vec<(StyleChain<'a>, usize)>, -} - -impl<'a, T> StyleVecBuilder<'a, T> { - /// Create a new style-vec builder. - pub fn new() -> Self { - Self { items: vec![], chains: vec![] } - } - - /// Push a new item into the style vector. - pub fn push(&mut self, item: T, styles: StyleChain<'a>) { - self.items.push(item); - - if let Some((prev, count)) = self.chains.last_mut() { - if *prev == styles { - *count += 1; - return; - } - } - - self.chains.push((styles, 1)); - } - - /// Access the last item mutably and its chain by value. - pub fn last_mut(&mut self) -> Option<(&mut T, StyleChain<'a>)> { - let item = self.items.last_mut()?; - let chain = self.chains.last()?.0; - Some((item, chain)) - } - - /// Iterate over the contained items. - pub fn items(&self) -> std::slice::Iter<'_, T> { - self.items.iter() - } - - /// Finish building, returning a pair of two things: - /// - a style vector of items with the non-shared styles - /// - a shared prefix chain of styles that apply to all items - pub fn finish(self) -> (StyleVec<T>, StyleChain<'a>) { - let mut iter = self.chains.iter(); - let mut trunk = match iter.next() { - Some(&(chain, _)) => chain, - None => return Default::default(), - }; - - let mut shared = trunk.links().count(); - for &(mut chain, _) in iter { - let len = chain.links().count(); - if len < shared { - for _ in 0 .. shared - len { - trunk.pop(); - } - shared = len; - } else if len > shared { - for _ in 0 .. len - shared { - chain.pop(); - } - } - - while shared > 0 && chain != trunk { - trunk.pop(); - chain.pop(); - shared -= 1; - } - } - - let maps = self - .chains - .into_iter() - .map(|(chain, count)| (chain.suffix(shared), count)) - .collect(); - - (StyleVec { items: self.items, maps }, trunk) - } -} - -impl<'a, T> Default for StyleVecBuilder<'a, T> { - fn default() -> Self { - Self::new() - } -} |
