Merge 5aa9b90a7b3e7f5ed964bf6727e445bdfc5211cd into 9b09146a6b5e936966ed7ee73bce9dd2df3810ae

This commit is contained in:
Schrottkatze 2025-05-07 01:58:44 +03:00 committed by GitHub
commit d70ed5eadd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 52 additions and 4 deletions

View File

@ -412,7 +412,7 @@ impl Content {
/// Queries the content tree for all elements that match the given selector. /// Queries the content tree for all elements that match the given selector.
/// ///
/// Elements produced in `show` rules will not be included in the results. /// Elements produced in `show` rules will not be included in the results.
pub fn query(&self, selector: Selector) -> Vec<Content> { pub fn query(&self, selector: &Selector) -> Vec<Content> {
let mut results = Vec::new(); let mut results = Vec::new();
self.traverse(&mut |element| -> ControlFlow<()> { self.traverse(&mut |element| -> ControlFlow<()> {
if selector.matches(&element, None) { if selector.matches(&element, None) {

View File

@ -93,6 +93,8 @@ pub enum Selector {
Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool }, Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool },
/// Matches all matches of `selector` after `start`. /// Matches all matches of `selector` after `start`.
After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool }, After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool },
/// Matches all children of `ancestor` matching `selector`
Within { selector: Arc<Self>, ancestor: Arc<Self> },
} }
impl Selector { impl Selector {
@ -139,7 +141,10 @@ impl Selector {
} }
Self::Location(location) => target.location() == Some(*location), Self::Location(location) => target.location() == Some(*location),
// Not supported here. // Not supported here.
Self::Regex(_) | Self::Before { .. } | Self::After { .. } => false, Self::Regex(_)
| Self::Before { .. }
| Self::After { .. }
| Self::Within { .. } => false,
} }
} }
} }
@ -221,6 +226,15 @@ impl Selector {
inclusive, inclusive,
} }
} }
/// Returns a modified selector that only matches `self` if it is contained in an `ancestor`.
#[func]
pub fn within(self, ancestor: LocatableSelector) -> Selector {
Self::Within {
selector: Arc::new(self),
ancestor: Arc::new(ancestor.0),
}
}
} }
impl From<Location> for Selector { impl From<Location> for Selector {
@ -266,6 +280,9 @@ impl Repr for Selector {
inclusive_arg inclusive_arg
) )
} }
Self::Within { selector, ancestor } => {
eco_format!("{}.within({})", selector.repr(), ancestor.repr())
}
} }
} }
} }
@ -352,7 +369,8 @@ impl FromValue for LocatableSelector {
} }
} }
Selector::Before { selector, end: split, .. } Selector::Before { selector, end: split, .. }
| Selector::After { selector, start: split, .. } => { | Selector::After { selector, start: split, .. }
| Selector::Within { selector, ancestor: split } => {
for selector in [selector, split] { for selector in [selector, split] {
validate(selector)?; validate(selector)?;
} }
@ -431,7 +449,8 @@ impl FromValue for ShowableSelector {
| Selector::Location(_) | Selector::Location(_)
| Selector::Can(_) | Selector::Can(_)
| Selector::Before { .. } | Selector::Before { .. }
| Selector::After { .. } => { | Selector::After { .. }
| Selector::Within { .. } => {
bail!("this selector cannot be used with show") bail!("this selector cannot be used with show")
} }
} }

View File

@ -185,6 +185,11 @@ impl Introspector {
} }
list list
} }
Selector::Within { selector, ancestor } => self
.query(ancestor)
.iter()
.flat_map(|children| children.query(selector))
.collect(),
// Not supported here. // Not supported here.
Selector::Can(_) | Selector::Regex(_) => EcoVec::new(), Selector::Can(_) | Selector::Regex(_) => EcoVec::new(),
}; };

BIN
tests/ref/query-within.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

View File

@ -250,3 +250,27 @@
t("b") t("b")
block(height: 1fr, metadata("b")) block(height: 1fr, metadata("b"))
} }
--- query-within ---
#let test-selector(selector, ref) = context {
test(query(selector).map(e => e.body), ref)
}
= A #strong[a] #label("strong")
#strong[b] #label("strong")
== B
== C #strong[c] #label("strong")
>
#test-selector(
selector(<strong>).within(heading),
([a], [c]),
)
#test-selector(
selector(<strong>).within(heading.where(level: 2)),
([c], ),
)