summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorSébastien d'Herbais de Thun <sebastien.d.herbais@gmail.com>2023-04-04 19:21:25 +0200
committerGitHub <noreply@github.com>2023-04-04 19:21:25 +0200
commitf347ed4314e32383dc09ff234180e8ea6fef7b8b (patch)
treedba4296812a131e52da4eb0079fd0d240860d832 /src/model
parent5b0297464efc131beb7be84fa7a02b9a8670b5dd (diff)
Improved figure numbering, labelling and referencing (#491)
Diffstat (limited to 'src/model')
-rw-r--r--src/model/content.rs64
-rw-r--r--src/model/element.rs8
-rw-r--r--src/model/realize.rs6
-rw-r--r--src/model/styles.rs17
4 files changed, 89 insertions, 6 deletions
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(&regex::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))