diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-25 10:36:31 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-25 12:16:13 +0100 |
| commit | bf5edbbbbb75120d065d1c9587ccfa4eed4fdca1 (patch) | |
| tree | 956af910ab27a8cec0db83171cd3f0b6d0570a60 /src/model/styles.rs | |
| parent | 96f72eee6c6b595164c7a0576c407d7a590661db (diff) | |
Tidy up
Diffstat (limited to 'src/model/styles.rs')
| -rw-r--r-- | src/model/styles.rs | 435 |
1 files changed, 135 insertions, 300 deletions
diff --git a/src/model/styles.rs b/src/model/styles.rs index 96eb0ab4..966a57ec 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use comemo::{Prehashed, Tracked}; -use super::{capability, Args, Content, Dict, Func, NodeId, Regex, Smart, Value}; +use super::{Args, Content, Dict, Func, NodeId, Regex, Smart, Value}; use crate::diag::{SourceResult, Trace, Tracepoint}; use crate::geom::{ Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides, @@ -55,34 +55,15 @@ impl StyleMap { .any(|property| property.is::<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) } - } + /// Apply outer styles. Like [`chain`](StyleChain::chain), but in-place. + pub fn apply(&mut self, outer: Self) { + self.0.splice(0..0, outer.0.iter().cloned()); } - /// Set an outer style. - /// - /// Like [`chain`](Self::chain) or [`apply_map`](Self::apply_map), but with - /// only a entry. - pub fn apply(&mut self, style: Style) { - self.0.insert(0, style); - } - - /// 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()); + /// Set an outer style. Like [`chain_one`](StyleChain::chain_one), but + /// in-place. + pub fn apply_one(&mut self, outer: Style) { + self.0.insert(0, outer); } /// Mark all contained properties as _scoped_. This means that they only @@ -131,21 +112,6 @@ pub enum Style { } impl Style { - /// Make this style the first link of the `tail` chain. - pub fn chain<'a>(&'a self, tail: &'a StyleChain) -> StyleChain<'a> { - if let Style::Barrier(id) = self { - if !tail - .entries() - .filter_map(Style::property) - .any(|p| p.scoped() && *id == p.node()) - { - return *tail; - } - } - - StyleChain { head: std::slice::from_ref(self), tail: Some(tail) } - } - /// If this is a property, return it. pub fn property(&self) -> Option<&Property> { match self { @@ -184,32 +150,6 @@ impl Debug for Style { } } -/// A style property key. -/// -/// This trait is not intended to be implemented manually, but rather through -/// the `#[node]` proc-macro. -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; - - /// The folded type of value that is returned when reading this property - /// from a style chain. - type Output<'a>; - - /// The name of the property, used for debug printing. - const NAME: &'static str; - - /// 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>; -} - /// A style property originating from a set rule or constructor. #[derive(Clone, Hash)] pub struct Property { @@ -308,6 +248,32 @@ where } } +/// A style property key. +/// +/// This trait is not intended to be implemented manually, but rather through +/// the `#[node]` proc-macro. +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; + + /// The folded type of value that is returned when reading this property + /// from a style chain. + type Output<'a>; + + /// The name of the property, used for debug printing. + const NAME: &'static str; + + /// 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>; +} + /// A unique identifier for a property key. #[derive(Copy, Clone, Eq, PartialEq, Hash)] struct KeyId(ReadableTypeId); @@ -325,35 +291,6 @@ impl Debug for KeyId { } } -/// A built-in show rule for a node. -#[capability] -pub trait Show { - /// Execute the base recipe for this node. - fn show( - &self, - world: Tracked<dyn World>, - styles: StyleChain, - ) -> SourceResult<Content>; -} - -/// 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 - /// the linking behaviour of a link node. - fn finalize( - &self, - world: Tracked<dyn World>, - styles: StyleChain, - realized: Content, - ) -> SourceResult<Content>; -} - -/// Indicates that a node cannot be labelled. -#[capability] -pub trait Unlabellable {} - /// A show rule recipe. #[derive(Clone, PartialEq, Hash)] pub struct Recipe { @@ -366,6 +303,14 @@ pub struct Recipe { } impl Recipe { + /// Whether this recipe is for the given node. + pub fn is_of(&self, node: NodeId) -> bool { + match self.selector { + Some(Selector::Node(id, _)) => id == node, + _ => false, + } + } + /// Whether the recipe is applicable to the target. pub fn applicable(&self, target: &Content) -> bool { self.selector @@ -373,74 +318,24 @@ impl Recipe { .map_or(false, |selector| selector.matches(target)) } - /// Try to apply the recipe to the target. + /// Apply the recipe to the given content. pub fn apply( &self, world: Tracked<dyn World>, - sel: RecipeId, - target: &Content, - ) -> SourceResult<Option<Content>> { - let content = match &self.selector { - Some(Selector::Node(id, _)) => { - if target.id() != *id { - return Ok(None); - } - - self.transform.apply(world, self.span, target.clone().guarded(sel))? - } - - Some(Selector::Regex(regex)) => { - let Some(text) = item!(text_str)(target) else { - return Ok(None); - }; - - let make = |s| { - let mut content = item!(text)(s); - content.copy_meta(target); - content - }; - - let mut result = vec![]; - let mut cursor = 0; - - for m in regex.find_iter(text) { - let start = m.start(); - if cursor < start { - result.push(make(text[cursor..start].into())); - } - - let transformed = self.transform.apply( - world, - self.span, - make(m.as_str().into()).guarded(sel), - )?; - - result.push(transformed); - cursor = m.end(); - } - - if result.is_empty() { - return Ok(None); - } - - if cursor < text.len() { - result.push(make(text[cursor..].into())); + content: Content, + ) -> SourceResult<Content> { + match &self.transform { + Transform::Content(content) => Ok(content.clone()), + Transform::Func(func) => { + let args = Args::new(self.span, [Value::Content(content.clone())]); + let mut result = func.call_detached(world, args); + if let Some(span) = content.span() { + let point = || Tracepoint::Show(content.name().into()); + result = result.trace(world, point, span); } - - Content::sequence(result) + Ok(result?.display()) } - - None => return Ok(None), - }; - - Ok(Some(content)) - } - - /// Whether this recipe is for the given node. - pub fn is_of(&self, node: NodeId) -> bool { - match self.selector { - Some(Selector::Node(id, _)) => id == node, - _ => false, + Transform::Style(styles) => Ok(content.styled_with_map(styles.clone())), } } } @@ -495,39 +390,6 @@ pub enum Transform { Style(StyleMap), } -impl Transform { - /// Apply the transform. - pub fn apply( - &self, - world: Tracked<dyn World>, - rule_span: Span, - content: Content, - ) -> SourceResult<Content> { - match self { - Transform::Content(content) => Ok(content.clone()), - Transform::Func(func) => { - let args = Args::new(rule_span, [Value::Content(content.clone())]); - let mut result = func.call_detached(world, args); - if let Some(span) = content.span() { - let point = || Tracepoint::Apply(content.name().into()); - result = result.trace(world, point, span); - } - Ok(result?.display()) - } - Transform::Style(styles) => Ok(content.styled_with_map(styles.clone())), - } - } -} - -/// Identifies a show rule recipe. -#[derive(Debug, Copy, Clone, PartialEq, Hash)] -pub enum RecipeId { - /// The nth recipe from the top of the chain. - Nth(usize), - /// The base recipe for a kind of node. - Base(NodeId), -} - /// A chain of style maps, similar to a linked list. /// /// A style chain allows to combine properties from multiple style maps in a @@ -544,97 +406,54 @@ pub struct StyleChain<'a> { } 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 { + pub fn new(root: &'a StyleMap) -> Self { Self { head: &root.0, tail: None } } - /// Get the output value of a style property. + /// Make the given map the first link of this chain. /// - /// 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> { - K::get(self, self.values(key)) - } - - /// Apply show recipes in this style chain to a target. - pub fn show( - self, - world: Tracked<dyn World>, - target: &Content, - ) -> SourceResult<Option<Content>> { - // Find out how many recipes there are. - let mut n = self.entries().filter_map(Style::recipe).count(); - - // Find an applicable recipe. - let mut realized = None; - for recipe in self.entries().filter_map(Style::recipe) { - let sel = RecipeId::Nth(n); - if recipe.applicable(target) && !target.is_guarded(sel) { - if let Some(content) = recipe.apply(world, sel, target)? { - realized = Some(content); - break; - } - } - n -= 1; - } - - // Realize if there was no matching recipe. - let base = RecipeId::Base(target.id()); - if realized.is_none() && !target.is_guarded(base) { - if let Some(showable) = target.with::<dyn Show>() { - realized = Some(showable.show(world, self)?); - } - } - - // Finalize only if this is the first application for this node. - if let Some(node) = target.with::<dyn Finalize>() { - if target.is_pristine() { - if let Some(content) = realized { - realized = Some(node.finalize(world, self, content)?); - } - } + /// The resulting style chain contains styles from `map` as well as + /// `self`. The ones from `map` take precedence over the ones from + /// `self`. For folded properties `map` contributes the inner value. + pub fn chain<'b>(&'b self, map: &'b StyleMap) -> StyleChain<'b> { + if map.is_empty() { + *self + } else { + StyleChain { head: &map.0, tail: Some(self) } } - - Ok(realized) } - /// Whether the style chain has a matching recipe for the content. - pub fn applicable(self, target: &Content) -> bool { - // Find out how many recipes there are. - let mut n = self.entries().filter_map(Style::recipe).count(); - - // Find out whether any recipe matches and is unguarded. - for recipe in self.entries().filter_map(Style::recipe) { - if recipe.applicable(target) && !target.is_guarded(RecipeId::Nth(n)) { - return true; + /// Make the given style the first link of the this chain. + pub fn chain_one<'b>(&'b self, style: &'b Style) -> StyleChain<'b> { + if let Style::Barrier(id) = style { + if !self + .entries() + .filter_map(Style::property) + .any(|p| p.scoped() && *id == p.node()) + { + return *self; } - n -= 1; } - false + StyleChain { + head: std::slice::from_ref(style), + tail: Some(self), + } } - /// Remove the last link from the chain. - fn pop(&mut self) { - *self = self.tail.copied().unwrap_or_default(); + /// 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>(self, key: K) -> K::Output<'a> { + K::get(self, self.values(key)) } - /// 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 style recipes in the chain. + pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> { + self.entries().filter_map(Style::recipe) } /// Iterate over all values for the given property in the chain. @@ -655,6 +474,22 @@ impl<'a> StyleChain<'a> { fn links(self) -> Links<'a> { Links(Some(self)) } + + /// 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 + } + + /// Remove the last link from the chain. + fn pop(&mut self) { + *self = self.tail.copied().unwrap_or_default(); + } } impl Debug for StyleChain<'_> { @@ -675,37 +510,6 @@ impl PartialEq for StyleChain<'_> { } } -/// An iterator over the values in a style chain. -struct Values<'a, K> { - entries: Entries<'a>, - key: PhantomData<K>, - barriers: usize, -} - -impl<'a, K: Key> Iterator for Values<'a, K> { - type Item = &'a K::Value; - - 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 !property.scoped() || self.barriers <= 1 { - return Some(value); - } - } - } - Style::Barrier(id) => { - self.barriers += (*id == K::node()) as usize; - } - _ => {} - } - } - - None - } -} - /// An iterator over the entries in a style chain. struct Entries<'a> { inner: std::slice::Iter<'a, Style>, @@ -742,6 +546,37 @@ impl<'a> Iterator for Links<'a> { } } +/// An iterator over the values in a style chain. +struct Values<'a, K> { + entries: Entries<'a>, + key: PhantomData<K>, + barriers: usize, +} + +impl<'a, K: Key> Iterator for Values<'a, K> { + type Item = &'a K::Value; + + 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 !property.scoped() || self.barriers <= 1 { + return Some(value); + } + } + } + Style::Barrier(id) => { + self.barriers += (*id == K::node()) as usize; + } + _ => {} + } + } + + None + } +} + /// A sequence of items with associated styles. #[derive(Clone, Hash)] pub struct StyleVec<T> { |
