From ba4d1dacaf4c8739d49380c136b40cfced37e01c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 15 Feb 2025 08:05:10 +0100 Subject: [PATCH 1/5] Implement basic `.contains` ancestry selector --- .../typst-library/src/foundations/content.rs | 2 +- .../typst-library/src/foundations/selector.rs | 20 ++++++++++++++++--- .../src/introspection/introspector.rs | 5 +++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/typst-library/src/foundations/content.rs b/crates/typst-library/src/foundations/content.rs index 76cd6a222..424b4597e 100644 --- a/crates/typst-library/src/foundations/content.rs +++ b/crates/typst-library/src/foundations/content.rs @@ -412,7 +412,7 @@ impl Content { /// Queries the content tree for all elements that match the given selector. /// /// Elements produced in `show` rules will not be included in the results. - pub fn query(&self, selector: Selector) -> Vec { + pub fn query(&self, selector: &Selector) -> Vec { let mut results = Vec::new(); self.traverse(&mut |element| { if selector.matches(&element, None) { diff --git a/crates/typst-library/src/foundations/selector.rs b/crates/typst-library/src/foundations/selector.rs index bf5449d94..1435465ab 100644 --- a/crates/typst-library/src/foundations/selector.rs +++ b/crates/typst-library/src/foundations/selector.rs @@ -93,6 +93,9 @@ pub enum Selector { Before { selector: Arc, end: Arc, inclusive: bool }, /// Matches all matches of `selector` after `start`. After { selector: Arc, start: Arc, inclusive: bool }, + /// Matches all children of `selector` matchin `c` + /// TODO: Better name + Contains { selector: Arc, c: Arc }, } impl Selector { @@ -139,7 +142,10 @@ impl Selector { } Self::Location(location) => target.location() == Some(*location), // Not supported here. - Self::Regex(_) | Self::Before { .. } | Self::After { .. } => false, + Self::Regex(_) + | Self::Before { .. } + | Self::After { .. } + | Self::Contains { .. } => false, } } } @@ -221,6 +227,11 @@ impl Selector { inclusive, } } + + #[func] + pub fn contains(self, c: LocatableSelector) -> Selector { + Self::Contains { selector: Arc::new(self), c: Arc::new(c.0) } + } } impl From for Selector { @@ -266,6 +277,7 @@ impl Repr for Selector { inclusive_arg ) } + Self::Contains { .. } => todo!(), } } } @@ -352,7 +364,8 @@ impl FromValue for LocatableSelector { } } Selector::Before { selector, end: split, .. } - | Selector::After { selector, start: split, .. } => { + | Selector::After { selector, start: split, .. } + | Selector::Contains { selector, c: split } => { for selector in [selector, split] { validate(selector)?; } @@ -431,7 +444,8 @@ impl FromValue for ShowableSelector { | Selector::Location(_) | Selector::Can(_) | Selector::Before { .. } - | Selector::After { .. } => { + | Selector::After { .. } + | Selector::Contains { .. } => { bail!("this selector cannot be used with show") } } diff --git a/crates/typst-library/src/introspection/introspector.rs b/crates/typst-library/src/introspection/introspector.rs index 8cbaea891..1b0f08fcb 100644 --- a/crates/typst-library/src/introspection/introspector.rs +++ b/crates/typst-library/src/introspection/introspector.rs @@ -185,6 +185,11 @@ impl Introspector { } list } + Selector::Contains { selector, c } => self + .query(selector) + .iter() + .flat_map(|children| children.query(c)) + .collect(), // Not supported here. Selector::Can(_) | Selector::Regex(_) => EcoVec::new(), }; From ff51535d7d03b0dbb2a0c092287f1a90c58ae65c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 15 Feb 2025 08:25:12 +0100 Subject: [PATCH 2/5] Add some documentation and a better name for children_selector --- .../typst-library/src/foundations/selector.rs | 20 ++++++++++++------- .../src/introspection/introspector.rs | 4 ++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/typst-library/src/foundations/selector.rs b/crates/typst-library/src/foundations/selector.rs index 1435465ab..58a43224b 100644 --- a/crates/typst-library/src/foundations/selector.rs +++ b/crates/typst-library/src/foundations/selector.rs @@ -93,9 +93,8 @@ pub enum Selector { Before { selector: Arc, end: Arc, inclusive: bool }, /// Matches all matches of `selector` after `start`. After { selector: Arc, start: Arc, inclusive: bool }, - /// Matches all children of `selector` matchin `c` - /// TODO: Better name - Contains { selector: Arc, c: Arc }, + /// Matches all children of `selector` matching `children_selector` + Contains { selector: Arc, children_selector: Arc }, } impl Selector { @@ -228,9 +227,14 @@ impl Selector { } } + /// Returns a modified selector that only matches children of what `self` + /// selects that match `children_selector`. #[func] - pub fn contains(self, c: LocatableSelector) -> Selector { - Self::Contains { selector: Arc::new(self), c: Arc::new(c.0) } + pub fn contains(self, children_selector: LocatableSelector) -> Selector { + Self::Contains { + selector: Arc::new(self), + children_selector: Arc::new(children_selector.0), + } } } @@ -277,7 +281,9 @@ impl Repr for Selector { inclusive_arg ) } - Self::Contains { .. } => todo!(), + Self::Contains { selector, children_selector } => { + eco_format!("{}.contains({})", selector.repr(), children_selector.repr()) + } } } } @@ -365,7 +371,7 @@ impl FromValue for LocatableSelector { } Selector::Before { selector, end: split, .. } | Selector::After { selector, start: split, .. } - | Selector::Contains { selector, c: split } => { + | Selector::Contains { selector, children_selector: split } => { for selector in [selector, split] { validate(selector)?; } diff --git a/crates/typst-library/src/introspection/introspector.rs b/crates/typst-library/src/introspection/introspector.rs index 1b0f08fcb..83ccb77aa 100644 --- a/crates/typst-library/src/introspection/introspector.rs +++ b/crates/typst-library/src/introspection/introspector.rs @@ -185,10 +185,10 @@ impl Introspector { } list } - Selector::Contains { selector, c } => self + Selector::Contains { selector, children_selector } => self .query(selector) .iter() - .flat_map(|children| children.query(c)) + .flat_map(|children| children.query(children_selector)) .collect(), // Not supported here. Selector::Can(_) | Selector::Regex(_) => EcoVec::new(), From 5fb96b28b4019dbb414addcc4867f12630e3bc6c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 16 Feb 2025 16:22:04 +0100 Subject: [PATCH 3/5] rename .contains to .within --- .../typst-library/src/foundations/selector.rs | 21 +++++++++---------- .../src/introspection/introspector.rs | 6 +++--- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/typst-library/src/foundations/selector.rs b/crates/typst-library/src/foundations/selector.rs index 58a43224b..312e5299f 100644 --- a/crates/typst-library/src/foundations/selector.rs +++ b/crates/typst-library/src/foundations/selector.rs @@ -93,8 +93,8 @@ pub enum Selector { Before { selector: Arc, end: Arc, inclusive: bool }, /// Matches all matches of `selector` after `start`. After { selector: Arc, start: Arc, inclusive: bool }, - /// Matches all children of `selector` matching `children_selector` - Contains { selector: Arc, children_selector: Arc }, + /// Matches all children of `ancestor` matching `selector` + Within { selector: Arc, ancestor: Arc }, } impl Selector { @@ -144,7 +144,7 @@ impl Selector { Self::Regex(_) | Self::Before { .. } | Self::After { .. } - | Self::Contains { .. } => false, + | Self::Within { .. } => false, } } } @@ -227,13 +227,12 @@ impl Selector { } } - /// Returns a modified selector that only matches children of what `self` - /// selects that match `children_selector`. + /// Returns a modified selector that only matches `self` if it is contained in an `ancestor`. #[func] - pub fn contains(self, children_selector: LocatableSelector) -> Selector { - Self::Contains { + pub fn within(self, ancestor: LocatableSelector) -> Selector { + Self::Within { selector: Arc::new(self), - children_selector: Arc::new(children_selector.0), + ancestor: Arc::new(ancestor.0), } } } @@ -281,7 +280,7 @@ impl Repr for Selector { inclusive_arg ) } - Self::Contains { selector, children_selector } => { + Self::Within { selector, ancestor: children_selector } => { eco_format!("{}.contains({})", selector.repr(), children_selector.repr()) } } @@ -371,7 +370,7 @@ impl FromValue for LocatableSelector { } Selector::Before { selector, end: split, .. } | Selector::After { selector, start: split, .. } - | Selector::Contains { selector, children_selector: split } => { + | Selector::Within { selector, ancestor: split } => { for selector in [selector, split] { validate(selector)?; } @@ -451,7 +450,7 @@ impl FromValue for ShowableSelector { | Selector::Can(_) | Selector::Before { .. } | Selector::After { .. } - | Selector::Contains { .. } => { + | Selector::Within { .. } => { bail!("this selector cannot be used with show") } } diff --git a/crates/typst-library/src/introspection/introspector.rs b/crates/typst-library/src/introspection/introspector.rs index 83ccb77aa..c09a1f3b6 100644 --- a/crates/typst-library/src/introspection/introspector.rs +++ b/crates/typst-library/src/introspection/introspector.rs @@ -185,10 +185,10 @@ impl Introspector { } list } - Selector::Contains { selector, children_selector } => self - .query(selector) + Selector::Within { selector, ancestor } => self + .query(ancestor) .iter() - .flat_map(|children| children.query(children_selector)) + .flat_map(|children| children.query(selector)) .collect(), // Not supported here. Selector::Can(_) | Selector::Regex(_) => EcoVec::new(), From 2ac47bc13ec01ee1ca2a93b443e9005f52fd5502 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 16 Feb 2025 20:50:22 +0100 Subject: [PATCH 4/5] write tests for within selector --- tests/ref/query-within.png | Bin 0 -> 560 bytes tests/suite/introspection/query.typ | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/ref/query-within.png diff --git a/tests/ref/query-within.png b/tests/ref/query-within.png new file mode 100644 index 0000000000000000000000000000000000000000..2092df0a76e12601cd4e865fb6720ccffd04576c GIT binary patch literal 560 zcmV-00?+-4P)FQSPEQhJC)R;ya|#DbuLHkB|g#uhE2CMpOD13?U=rf^gQaWg7D@U`&) zjxJS1MFbVZK^KCk;8g@iVH|b5>fEb;7o#p+xHxB!b5{Se`oZ}=&Pf1MB?v6Az^4x0 zv3cNzD}EN(zN1_$aI5dCWA(6!PMc)#>-F!t>LBN2rC&FQ;Bhww^z&GrNeqlC4-x$J z>ATFNGjoX5LVY!5z(yG6iW~`n1wIj+ofzSPd$h>|_v(qt5D#4c(#Zpx z^$v>%_N9RV?t18bHNgQ}uw-)yEU>@=3mj)~o!g!LzmEaNt;=ZFB@FPh6wK&c4){{p zi+d{^@U{7lhrd y@W5EF^E9)-%N3pumQhzM%dZw#V1WfbruG3<@~s@lYKIE|0000 e.body), ref) +} + += A #strong[a] #label("strong") + +#strong[b] #label("strong") + +== B + +== C #strong[c] #label("strong") +> +#test-selector( + selector().within(heading), + ([a], [c]), +) + +#test-selector( + selector().within(heading.where(level: 2)), + ([c], ), +) From 5aa9b90a7b3e7f5ed964bf6727e445bdfc5211cd Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 16 Feb 2025 21:15:13 +0100 Subject: [PATCH 5/5] fix repr --- crates/typst-library/src/foundations/selector.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/typst-library/src/foundations/selector.rs b/crates/typst-library/src/foundations/selector.rs index 312e5299f..7d76ac209 100644 --- a/crates/typst-library/src/foundations/selector.rs +++ b/crates/typst-library/src/foundations/selector.rs @@ -280,8 +280,8 @@ impl Repr for Selector { inclusive_arg ) } - Self::Within { selector, ancestor: children_selector } => { - eco_format!("{}.contains({})", selector.repr(), children_selector.repr()) + Self::Within { selector, ancestor } => { + eco_format!("{}.within({})", selector.repr(), ancestor.repr()) } } }