typst_library/foundations/
selector.rs

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