diff --git a/crates/typst/src/realize/process.rs b/crates/typst/src/realize/process.rs index 8d3d01a61..a7bb7d1dd 100644 --- a/crates/typst/src/realize/process.rs +++ b/crates/typst/src/realize/process.rs @@ -198,9 +198,14 @@ fn prepare( // Generate a location for the element, which uniquely identifies it in // the document. This has some overhead, so we only do it for elements // that are explicitly marked as locatable and labelled elements. - if target.can::() || target.label().is_some() { + // + // The element could already have a location even if it is not prepared + // when it stems from a query. + let mut located = target.location().is_some(); + if !located && (target.can::() || target.label().is_some()) { let location = engine.locator.locate(hash128(&target)); target.set_location(location); + located = true; } // Apply built-in show-set rules. User-defined show-set rules are already @@ -220,24 +225,24 @@ fn prepare( // available in rules. target.materialize(styles.chain(map)); + if located { + // Apply metadata to be able to find the element in the frames. Do this + // after synthesis and materialization, so that it includes the + // synthesized fields. Do it before marking as prepared so that show-set + // rules will apply to this element when queried. This adds a style to + // the whole element's subtree identifying it as belonging to the + // element. + map.set(MetaElem::set_data(smallvec![Meta::Elem(target.clone())])); + } + // Ensure that this preparation only runs once by marking the element as // prepared. target.mark_prepared(); - // Apply metadata be able to find the element in the frames. - // Do this after synthesis, so that it includes the synthesized fields. - if target.location().is_some() { - // Add a style to the whole element's subtree identifying it as - // belonging to the element. - map.set(MetaElem::set_data(smallvec![Meta::Elem(target.clone())])); - - // Return an extra meta elem that will be attached so that the metadata - // styles are not lost in case the element's show rule results in - // nothing. - return Ok(Some(Packed::new(MetaElem::new()).spanned(target.span()))); - } - - Ok(None) + // If the element is located, return an extra meta elem that will be + // attached so that the metadata styles are not lost in case the element's + // show rule results in nothing. + Ok(located.then(|| Packed::new(MetaElem::new()).spanned(target.span()))) } /// Apply a step. diff --git a/tests/ref/issue-3726-query-show-set.png b/tests/ref/issue-3726-query-show-set.png new file mode 100644 index 000000000..3f5c88846 Binary files /dev/null and b/tests/ref/issue-3726-query-show-set.png differ diff --git a/tests/suite/introspection/query.typ b/tests/suite/introspection/query.typ index 3a4b4fbf4..6cdd4bab0 100644 --- a/tests/suite/introspection/query.typ +++ b/tests/suite/introspection/query.typ @@ -265,3 +265,19 @@ )), ([Frog], [GiraffeCat], [Iguana]) ) + +--- issue-3726-query-show-set --- +// Test that show rules apply to queried elements, i.e. that the content +// returned from `query` isn't yet marked as prepared. +#set heading(numbering: "1.") +#show heading: underline += Hi + +#set heading(numbering: "I.") +#show heading: set text(blue) +#show heading: highlight.with(fill: aqua.lighten(50%)) += Bye + +// New show rules apply to this, but its location and the materialized fields +// from the original are retained. +#context query(heading).join()