typst_library/foundations/
selector.rs1use std::any::{Any, TypeId};
2use std::sync::Arc;
3
4use comemo::Tracked;
5use ecow::{EcoString, EcoVec, eco_format};
6use smallvec::SmallVec;
7use typst_syntax::Span;
8
9use crate::diag::{At, HintedStrResult, SourceResult, StrResult, bail};
10use crate::engine::Engine;
11use crate::foundations::{
12 CastInfo, Content, Context, Dict, Element, FromValue, Func, Label, Reflect, Regex,
13 Repr, Str, StyleChain, Symbol, Type, Value, cast, func, repr, scope, ty,
14};
15use crate::introspection::{Locatable, Location, QueryUniqueIntrospection, Unqueriable};
16
17#[macro_export]
19#[doc(hidden)]
20macro_rules! __select_where {
21 ($ty:ty $(, $field:ident => $value:expr)* $(,)?) => {{
22 #[allow(unused_mut)]
23 let mut fields = $crate::foundations::SmallVec::new();
24 $(
25 fields.push((
26 <$ty>::$field.index(),
27 $crate::foundations::IntoValue::into_value($value),
28 ));
29 )*
30 $crate::foundations::Selector::Elem(
31 <$ty as $crate::foundations::NativeElement>::ELEM,
32 Some(fields),
33 )
34 }};
35}
36
37#[doc(inline)]
38pub use crate::__select_where as select_where;
39
40#[ty(scope, cast)]
75#[derive(Debug, Clone, PartialEq, Hash)]
76pub enum Selector {
77 Elem(Element, Option<SmallVec<[(u8, Value); 1]>>),
82 Location(Location),
84 Label(Label),
86 Regex(Regex),
88 Can(TypeId),
92 Or(EcoVec<Self>),
94 And(EcoVec<Self>),
96 Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool },
98 After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool },
100 Within { selector: Arc<Self>, ancestor: Arc<Self> },
104}
105
106impl Selector {
107 pub fn text(text: &str) -> StrResult<Self> {
109 if text.is_empty() {
110 bail!("text selector is empty");
111 }
112 Ok(Self::Regex(Regex::new(®ex::escape(text)).unwrap()))
113 }
114
115 pub fn regex(regex: Regex) -> StrResult<Self> {
117 if regex.as_str().is_empty() {
118 bail!("regex selector is empty");
119 }
120 if regex.is_match("") {
121 bail!("regex matches empty text");
122 }
123 Ok(Self::Regex(regex))
124 }
125
126 pub fn can<T: ?Sized + Any>() -> Self {
128 Self::Can(TypeId::of::<T>())
129 }
130
131 pub fn matches(&self, target: &Content, styles: Option<StyleChain>) -> bool {
133 match self {
134 Self::Elem(element, dict) => {
135 target.elem() == *element
136 && dict.iter().flat_map(|dict| dict.iter()).all(|(id, value)| {
137 target.get(*id, styles).as_ref().ok() == Some(value)
138 })
139 }
140 Self::Label(label) => target.label() == Some(*label),
141 Self::Can(cap) => target.func().can_type_id(*cap),
142 Self::Or(selectors) => {
143 selectors.iter().any(move |sel| sel.matches(target, styles))
144 }
145 Self::And(selectors) => {
146 selectors.iter().all(move |sel| sel.matches(target, styles))
147 }
148 Self::Location(location) => target.location() == Some(*location),
149 Self::Regex(_)
151 | Self::Before { .. }
152 | Self::After { .. }
153 | Self::Within { .. } => false,
154 }
155 }
156}
157
158#[scope]
159impl Selector {
160 #[func(constructor)]
167 pub fn construct(
168 target: Selector,
172 ) -> Selector {
173 target
174 }
175
176 #[func]
178 pub fn or(
179 self,
180 #[variadic]
182 others: Vec<Selector>,
183 ) -> Selector {
184 Self::Or(others.into_iter().chain(Some(self)).collect())
185 }
186
187 #[func]
189 pub fn and(
190 self,
191 #[variadic]
193 others: Vec<Selector>,
194 ) -> Selector {
195 Self::And(others.into_iter().chain(Some(self)).collect())
196 }
197
198 #[func]
204 pub fn before(
205 self,
206 end: LocatableSelector,
208 #[named]
211 #[default(true)]
212 inclusive: bool,
213 ) -> Selector {
214 Self::Before {
215 selector: Arc::new(self),
216 end: Arc::new(end.0),
217 inclusive,
218 }
219 }
220
221 #[func]
227 pub fn after(
228 self,
229 start: LocatableSelector,
231 #[named]
234 #[default(true)]
235 inclusive: bool,
236 ) -> Selector {
237 Self::After {
238 selector: Arc::new(self),
239 start: Arc::new(start.0),
240 inclusive,
241 }
242 }
243
244 #[func]
285 pub fn within(
286 self,
287 ancestor: LocatableSelector,
292 ) -> Selector {
293 Selector::Within {
294 selector: Arc::new(self),
295 ancestor: Arc::new(ancestor.0),
296 }
297 }
298}
299
300impl From<Location> for Selector {
301 fn from(value: Location) -> Self {
302 Self::Location(value)
303 }
304}
305
306impl Repr for Selector {
307 fn repr(&self) -> EcoString {
308 match self {
309 Self::Elem(elem, dict) => {
310 if let Some(dict) = dict {
311 let dict = dict
312 .iter()
313 .map(|(id, value)| (elem.field_name(*id).unwrap(), value.clone()))
314 .map(|(name, value)| (EcoString::from(name).into(), value))
315 .collect::<Dict>();
316 eco_format!("{}.where{}", elem.name(), dict.repr())
317 } else {
318 elem.name().into()
319 }
320 }
321 Self::Label(label) => label.repr(),
322 Self::Regex(regex) => regex.repr(),
323 Self::Can(_) => eco_format!("selector(..)"),
324 Self::Or(selectors) | Self::And(selectors) => {
325 let function = if matches!(self, Self::Or(_)) { "or" } else { "and" };
326 let pieces: Vec<_> = selectors.iter().map(Selector::repr).collect();
327 eco_format!(
328 "selector.{}{}",
329 function,
330 repr::pretty_array_like(&pieces, false)
331 )
332 }
333 Self::Location(loc) => loc.repr(),
334 Self::Before { selector, end: split, inclusive }
335 | Self::After { selector, start: split, inclusive } => {
336 let method =
337 if matches!(self, Self::Before { .. }) { "before" } else { "after" };
338 let inclusive_arg = if !*inclusive { ", inclusive: false" } else { "" };
339 eco_format!(
340 "{}.{}({}{})",
341 selector.repr(),
342 method,
343 split.repr(),
344 inclusive_arg
345 )
346 }
347 Self::Within { selector, ancestor } => {
348 eco_format!("{}.within({})", selector.repr(), ancestor.repr(),)
349 }
350 }
351 }
352}
353
354cast! {
355 type Selector,
356 text: EcoString => Self::text(&text)?,
357 func: Func => func
358 .to_element()
359 .ok_or("only element functions can be used as selectors")?
360 .select(),
361 label: Label => Self::Label(label),
362 regex: Regex => Self::regex(regex)?,
363 location: Location => Self::Location(location),
364}
365
366#[derive(Debug, Clone, PartialEq, Hash)]
371pub struct LocatableSelector(pub Selector);
372
373impl LocatableSelector {
374 pub fn resolve_unique(
376 &self,
377 engine: &mut Engine,
378 context: Tracked<Context>,
379 span: Span,
380 ) -> SourceResult<Location> {
381 match self.0.clone() {
382 Selector::Location(loc) => Ok(loc),
383 other => {
384 context.introspect().at(span)?;
385 engine
386 .introspect(QueryUniqueIntrospection(other, span))
387 .map(|c| c.location().unwrap())
388 .at(span)
389 }
390 }
391 }
392}
393
394impl Reflect for LocatableSelector {
395 fn input() -> CastInfo {
396 CastInfo::Union(vec![
397 CastInfo::Type(Type::of::<Label>()),
398 CastInfo::Type(Type::of::<Func>()),
399 CastInfo::Type(Type::of::<Location>()),
400 CastInfo::Type(Type::of::<Selector>()),
401 ])
402 }
403
404 fn output() -> CastInfo {
405 CastInfo::Type(Type::of::<Selector>())
406 }
407
408 fn castable(value: &Value) -> bool {
409 Label::castable(value)
410 || Func::castable(value)
411 || Location::castable(value)
412 || Selector::castable(value)
413 }
414}
415
416cast! {
417 LocatableSelector,
418 self => self.0.into_value(),
419}
420
421impl FromValue for LocatableSelector {
422 fn from_value(value: Value) -> HintedStrResult<Self> {
423 fn validate(selector: &Selector) -> StrResult<()> {
424 match selector {
425 Selector::Elem(elem, _) => {
426 if !elem.can::<dyn Locatable>() || elem.can::<dyn Unqueriable>() {
427 Err(eco_format!("{} is not locatable", elem.name()))?
428 }
429 }
430 Selector::Location(_) => {}
431 Selector::Label(_) => {}
432 Selector::Regex(_) => bail!("text is not locatable"),
433 Selector::Can(_) => bail!("capability is not locatable"),
434 Selector::Or(list) | Selector::And(list) => {
435 for selector in list {
436 validate(selector)?;
437 }
438 }
439 Selector::Before { selector, end: split, .. }
440 | Selector::After { selector, start: split, .. } => {
441 for selector in [selector, split] {
442 validate(selector)?;
443 }
444 }
445 Selector::Within { selector, ancestor } => {
446 for selector in [selector, ancestor] {
447 validate(selector)?;
448 }
449 }
450 }
451 Ok(())
452 }
453
454 if !Self::castable(&value) {
455 return Err(Self::error(&value));
456 }
457
458 let selector = Selector::from_value(value)?;
459 validate(&selector)?;
460 Ok(Self(selector))
461 }
462}
463
464impl From<Location> for LocatableSelector {
465 fn from(loc: Location) -> Self {
466 Self(Selector::Location(loc))
467 }
468}
469
470#[derive(Clone, PartialEq, Hash)]
475pub struct ShowableSelector(pub Selector);
476
477impl Reflect for ShowableSelector {
478 fn input() -> CastInfo {
479 CastInfo::Union(vec![
480 CastInfo::Type(Type::of::<Symbol>()),
481 CastInfo::Type(Type::of::<Str>()),
482 CastInfo::Type(Type::of::<Label>()),
483 CastInfo::Type(Type::of::<Func>()),
484 CastInfo::Type(Type::of::<Regex>()),
485 CastInfo::Type(Type::of::<Selector>()),
486 ])
487 }
488
489 fn output() -> CastInfo {
490 CastInfo::Type(Type::of::<Selector>())
491 }
492
493 fn castable(value: &Value) -> bool {
494 Symbol::castable(value)
495 || Str::castable(value)
496 || Label::castable(value)
497 || Func::castable(value)
498 || Regex::castable(value)
499 || Selector::castable(value)
500 }
501}
502
503cast! {
504 ShowableSelector,
505 self => self.0.into_value(),
506}
507
508impl FromValue for ShowableSelector {
509 fn from_value(value: Value) -> HintedStrResult<Self> {
510 fn validate(selector: &Selector, nested: bool) -> HintedStrResult<()> {
511 match selector {
512 Selector::Elem(_, _) => {}
513 Selector::Label(_) => {}
514 Selector::Regex(_) if !nested => {}
515 Selector::Or(list) | Selector::And(list) => {
516 for selector in list {
517 validate(selector, true)?;
518 }
519 }
520 Selector::Regex(_)
521 | Selector::Location(_)
522 | Selector::Can(_)
523 | Selector::Before { .. }
524 | Selector::After { .. } => {
525 bail!("this selector cannot be used with show")
526 }
527 Selector::Within { .. } => {
528 bail!(
529 "this selector cannot currently be used with show";
530 hint: "support for this is planned for the future";
531 )
532 }
533 }
534 Ok(())
535 }
536
537 if !Self::castable(&value) {
538 return Err(Self::error(&value));
539 }
540
541 let selector = Selector::from_value(value)?;
542 validate(&selector, false)?;
543 Ok(Self(selector))
544 }
545}