From b744b8781835d704236210d8a3dd603c6a29c8d0 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 30 Jan 2024 15:24:41 +0100 Subject: [PATCH] Respect set rules in where selectors (#3290) --- crates/typst-macros/src/elem.rs | 60 +++++++++--- crates/typst/src/foundations/content.rs | 18 ++-- crates/typst/src/foundations/element.rs | 3 + crates/typst/src/foundations/selector.rs | 21 ++-- crates/typst/src/foundations/styles.rs | 4 +- .../typst/src/introspection/introspector.rs | 8 +- crates/typst/src/realize/mod.rs | 4 +- tests/ref/compiler/select-where-styles.png | Bin 0 -> 17334 bytes tests/typ/compiler/select-where-styles.typ | 91 ++++++++++++++++++ 9 files changed, 175 insertions(+), 34 deletions(-) create mode 100644 tests/ref/compiler/select-where-styles.png create mode 100644 tests/typ/compiler/select-where-styles.typ diff --git a/crates/typst-macros/src/elem.rs b/crates/typst-macros/src/elem.rs index a3e2a0079..d689aa11d 100644 --- a/crates/typst-macros/src/elem.rs +++ b/crates/typst-macros/src/elem.rs @@ -93,7 +93,7 @@ impl Elem { /// Fields that are visible to the user. fn visible_fields(&self) -> impl Iterator + Clone { - self.real_fields().filter(|field| !field.internal && !field.ghost) + self.real_fields().filter(|field| !field.internal) } } @@ -509,7 +509,11 @@ fn create_field_method(field: &Field) -> TokenStream { quote! { (&self, styles: #foundations::StyleChain) -> #output } }; - let mut value = create_style_chain_access(field, quote! { self.#ident.as_ref() }); + let mut value = create_style_chain_access( + field, + field.borrowed, + quote! { self.#ident.as_ref() }, + ); if field.resolve { value = quote! { #foundations::Resolve::resolve(#value, styles) }; } @@ -530,7 +534,7 @@ fn create_field_in_method(field: &Field) -> TokenStream { let ref_ = field.borrowed.then(|| quote! { & }); - let mut value = create_style_chain_access(field, quote! { None }); + let mut value = create_style_chain_access(field, field.borrowed, quote! { None }); if field.resolve { value = quote! { #foundations::Resolve::resolve(#value, styles) }; } @@ -560,16 +564,20 @@ fn create_set_field_method(field: &Field) -> TokenStream { } /// Create a style chain access method for a field. -fn create_style_chain_access(field: &Field, inherent: TokenStream) -> TokenStream { +fn create_style_chain_access( + field: &Field, + borrowed: bool, + inherent: TokenStream, +) -> TokenStream { let Field { ty, default, enum_ident, const_ident, .. } = field; - let getter = match (field.fold, field.borrowed) { + let getter = match (field.fold, borrowed) { (false, false) => quote! { get }, (false, true) => quote! { get_ref }, (true, _) => quote! { get_folded }, }; - let default = if field.borrowed { + let default = if borrowed { quote! { || &#const_ident } } else { match default { @@ -821,9 +829,10 @@ fn create_capable_impl(element: &Elem) -> TokenStream { /// Creates the element's `Fields` implementation. fn create_fields_impl(element: &Elem) -> TokenStream { let into_value = quote! { #foundations::IntoValue::into_value }; + let visible_non_ghost = || element.visible_fields().filter(|field| !field.ghost); // Fields that can be checked using the `has` method. - let has_arms = element.visible_fields().map(|field| { + let has_arms = visible_non_ghost().map(|field| { let Field { enum_ident, ident, .. } = field; let expr = if field.inherent() { @@ -836,7 +845,7 @@ fn create_fields_impl(element: &Elem) -> TokenStream { }); // Fields that can be accessed using the `field` method. - let field_arms = element.visible_fields().map(|field| { + let field_arms = visible_non_ghost().filter(|field| !field.ghost).map(|field| { let Field { enum_ident, ident, .. } = field; let expr = if field.inherent() { @@ -848,8 +857,29 @@ fn create_fields_impl(element: &Elem) -> TokenStream { quote! { Fields::#enum_ident => #expr } }); + // Fields that can be accessed using the `field_with_styles` method. + let field_with_styles_arms = element.visible_fields().map(|field| { + let Field { enum_ident, ident, .. } = field; + + let expr = if field.inherent() { + quote! { Some(#into_value(self.#ident.clone())) } + } else if field.synthesized && field.default.is_none() { + quote! { self.#ident.clone().map(#into_value) } + } else { + let value = create_style_chain_access( + field, + false, + if field.ghost { quote!(None) } else { quote!(self.#ident.as_ref()) }, + ); + + quote! { Some(#into_value(#value)) } + }; + + quote! { Fields::#enum_ident => #expr } + }); + // Creation of the `fields` dictionary for inherent fields. - let field_inserts = element.visible_fields().map(|field| { + let field_inserts = visible_non_ghost().map(|field| { let Field { ident, name, .. } = field; let string = quote! { #name.into() }; @@ -873,7 +903,7 @@ fn create_fields_impl(element: &Elem) -> TokenStream { type Enum = Fields; fn has(&self, id: u8) -> bool { - let Ok(id) = <#ident as #foundations::Fields>::Enum::try_from(id) else { + let Ok(id) = Fields::try_from(id) else { return false; }; @@ -884,13 +914,21 @@ fn create_fields_impl(element: &Elem) -> TokenStream { } fn field(&self, id: u8) -> Option<#foundations::Value> { - let id = <#ident as #foundations::Fields>::Enum::try_from(id).ok()?; + let id = Fields::try_from(id).ok()?; match id { #(#field_arms,)* _ => None, } } + fn field_with_styles(&self, id: u8, styles: #foundations::StyleChain) -> Option<#foundations::Value> { + let id = Fields::try_from(id).ok()?; + match id { + #(#field_with_styles_arms,)* + _ => None, + } + } + fn fields(&self) -> #foundations::Dict { let mut fields = #foundations::Dict::new(); #(#field_inserts)* diff --git a/crates/typst/src/foundations/content.rs b/crates/typst/src/foundations/content.rs index 8a7346350..49497b8ff 100644 --- a/crates/typst/src/foundations/content.rs +++ b/crates/typst/src/foundations/content.rs @@ -15,7 +15,8 @@ use crate::diag::{SourceResult, StrResult}; use crate::engine::Engine; use crate::foundations::{ elem, func, scope, ty, Dict, Element, Fields, Finalize, Guard, IntoValue, Label, - NativeElement, Recipe, Repr, Selector, Str, Style, Styles, Synthesize, Value, + NativeElement, Recipe, Repr, Selector, Str, Style, StyleChain, Styles, Synthesize, + Value, }; use crate::introspection::{Locatable, Location, Meta, MetaElem}; use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides}; @@ -181,13 +182,16 @@ impl Content { /// This is the preferred way to access fields. However, you can only use it /// if you have set the field IDs yourself or are using the field IDs /// generated by the `#[elem]` macro. - pub fn get(&self, id: u8) -> Option { + pub fn get(&self, id: u8, styles: Option) -> Option { if id == 255 { if let Some(label) = self.label() { return Some(label.into_value()); } } - self.inner.elem.field(id) + match styles { + Some(styles) => self.inner.elem.field_with_styles(id, styles), + None => self.inner.elem.field(id), + } } /// Get a field by name. @@ -201,7 +205,7 @@ impl Content { } } let id = self.elem().field_id(name)?; - self.get(id) + self.get(id, None) } /// Get a field by ID, returning a missing field error if it does not exist. @@ -210,7 +214,7 @@ impl Content { /// if you have set the field IDs yourself or are using the field IDs /// generated by the `#[elem]` macro. pub fn field(&self, id: u8) -> StrResult { - self.get(id) + self.get(id, None) .ok_or_else(|| missing_field(self.elem().field_name(id).unwrap())) } @@ -400,7 +404,7 @@ impl Content { pub fn query(&self, selector: Selector) -> Vec { let mut results = Vec::new(); self.traverse(&mut |element| { - if selector.matches(&element) { + if selector.matches(&element, None) { results.push(element); } }); @@ -414,7 +418,7 @@ impl Content { pub fn query_first(&self, selector: Selector) -> Option { let mut result = None; self.traverse(&mut |element| { - if result.is_none() && selector.matches(&element) { + if result.is_none() && selector.matches(&element, None) { result = Some(element); } }); diff --git a/crates/typst/src/foundations/element.rs b/crates/typst/src/foundations/element.rs index 24e5cf007..489077e4a 100644 --- a/crates/typst/src/foundations/element.rs +++ b/crates/typst/src/foundations/element.rs @@ -220,6 +220,9 @@ pub trait Fields { /// Get the field with the given field ID. fn field(&self, id: u8) -> Option; + /// Get the field with the given ID in the presence of styles. + fn field_with_styles(&self, id: u8, styles: StyleChain) -> Option; + /// Get the fields of the element. fn fields(&self) -> Dict; } diff --git a/crates/typst/src/foundations/selector.rs b/crates/typst/src/foundations/selector.rs index 2321f317e..16bd721d6 100644 --- a/crates/typst/src/foundations/selector.rs +++ b/crates/typst/src/foundations/selector.rs @@ -7,7 +7,7 @@ use smallvec::SmallVec; use crate::diag::{bail, StrResult}; use crate::foundations::{ cast, func, repr, scope, ty, CastInfo, Content, Dict, Element, FromValue, Func, - Label, Reflect, Regex, Repr, Str, Type, Value, + Label, Reflect, Regex, Repr, Str, StyleChain, Type, Value, }; use crate::introspection::{Locatable, Location}; use crate::symbols::Symbol; @@ -128,23 +128,26 @@ impl Selector { } /// Whether the selector matches for the target. - pub fn matches(&self, target: &Content) -> bool { - // TODO: optimize field access to not clone. + pub fn matches(&self, target: &Content, styles: Option) -> bool { match self { Self::Elem(element, dict) => { + // TODO: Optimize field access to not clone. target.func() == *element - && dict - .iter() - .flat_map(|dict| dict.iter()) - .all(|(id, value)| target.get(*id).as_ref() == Some(value)) + && dict.iter().flat_map(|dict| dict.iter()).all(|(id, value)| { + target.get(*id, styles).as_ref() == Some(value) + }) } Self::Label(label) => target.label() == Some(*label), Self::Regex(regex) => target .to_packed::() .map_or(false, |elem| regex.is_match(elem.text())), Self::Can(cap) => target.func().can_type_id(*cap), - Self::Or(selectors) => selectors.iter().any(move |sel| sel.matches(target)), - Self::And(selectors) => selectors.iter().all(move |sel| sel.matches(target)), + Self::Or(selectors) => { + selectors.iter().any(move |sel| sel.matches(target, styles)) + } + Self::And(selectors) => { + selectors.iter().all(move |sel| sel.matches(target, styles)) + } Self::Location(location) => target.location() == Some(*location), // Not supported here. Self::Before { .. } | Self::After { .. } => false, diff --git a/crates/typst/src/foundations/styles.rs b/crates/typst/src/foundations/styles.rs index bac92887b..6946d076d 100644 --- a/crates/typst/src/foundations/styles.rs +++ b/crates/typst/src/foundations/styles.rs @@ -369,10 +369,10 @@ impl Recipe { } /// Whether the recipe is applicable to the target. - pub fn applicable(&self, target: &Content) -> bool { + pub fn applicable(&self, target: &Content, styles: StyleChain) -> bool { self.selector .as_ref() - .map_or(false, |selector| selector.matches(target)) + .map_or(false, |selector| selector.matches(target, Some(styles))) } /// Apply the recipe to the given content. diff --git a/crates/typst/src/introspection/introspector.rs b/crates/typst/src/introspection/introspector.rs index 75882edf0..f8270025c 100644 --- a/crates/typst/src/introspection/introspector.rs +++ b/crates/typst/src/introspection/introspector.rs @@ -127,9 +127,11 @@ impl Introspector { indices.iter().map(|&index| self.elems[index].0.clone()).collect() }) .unwrap_or_default(), - Selector::Elem(..) | Selector::Regex(_) | Selector::Can(_) => { - self.all().filter(|elem| selector.matches(elem)).cloned().collect() - } + Selector::Elem(..) | Selector::Regex(_) | Selector::Can(_) => self + .all() + .filter(|elem| selector.matches(elem, None)) + .cloned() + .collect(), Selector::Location(location) => { self.get(location).cloned().into_iter().collect() } diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs index cd40ac866..f55555978 100644 --- a/crates/typst/src/realize/mod.rs +++ b/crates/typst/src/realize/mod.rs @@ -80,7 +80,7 @@ pub fn applicable(target: &Content, styles: StyleChain) -> bool { // Find out whether any recipe matches and is unguarded. for recipe in styles.recipes() { - if !target.is_guarded(Guard(n)) && recipe.applicable(target) { + if !target.is_guarded(Guard(n)) && recipe.applicable(target, styles) { return true; } n -= 1; @@ -133,7 +133,7 @@ pub fn realize( // Find an applicable show rule recipe. for recipe in styles.recipes() { let guard = Guard(n); - if !target.is_guarded(guard) && recipe.applicable(target) { + if !target.is_guarded(guard) && recipe.applicable(target, styles) { if let Some(content) = try_apply(engine, target, recipe, guard)? { return Ok(Some(content)); } diff --git a/tests/ref/compiler/select-where-styles.png b/tests/ref/compiler/select-where-styles.png new file mode 100644 index 0000000000000000000000000000000000000000..ffdc4babf01f05c668a0a5b6c809ef20ecbb0d0f GIT binary patch literal 17334 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 Q_$Nq^mQWC{5;gGuAAt3SO#lD@ literal 0 HcmV?d00001 diff --git a/tests/typ/compiler/select-where-styles.typ b/tests/typ/compiler/select-where-styles.typ new file mode 100644 index 000000000..028be2e92 --- /dev/null +++ b/tests/typ/compiler/select-where-styles.typ @@ -0,0 +1,91 @@ +// Test that where selectors also work with settable fields. + +--- +// Test that where selectors also trigger on set rule fields. +#show raw.where(block: false): box.with( + fill: luma(220), + inset: (x: 3pt, y: 0pt), + outset: (y: 3pt), + radius: 2pt, +) + +This is #raw("fn main() {}") some text. + +--- +// Note: This show rule is horribly inefficient because it triggers for +// every individual text element. But it should still work. +#show text.where(lang: "de"): set text(red) + +#set text(lang: "es") +Hola, mundo! + +#set text(lang: "de") +Hallo Welt! + +#set text(lang: "en") +Hello World! + +--- +// Test that folding is taken into account. +#set text(5pt) +#set text(2em) + +#[ + #show text.where(size: 2em): set text(blue) + 2em not blue +] + +#[ + #show text.where(size: 10pt): set text(blue) + 10pt blue +] + +--- +// Test again that folding is taken into account. +#set rect(width: 40pt, height: 10pt) +#set rect(stroke: blue) +#set rect(stroke: 2pt) + +#{ + show rect.where(stroke: blue): "Not Triggered" + rect() +} +#{ + show rect.where(stroke: 2pt): "Not Triggered" + rect() +} +#{ + show rect.where(stroke: 2pt + blue): "Triggered" + rect() +} + +--- +// Test that resolving is *not* taken into account. +#set line(start: (1em, 1em + 2pt)) + +#{ + show line.where(start: (1em, 1em + 2pt)): "Triggered" + line() +} +#{ + show line.where(start: (10pt, 12pt)): "Not Triggered" + line() +} + + +--- +// Test again that resolving is *not* taken into account. +#set text(hyphenate: auto) + +#[ + #show text.where(hyphenate: auto): underline + Auto +] +#[ + #show text.where(hyphenate: true): underline + True +] +#[ + #show text.where(hyphenate: false): underline + False +]