diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/geom/smart.rs | 9 | ||||
| -rw-r--r-- | src/model/content.rs | 64 | ||||
| -rw-r--r-- | src/model/element.rs | 8 | ||||
| -rw-r--r-- | src/model/realize.rs | 6 | ||||
| -rw-r--r-- | src/model/styles.rs | 17 |
5 files changed, 98 insertions, 6 deletions
diff --git a/src/geom/smart.rs b/src/geom/smart.rs index c977d651..d9f8fd16 100644 --- a/src/geom/smart.rs +++ b/src/geom/smart.rs @@ -20,6 +20,15 @@ impl<T> Smart<T> { matches!(self, Self::Custom(_)) } + /// Returns a reference the contained custom value. + /// If the value is [`Smart::Auto`], `None` is returned. + pub fn as_custom(self) -> Option<T> { + match self { + Self::Auto => None, + Self::Custom(x) => Some(x), + } + } + /// Map the contained custom value with `f`. pub fn map<F, U>(self, f: F) -> Smart<U> where diff --git a/src/model/content.rs b/src/model/content.rs index f95ce104..db867715 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -7,7 +7,7 @@ use ecow::{eco_format, EcoString, EcoVec}; use super::{ element, Behave, Behaviour, ElemFunc, Element, Fold, Guard, Label, Locatable, - Location, Recipe, Style, Styles, Synthesize, + Location, Recipe, Selector, Style, Styles, Synthesize, }; use crate::diag::{SourceResult, StrResult}; use crate::doc::Meta; @@ -105,6 +105,12 @@ impl Content { (self.func.0.vtable)(TypeId::of::<C>()).is_some() } + /// Whether the contained element has the given capability. + /// Where the capability is given by a `TypeId`. + pub fn can_type_id(&self, type_id: TypeId) -> bool { + (self.func.0.vtable)(type_id).is_some() + } + /// Cast to a trait object if the contained element has the given /// capability. pub fn with<C>(&self) -> Option<&C> @@ -347,6 +353,62 @@ impl Content { pub fn set_location(&mut self, location: Location) { self.attrs.push(Attr::Location(location)); } + + /// Gives an iterator over the children of this content + pub fn children(&self) -> impl Iterator<Item = &Content> { + self.attrs.iter().filter_map(Attr::child) + } + + /// Gives an iterator over the children of this content that are contained + /// within the arguments of the content. + pub fn children_in_args(&self) -> impl Iterator<Item = &Content> { + self.attrs + .iter() + .filter_map(Attr::value) + .filter_map(|value| match value { + Value::Content(content) => Some(content), + _ => None, + }) + } + + /// Queries the content tree for all elements that match the given selector. + /// + /// # Show rules + /// Elements produced in `show` rules will not be included in the results. + pub fn query(&self, selector: Selector) -> Vec<Content> { + let mut results = Vec::new(); + self.query_into(&selector, &mut results); + results + } + + /// Queries the content tree for all elements that match the given selector + /// and stores the results inside of the `results` vec. + fn query_into(&self, selector: &Selector, results: &mut Vec<Content>) { + if selector.matches(self) { + results.push(self.clone()); + } + + for attr in &self.attrs { + match attr { + Attr::Child(child) => child.query_into(selector, results), + Attr::Value(value) => walk_value(&value, selector, results), + _ => {} + } + } + + /// Walks a given value to find any content that matches the selector. + fn walk_value(value: &Value, selector: &Selector, results: &mut Vec<Content>) { + match value { + Value::Content(content) => content.query_into(selector, results), + Value::Array(array) => { + for value in array { + walk_value(value, selector, results); + } + } + _ => {} + } + } + } } impl Debug for Content { diff --git a/src/model/element.rs b/src/model/element.rs index c6738582..4c825a20 100644 --- a/src/model/element.rs +++ b/src/model/element.rs @@ -63,6 +63,14 @@ impl ElemFunc { (self.0.construct)(vm, args) } + /// Whether the contained element has the given capability. + pub fn can<C>(&self) -> bool + where + C: ?Sized + 'static, + { + (self.0.vtable)(TypeId::of::<C>()).is_some() + } + /// Create a selector for elements of this function. pub fn select(self) -> Selector { Selector::Elem(self, None) diff --git a/src/model/realize.rs b/src/model/realize.rs index e96e0dc1..48a0fbdc 100644 --- a/src/model/realize.rs +++ b/src/model/realize.rs @@ -42,7 +42,7 @@ pub fn realize( } if let Some(elem) = elem.with_mut::<dyn Synthesize>() { - elem.synthesize(styles); + elem.synthesize(vt, styles)?; } elem.mark_prepared(); @@ -152,7 +152,7 @@ fn try_apply( } // Not supported here. - Some(Selector::Any(_)) => Ok(None), + Some(Selector::Any(_) | Selector::All(_) | Selector::Can(_)) => Ok(None), None => Ok(None), } @@ -165,7 +165,7 @@ pub trait Locatable {} /// rule. pub trait Synthesize { /// Prepare the element for show rule application. - fn synthesize(&mut self, styles: StyleChain); + fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()>; } /// The base recipe for an element. diff --git a/src/model/styles.rs b/src/model/styles.rs index 82ec2ed5..4b309ed0 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -1,3 +1,4 @@ +use std::any::{Any, TypeId}; use std::fmt::{self, Debug, Formatter, Write}; use std::iter; use std::mem; @@ -260,8 +261,12 @@ pub enum Selector { Label(Label), /// Matches text elements through a regular expression. Regex(Regex), + /// Matches elements with a specific capability. + Can(TypeId), /// Matches if any of the subselectors match. Any(EcoVec<Self>), + /// Matches if all of the subselectors match. + All(EcoVec<Self>), } impl Selector { @@ -270,6 +275,11 @@ impl Selector { Self::Regex(Regex::new(®ex::escape(text)).unwrap()) } + /// Define a simple [`Selector::Can`] selector. + pub fn can<T: ?Sized + Any>() -> Self { + Self::Can(TypeId::of::<T>()) + } + /// Whether the selector matches for the target. pub fn matches(&self, target: &Content) -> bool { match self { @@ -285,7 +295,9 @@ impl Selector { target.func() == item!(text_func) && item!(text_str)(target).map_or(false, |text| regex.is_match(&text)) } + Self::Can(cap) => target.can_type_id(*cap), Self::Any(selectors) => selectors.iter().any(|sel| sel.matches(target)), + Self::All(selectors) => selectors.iter().all(|sel| sel.matches(target)), } } } @@ -303,8 +315,9 @@ impl Debug for Selector { } Self::Label(label) => label.fmt(f), Self::Regex(regex) => regex.fmt(f), - Self::Any(selectors) => { - f.write_str("any")?; + Self::Can(cap) => cap.fmt(f), + Self::Any(selectors) | Self::All(selectors) => { + f.write_str(if matches!(self, Self::Any(_)) { "any" } else { "all" })?; let pieces: Vec<_> = selectors.iter().map(|sel| eco_format!("{sel:?}")).collect(); f.write_str(&pretty_array_like(&pieces, false)) |
