diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-07 14:30:50 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-07 14:43:13 +0100 |
| commit | 0a41844cc4e645e87fe48aa31ed3a4fd40a6ab11 (patch) | |
| tree | c7cac97079491c8a11afae1211d7a80415fe64ef /src/model/styles.rs | |
| parent | efd1853d069fbd1476e82d015da4d0d04cfaccc0 (diff) | |
Selectors
Diffstat (limited to 'src/model/styles.rs')
| -rw-r--r-- | src/model/styles.rs | 79 |
1 files changed, 47 insertions, 32 deletions
diff --git a/src/model/styles.rs b/src/model/styles.rs index 3800490b..8e731942 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, Func, NodeId, Regex, Smart, Value}; +use super::{capability, Args, Content, Dict, Func, NodeId, Regex, Smart, Value}; use crate::diag::SourceResult; use crate::geom::{ Abs, Align, Axes, Corners, Em, GenAlign, Length, Numeric, PartialStroke, Rel, Sides, @@ -141,9 +141,9 @@ pub enum StyleEntry { /// A barrier for scoped styles. Barrier(Barrier), /// Guards against recursive show rules. - Guard(Selector), + Guard(RecipeId), /// Allows recursive show rules again. - Unguard(Selector), + Unguard(RecipeId), } impl StyleEntry { @@ -243,8 +243,7 @@ impl<'a> StyleChain<'a> { world: Tracked<dyn World>, target: Target, ) -> SourceResult<Option<Content>> { - // Find out how many recipes there any and whether any of their patterns - // match. + // Find out how many recipes there any and whether any of them match. let mut n = 0; let mut any = true; for recipe in self.entries().filter_map(StyleEntry::recipe) { @@ -258,7 +257,7 @@ impl<'a> StyleChain<'a> { if any { for recipe in self.entries().filter_map(StyleEntry::recipe) { if recipe.applicable(target) { - let sel = Selector::Nth(n); + let sel = RecipeId::Nth(n); if self.guarded(sel) { guarded = true; } else if let Some(content) = recipe.apply(world, sel, target)? { @@ -273,7 +272,7 @@ impl<'a> StyleChain<'a> { if let Target::Node(node) = target { // Realize if there was no matching recipe. if realized.is_none() { - let sel = Selector::Base(node.id()); + let sel = RecipeId::Base(node.id()); if self.guarded(sel) { guarded = true; } else { @@ -302,7 +301,7 @@ impl<'a> StyleChain<'a> { } /// Whether the recipe identified by the selector is guarded. - fn guarded(self, sel: Selector) -> bool { + fn guarded(self, sel: RecipeId) -> bool { for entry in self.entries() { match *entry { StyleEntry::Guard(s) if s == sel => return true, @@ -976,8 +975,8 @@ impl Fold for PartialStroke<Abs> { pub struct Recipe { /// The span errors are reported with. pub span: Span, - /// The pattern that the rule applies to. - pub pattern: Option<Pattern>, + /// Determines whether the recipe applies to a node. + pub selector: Option<Selector>, /// The transformation to perform on the match. pub transform: Transform, } @@ -985,28 +984,26 @@ pub struct Recipe { impl Recipe { /// Whether the recipe is applicable to the target. pub fn applicable(&self, target: Target) -> bool { - match (&self.pattern, target) { - (Some(Pattern::Node(id)), Target::Node(node)) => *id == node.id(), - (Some(Pattern::Regex(_)), Target::Text(_)) => true, - _ => false, - } + self.selector + .as_ref() + .map_or(false, |selector| selector.matches(target)) } /// Try to apply the recipe to the target. pub fn apply( &self, world: Tracked<dyn World>, - sel: Selector, + sel: RecipeId, target: Target, ) -> SourceResult<Option<Content>> { - let content = match (target, &self.pattern) { - (Target::Node(node), Some(Pattern::Node(id))) if node.id() == *id => { + let content = match (target, &self.selector) { + (Target::Node(node), Some(Selector::Node(id, _))) if node.id() == *id => { self.transform.apply(world, self.span, || { Value::Content(node.to::<dyn Show>().unwrap().unguard_parts(sel)) })? } - (Target::Text(text), Some(Pattern::Regex(regex))) => { + (Target::Text(text), Some(Selector::Regex(regex))) => { let make = world.config().items.text; let mut result = vec![]; let mut cursor = 0; @@ -1043,8 +1040,8 @@ impl Recipe { /// Whether this recipe is for the given node. pub fn is_of(&self, node: NodeId) -> bool { - match self.pattern { - Some(Pattern::Node(id)) => id == node, + match self.selector { + Some(Selector::Node(id, _)) => id == node, _ => false, } } @@ -1052,24 +1049,42 @@ impl Recipe { impl Debug for Recipe { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Recipe matching {:?}", self.pattern) + write!(f, "Recipe matching {:?}", self.selector) } } -/// A show rule pattern that may match a target. +/// A selector in a show rule. #[derive(Debug, Clone, PartialEq, Hash)] -pub enum Pattern { - /// Defines the appearence of some node. - Node(NodeId), - /// Defines text to be replaced. +pub enum Selector { + /// Matches a specific type of node. + /// + /// If there is a dictionary, only nodes with the fields from the + /// dictionary match. + Node(NodeId, Option<Dict>), + /// Matches text through a regular expression. Regex(Regex), } -impl Pattern { - /// Define a simple text replacement pattern. +impl Selector { + /// Define a simple text selector. pub fn text(text: &str) -> Self { Self::Regex(Regex::new(®ex::escape(text)).unwrap()) } + + /// Whether the selector matches for the target. + pub fn matches(&self, target: Target) -> bool { + match (self, target) { + (Self::Node(id, dict), Target::Node(node)) => { + *id == node.id() + && dict + .iter() + .flat_map(|dict| dict.iter()) + .all(|(name, value)| node.field(name).as_ref() == Some(value)) + } + (Self::Regex(_), Target::Text(_)) => true, + _ => false, + } + } } /// A show rule transformation that can be applied to a match. @@ -1113,7 +1128,7 @@ pub enum Target<'a> { /// Identifies a show rule recipe. #[derive(Debug, Copy, Clone, PartialEq, Hash)] -pub enum Selector { +pub enum RecipeId { /// The nth recipe from the top of the chain. Nth(usize), /// The base recipe for a kind of node. @@ -1123,8 +1138,8 @@ pub enum Selector { /// A node that can be realized given some styles. #[capability] pub trait Show: 'static + Sync + Send { - /// Unguard nested content against recursive show rules. - fn unguard_parts(&self, sel: Selector) -> Content; + /// Unguard nested content against a specific recipe. + fn unguard_parts(&self, id: RecipeId) -> Content; /// The base recipe for this node that is executed if there is no /// user-defined show rule. |
