typst_library/foundations/
selector.rs

1use std::any::{Any, TypeId};
2use std::sync::Arc;
3
4use comemo::Tracked;
5use ecow::{eco_format, EcoString, EcoVec};
6use smallvec::SmallVec;
7
8use crate::diag::{bail, HintedStrResult, StrResult};
9use crate::foundations::{
10    cast, func, repr, scope, ty, CastInfo, Content, Context, Dict, Element, FromValue,
11    Func, Label, Reflect, Regex, Repr, Str, StyleChain, Symbol, Type, Value,
12};
13use crate::introspection::{Introspector, Locatable, Location, Unqueriable};
14
15/// A helper macro to create a field selector used in [`Selector::Elem`]
16#[macro_export]
17#[doc(hidden)]
18macro_rules! __select_where {
19    ($ty:ty $(, $field:ident => $value:expr)* $(,)?) => {{
20        #[allow(unused_mut)]
21        let mut fields = ::smallvec::SmallVec::new();
22        $(
23            fields.push((
24                <$ty as $crate::foundations::Fields>::Enum::$field as u8,
25                $crate::foundations::IntoValue::into_value($value),
26            ));
27        )*
28        $crate::foundations::Selector::Elem(
29            <$ty as $crate::foundations::NativeElement>::elem(),
30            Some(fields),
31        )
32    }};
33}
34
35#[doc(inline)]
36pub use crate::__select_where as select_where;
37
38/// A filter for selecting elements within the document.
39///
40/// You can construct a selector in the following ways:
41/// - you can use an element [function]
42/// - you can filter for an element function with
43///   [specific fields]($function.where)
44/// - you can use a [string]($str) or [regular expression]($regex)
45/// - you can use a [`{<label>}`]($label)
46/// - you can use a [`location`]
47/// - call the [`selector`] constructor to convert any of the above types into a
48///   selector value and use the methods below to refine it
49///
50/// Selectors are used to [apply styling rules]($styling/#show-rules) to
51/// elements. You can also use selectors to [query] the document for certain
52/// types of elements.
53///
54/// Furthermore, you can pass a selector to several of Typst's built-in
55/// functions to configure their behaviour. One such example is the [outline]
56/// where it can be used to change which elements are listed within the outline.
57///
58/// Multiple selectors can be combined using the methods shown below. However,
59/// not all kinds of selectors are supported in all places, at the moment.
60///
61/// # Example
62/// ```example
63/// #context query(
64///   heading.where(level: 1)
65///     .or(heading.where(level: 2))
66/// )
67///
68/// = This will be found
69/// == So will this
70/// === But this will not.
71/// ```
72#[ty(scope, cast)]
73#[derive(Debug, Clone, PartialEq, Hash)]
74pub enum Selector {
75    /// Matches a specific type of element.
76    ///
77    /// If there is a dictionary, only elements with the fields from the
78    /// dictionary match.
79    Elem(Element, Option<SmallVec<[(u8, Value); 1]>>),
80    /// Matches the element at the specified location.
81    Location(Location),
82    /// Matches elements with a specific label.
83    Label(Label),
84    /// Matches text elements through a regular expression.
85    Regex(Regex),
86    /// Matches elements with a specific capability.
87    Can(TypeId),
88    /// Matches if any of the subselectors match.
89    Or(EcoVec<Self>),
90    /// Matches if all of the subselectors match.
91    And(EcoVec<Self>),
92    /// Matches all matches of `selector` before `end`.
93    Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool },
94    /// Matches all matches of `selector` after `start`.
95    After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool },
96}
97
98impl Selector {
99    /// Define a simple text selector.
100    pub fn text(text: &str) -> StrResult<Self> {
101        if text.is_empty() {
102            bail!("text selector is empty");
103        }
104        Ok(Self::Regex(Regex::new(&regex::escape(text)).unwrap()))
105    }
106
107    /// Define a regex selector.
108    pub fn regex(regex: Regex) -> StrResult<Self> {
109        if regex.as_str().is_empty() {
110            bail!("regex selector is empty");
111        }
112        if regex.is_match("") {
113            bail!("regex matches empty text");
114        }
115        Ok(Self::Regex(regex))
116    }
117
118    /// Define a simple [`Selector::Can`] selector.
119    pub fn can<T: ?Sized + Any>() -> Self {
120        Self::Can(TypeId::of::<T>())
121    }
122
123    /// Whether the selector matches for the target.
124    pub fn matches(&self, target: &Content, styles: Option<StyleChain>) -> bool {
125        match self {
126            Self::Elem(element, dict) => {
127                target.elem() == *element
128                    && dict.iter().flat_map(|dict| dict.iter()).all(|(id, value)| {
129                        target.get(*id, styles).as_ref().ok() == Some(value)
130                    })
131            }
132            Self::Label(label) => target.label() == Some(*label),
133            Self::Can(cap) => target.func().can_type_id(*cap),
134            Self::Or(selectors) => {
135                selectors.iter().any(move |sel| sel.matches(target, styles))
136            }
137            Self::And(selectors) => {
138                selectors.iter().all(move |sel| sel.matches(target, styles))
139            }
140            Self::Location(location) => target.location() == Some(*location),
141            // Not supported here.
142            Self::Regex(_) | Self::Before { .. } | Self::After { .. } => false,
143        }
144    }
145}
146
147#[scope]
148impl Selector {
149    /// Turns a value into a selector. The following values are accepted:
150    /// - An element function like a `heading` or `figure`.
151    /// - A `{<label>}`.
152    /// - A more complex selector like `{heading.where(level: 1)}`.
153    #[func(constructor)]
154    pub fn construct(
155        /// Can be an element function like a `heading` or `figure`, a `{<label>}`
156        /// or a more complex selector like `{heading.where(level: 1)}`.
157        target: Selector,
158    ) -> Selector {
159        target
160    }
161
162    /// Selects all elements that match this or any of the other selectors.
163    #[func]
164    pub fn or(
165        self,
166        /// The other selectors to match on.
167        #[variadic]
168        others: Vec<Selector>,
169    ) -> Selector {
170        Self::Or(others.into_iter().chain(Some(self)).collect())
171    }
172
173    /// Selects all elements that match this and all of the other selectors.
174    #[func]
175    pub fn and(
176        self,
177        /// The other selectors to match on.
178        #[variadic]
179        others: Vec<Selector>,
180    ) -> Selector {
181        Self::And(others.into_iter().chain(Some(self)).collect())
182    }
183
184    /// Returns a modified selector that will only match elements that occur
185    /// before the first match of `end`.
186    #[func]
187    pub fn before(
188        self,
189        /// The original selection will end at the first match of `end`.
190        end: LocatableSelector,
191        /// Whether `end` itself should match or not. This is only relevant if
192        /// both selectors match the same type of element. Defaults to `{true}`.
193        #[named]
194        #[default(true)]
195        inclusive: bool,
196    ) -> Selector {
197        Self::Before {
198            selector: Arc::new(self),
199            end: Arc::new(end.0),
200            inclusive,
201        }
202    }
203
204    /// Returns a modified selector that will only match elements that occur
205    /// after the first match of `start`.
206    #[func]
207    pub fn after(
208        self,
209        /// The original selection will start at the first match of `start`.
210        start: LocatableSelector,
211        ///  Whether `start` itself should match or not. This is only relevant
212        ///  if both selectors match the same type of element. Defaults to
213        ///  `{true}`.
214        #[named]
215        #[default(true)]
216        inclusive: bool,
217    ) -> Selector {
218        Self::After {
219            selector: Arc::new(self),
220            start: Arc::new(start.0),
221            inclusive,
222        }
223    }
224}
225
226impl From<Location> for Selector {
227    fn from(value: Location) -> Self {
228        Self::Location(value)
229    }
230}
231
232impl Repr for Selector {
233    fn repr(&self) -> EcoString {
234        match self {
235            Self::Elem(elem, dict) => {
236                if let Some(dict) = dict {
237                    let dict = dict
238                        .iter()
239                        .map(|(id, value)| (elem.field_name(*id).unwrap(), value.clone()))
240                        .map(|(name, value)| (EcoString::from(name).into(), value))
241                        .collect::<Dict>();
242                    eco_format!("{}.where{}", elem.name(), dict.repr())
243                } else {
244                    elem.name().into()
245                }
246            }
247            Self::Label(label) => label.repr(),
248            Self::Regex(regex) => regex.repr(),
249            Self::Can(cap) => eco_format!("{cap:?}"),
250            Self::Or(selectors) | Self::And(selectors) => {
251                let function = if matches!(self, Self::Or(_)) { "or" } else { "and" };
252                let pieces: Vec<_> = selectors.iter().map(Selector::repr).collect();
253                eco_format!("{}{}", function, repr::pretty_array_like(&pieces, false))
254            }
255            Self::Location(loc) => loc.repr(),
256            Self::Before { selector, end: split, inclusive }
257            | Self::After { selector, start: split, inclusive } => {
258                let method =
259                    if matches!(self, Self::Before { .. }) { "before" } else { "after" };
260                let inclusive_arg = if !*inclusive { ", inclusive: false" } else { "" };
261                eco_format!(
262                    "{}.{}({}{})",
263                    selector.repr(),
264                    method,
265                    split.repr(),
266                    inclusive_arg
267                )
268            }
269        }
270    }
271}
272
273cast! {
274    type Selector,
275    text: EcoString => Self::text(&text)?,
276    func: Func => func
277        .element()
278        .ok_or("only element functions can be used as selectors")?
279        .select(),
280    label: Label => Self::Label(label),
281    regex: Regex => Self::regex(regex)?,
282    location: Location => Self::Location(location),
283}
284
285/// A selector that can be used with `query`.
286///
287/// Hopefully, this is made obsolete by a more powerful query mechanism in the
288/// future.
289#[derive(Debug, Clone, PartialEq, Hash)]
290pub struct LocatableSelector(pub Selector);
291
292impl LocatableSelector {
293    /// Resolve this selector into a location that is guaranteed to be unique.
294    pub fn resolve_unique(
295        &self,
296        introspector: Tracked<Introspector>,
297        context: Tracked<Context>,
298    ) -> HintedStrResult<Location> {
299        match &self.0 {
300            Selector::Location(loc) => Ok(*loc),
301            other => {
302                context.introspect()?;
303                Ok(introspector.query_unique(other).map(|c| c.location().unwrap())?)
304            }
305        }
306    }
307}
308
309impl Reflect for LocatableSelector {
310    fn input() -> CastInfo {
311        CastInfo::Union(vec![
312            CastInfo::Type(Type::of::<Label>()),
313            CastInfo::Type(Type::of::<Func>()),
314            CastInfo::Type(Type::of::<Location>()),
315            CastInfo::Type(Type::of::<Selector>()),
316        ])
317    }
318
319    fn output() -> CastInfo {
320        CastInfo::Type(Type::of::<Selector>())
321    }
322
323    fn castable(value: &Value) -> bool {
324        Label::castable(value)
325            || Func::castable(value)
326            || Location::castable(value)
327            || Selector::castable(value)
328    }
329}
330
331cast! {
332    LocatableSelector,
333    self => self.0.into_value(),
334}
335
336impl FromValue for LocatableSelector {
337    fn from_value(value: Value) -> HintedStrResult<Self> {
338        fn validate(selector: &Selector) -> StrResult<()> {
339            match selector {
340                Selector::Elem(elem, _) => {
341                    if !elem.can::<dyn Locatable>() || elem.can::<dyn Unqueriable>() {
342                        Err(eco_format!("{} is not locatable", elem.name()))?
343                    }
344                }
345                Selector::Location(_) => {}
346                Selector::Label(_) => {}
347                Selector::Regex(_) => bail!("text is not locatable"),
348                Selector::Can(_) => bail!("capability is not locatable"),
349                Selector::Or(list) | Selector::And(list) => {
350                    for selector in list {
351                        validate(selector)?;
352                    }
353                }
354                Selector::Before { selector, end: split, .. }
355                | Selector::After { selector, start: split, .. } => {
356                    for selector in [selector, split] {
357                        validate(selector)?;
358                    }
359                }
360            }
361            Ok(())
362        }
363
364        if !Self::castable(&value) {
365            return Err(Self::error(&value));
366        }
367
368        let selector = Selector::from_value(value)?;
369        validate(&selector)?;
370        Ok(Self(selector))
371    }
372}
373
374impl From<Location> for LocatableSelector {
375    fn from(loc: Location) -> Self {
376        Self(Selector::Location(loc))
377    }
378}
379
380/// A selector that can be used with show rules.
381///
382/// Hopefully, this is made obsolete by a more powerful showing mechanism in the
383/// future.
384#[derive(Clone, PartialEq, Hash)]
385pub struct ShowableSelector(pub Selector);
386
387impl Reflect for ShowableSelector {
388    fn input() -> CastInfo {
389        CastInfo::Union(vec![
390            CastInfo::Type(Type::of::<Symbol>()),
391            CastInfo::Type(Type::of::<Str>()),
392            CastInfo::Type(Type::of::<Label>()),
393            CastInfo::Type(Type::of::<Func>()),
394            CastInfo::Type(Type::of::<Regex>()),
395            CastInfo::Type(Type::of::<Selector>()),
396        ])
397    }
398
399    fn output() -> CastInfo {
400        CastInfo::Type(Type::of::<Selector>())
401    }
402
403    fn castable(value: &Value) -> bool {
404        Symbol::castable(value)
405            || Str::castable(value)
406            || Label::castable(value)
407            || Func::castable(value)
408            || Regex::castable(value)
409            || Selector::castable(value)
410    }
411}
412
413cast! {
414    ShowableSelector,
415    self => self.0.into_value(),
416}
417
418impl FromValue for ShowableSelector {
419    fn from_value(value: Value) -> HintedStrResult<Self> {
420        fn validate(selector: &Selector, nested: bool) -> HintedStrResult<()> {
421            match selector {
422                Selector::Elem(_, _) => {}
423                Selector::Label(_) => {}
424                Selector::Regex(_) if !nested => {}
425                Selector::Or(list) | Selector::And(list) => {
426                    for selector in list {
427                        validate(selector, true)?;
428                    }
429                }
430                Selector::Regex(_)
431                | Selector::Location(_)
432                | Selector::Can(_)
433                | Selector::Before { .. }
434                | Selector::After { .. } => {
435                    bail!("this selector cannot be used with show")
436                }
437            }
438            Ok(())
439        }
440
441        if !Self::castable(&value) {
442            return Err(Self::error(&value));
443        }
444
445        let selector = Selector::from_value(value)?;
446        validate(&selector, false)?;
447        Ok(Self(selector))
448    }
449}