summaryrefslogtreecommitdiff
path: root/crates/typst-realize/src/lib.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-07-09 10:16:36 +0200
committerGitHub <noreply@github.com>2025-07-09 08:16:36 +0000
commite5e1dcd9c01341d2cd3473ac94a70223d5966086 (patch)
treed648efad9463cb10270d55ba35210eeb1e91ee22 /crates/typst-realize/src/lib.rs
parent0a3c6939dd274f40672484695d909c2cc0d0d755 (diff)
Target-specific native show rules (#6569)
Diffstat (limited to 'crates/typst-realize/src/lib.rs')
-rw-r--r--crates/typst-realize/src/lib.rs75
1 files changed, 38 insertions, 37 deletions
diff --git a/crates/typst-realize/src/lib.rs b/crates/typst-realize/src/lib.rs
index fcfb4066..6af249cc 100644
--- a/crates/typst-realize/src/lib.rs
+++ b/crates/typst-realize/src/lib.rs
@@ -14,9 +14,9 @@ use ecow::EcoString;
use typst_library::diag::{bail, At, SourceResult};
use typst_library::engine::Engine;
use typst_library::foundations::{
- Content, Context, ContextElem, Element, NativeElement, Recipe, RecipeIndex, Selector,
- SequenceElem, Show, ShowSet, Style, StyleChain, StyledElem, Styles, SymbolElem,
- Synthesize, Transformation,
+ Content, Context, ContextElem, Element, NativeElement, NativeShowRule, Recipe,
+ RecipeIndex, Selector, SequenceElem, ShowSet, Style, StyleChain, StyledElem, Styles,
+ SymbolElem, Synthesize, TargetElem, Transformation,
};
use typst_library::html::{tag, FrameElem, HtmlElem};
use typst_library::introspection::{Locatable, SplitLocator, Tag, TagElem};
@@ -160,7 +160,7 @@ enum ShowStep<'a> {
/// A user-defined transformational show rule.
Recipe(&'a Recipe, RecipeIndex),
/// The built-in show rule.
- Builtin,
+ Builtin(NativeShowRule),
}
/// A match of a regex show rule.
@@ -382,9 +382,7 @@ fn visit_show_rules<'a>(
}
// Apply a built-in show rule.
- ShowStep::Builtin => {
- output.with::<dyn Show>().unwrap().show(s.engine, chained)
- }
+ ShowStep::Builtin(rule) => rule.apply(&output, s.engine, chained),
};
// Errors in show rules don't terminate compilation immediately. We just
@@ -426,14 +424,14 @@ fn visit_show_rules<'a>(
Ok(true)
}
-/// Inspects a target element and the current styles and determines how to
-/// proceed with the styling.
+/// Inspects an element and the current styles and determines how to proceed
+/// with the styling.
fn verdict<'a>(
engine: &mut Engine,
- target: &'a Content,
+ elem: &'a Content,
styles: StyleChain<'a>,
) -> Option<Verdict<'a>> {
- let prepared = target.is_prepared();
+ let prepared = elem.is_prepared();
let mut map = Styles::new();
let mut step = None;
@@ -441,20 +439,20 @@ fn verdict<'a>(
// fields before real synthesis runs (during preparation). It's really
// unfortunate that we have to do this, but otherwise
// `show figure.where(kind: table)` won't work :(
- let mut target = target;
+ let mut elem = elem;
let mut slot;
- if !prepared && target.can::<dyn Synthesize>() {
- slot = target.clone();
+ if !prepared && elem.can::<dyn Synthesize>() {
+ slot = elem.clone();
slot.with_mut::<dyn Synthesize>()
.unwrap()
.synthesize(engine, styles)
.ok();
- target = &slot;
+ elem = &slot;
}
// Lazily computes the total number of recipes in the style chain. We need
// it to determine whether a particular show rule was already applied to the
- // `target` previously. For this purpose, show rules are indexed from the
+ // `elem` previously. For this purpose, show rules are indexed from the
// top of the chain as the chain might grow to the bottom.
let depth = LazyCell::new(|| styles.recipes().count());
@@ -462,7 +460,7 @@ fn verdict<'a>(
// We're not interested in recipes that don't match.
if !recipe
.selector()
- .is_some_and(|selector| selector.matches(target, Some(styles)))
+ .is_some_and(|selector| selector.matches(elem, Some(styles)))
{
continue;
}
@@ -480,9 +478,9 @@ fn verdict<'a>(
continue;
}
- // Check whether this show rule was already applied to the target.
+ // Check whether this show rule was already applied to the element.
let index = RecipeIndex(*depth - r);
- if target.is_guarded(index) {
+ if elem.is_guarded(index) {
continue;
}
@@ -498,19 +496,22 @@ fn verdict<'a>(
}
// If we found no user-defined rule, also consider the built-in show rule.
- if step.is_none() && target.can::<dyn Show>() {
- step = Some(ShowStep::Builtin);
+ if step.is_none() {
+ let target = styles.get(TargetElem::target);
+ if let Some(rule) = engine.routines.rules.get(target, elem) {
+ step = Some(ShowStep::Builtin(rule));
+ }
}
// If there's no nothing to do, there is also no verdict.
if step.is_none()
&& map.is_empty()
&& (prepared || {
- target.label().is_none()
- && target.location().is_none()
- && !target.can::<dyn ShowSet>()
- && !target.can::<dyn Locatable>()
- && !target.can::<dyn Synthesize>()
+ elem.label().is_none()
+ && elem.location().is_none()
+ && !elem.can::<dyn ShowSet>()
+ && !elem.can::<dyn Locatable>()
+ && !elem.can::<dyn Synthesize>()
})
{
return None;
@@ -523,7 +524,7 @@ fn verdict<'a>(
fn prepare(
engine: &mut Engine,
locator: &mut SplitLocator,
- target: &mut Content,
+ elem: &mut Content,
map: &mut Styles,
styles: StyleChain,
) -> SourceResult<Option<(Tag, Tag)>> {
@@ -533,43 +534,43 @@ fn prepare(
//
// The element could already have a location even if it is not prepared
// when it stems from a query.
- let key = typst_utils::hash128(&target);
- if target.location().is_none()
- && (target.can::<dyn Locatable>() || target.label().is_some())
+ let key = typst_utils::hash128(&elem);
+ if elem.location().is_none()
+ && (elem.can::<dyn Locatable>() || elem.label().is_some())
{
let loc = locator.next_location(engine.introspector, key);
- target.set_location(loc);
+ elem.set_location(loc);
}
// Apply built-in show-set rules. User-defined show-set rules are already
// considered in the map built while determining the verdict.
- if let Some(show_settable) = target.with::<dyn ShowSet>() {
+ if let Some(show_settable) = elem.with::<dyn ShowSet>() {
map.apply(show_settable.show_set(styles));
}
// If necessary, generated "synthesized" fields (which are derived from
// other fields or queries). Do this after show-set so that show-set styles
// are respected.
- if let Some(synthesizable) = target.with_mut::<dyn Synthesize>() {
+ if let Some(synthesizable) = elem.with_mut::<dyn Synthesize>() {
synthesizable.synthesize(engine, styles.chain(map))?;
}
// Copy style chain fields into the element itself, so that they are
// available in rules.
- target.materialize(styles.chain(map));
+ elem.materialize(styles.chain(map));
// If the element is locatable, create start and end tags to be able to find
// the element in the frames after layout. 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.
- let tags = target
+ let tags = elem
.location()
- .map(|loc| (Tag::Start(target.clone()), Tag::End(loc, key)));
+ .map(|loc| (Tag::Start(elem.clone()), Tag::End(loc, key)));
// Ensure that this preparation only runs once by marking the element as
// prepared.
- target.mark_prepared();
+ elem.mark_prepared();
Ok(tags)
}