mirror of
https://github.com/typst/typst
synced 2025-05-22 21:15:28 +08:00
Locatable selectors
This commit is contained in:
parent
23715e813e
commit
715f9fb0a5
@ -276,10 +276,11 @@ use crate::prelude::*;
|
|||||||
pub fn counter(
|
pub fn counter(
|
||||||
/// The key that identifies this counter.
|
/// The key that identifies this counter.
|
||||||
///
|
///
|
||||||
/// - If this is the [`page`]($func/page) function, counts through pages.
|
|
||||||
/// - If this is any other element function, counts through its elements.
|
|
||||||
/// - If it is a string, creates a custom counter that is only affected by
|
/// - If it is a string, creates a custom counter that is only affected by
|
||||||
/// manual updates.
|
/// manual updates,
|
||||||
|
/// - If this is a `{<label>}`, counts through all elements with that label,
|
||||||
|
/// - If this is an element function or selector, counts through its elements,
|
||||||
|
/// - If this is the [`page`]($func/page) function, counts through pages.
|
||||||
key: CounterKey,
|
key: CounterKey,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
Value::dynamic(Counter::new(key))
|
Value::dynamic(Counter::new(key))
|
||||||
@ -485,18 +486,14 @@ cast_from_value! {
|
|||||||
CounterKey,
|
CounterKey,
|
||||||
v: Str => Self::Str(v),
|
v: Str => Self::Str(v),
|
||||||
label: Label => Self::Selector(Selector::Label(label)),
|
label: Label => Self::Selector(Selector::Label(label)),
|
||||||
element: ElemFunc => {
|
v: ElemFunc => {
|
||||||
if element == PageElem::func() {
|
if v == PageElem::func() {
|
||||||
return Ok(Self::Page);
|
Self::Page
|
||||||
|
} else {
|
||||||
|
Self::Selector(LocatableSelector::cast(Value::from(v))?.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Content::new(element).can::<dyn Locatable>() {
|
|
||||||
Err(eco_format!("cannot count through {}s", element.name()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::Selector(Selector::Elem(element, None))
|
|
||||||
},
|
},
|
||||||
selector: Selector => Self::Selector(selector),
|
selector: LocatableSelector => Self::Selector(selector.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for CounterKey {
|
impl Debug for CounterKey {
|
||||||
|
@ -94,8 +94,8 @@ use crate::prelude::*;
|
|||||||
/// Returns: content
|
/// Returns: content
|
||||||
#[func]
|
#[func]
|
||||||
pub fn query(
|
pub fn query(
|
||||||
/// Can be an element function like a `heading` or `figure` or a
|
/// Can be an element function like a `heading` or `figure`, a `{<label>}`
|
||||||
/// `{<label>}`.
|
/// or a more complex selector like `{heading.where(level: 1)}`.
|
||||||
///
|
///
|
||||||
/// Currently, only a subset of element functions is supported. Aside from
|
/// Currently, only a subset of element functions is supported. Aside from
|
||||||
/// headings and figures, this includes equations, references and all
|
/// headings and figures, this includes equations, references and all
|
||||||
@ -104,7 +104,7 @@ pub fn query(
|
|||||||
/// have an explicit label attached to them. This limitation will be
|
/// have an explicit label attached to them. This limitation will be
|
||||||
/// resolved
|
/// resolved
|
||||||
/// in the future.
|
/// in the future.
|
||||||
target: Target,
|
target: LocatableSelector,
|
||||||
|
|
||||||
/// Can be any location. Why is it required then? As noted before, Typst has
|
/// Can be any location. Why is it required then? As noted before, Typst has
|
||||||
/// to evaluate parts of your code multiple times to determine the values of
|
/// to evaluate parts of your code multiple times to determine the values of
|
||||||
@ -151,18 +151,3 @@ pub fn query(
|
|||||||
};
|
};
|
||||||
elements.into()
|
elements.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A query target.
|
|
||||||
struct Target(Selector);
|
|
||||||
|
|
||||||
cast_from_value! {
|
|
||||||
Target,
|
|
||||||
label: Label => Self(Selector::Label(label)),
|
|
||||||
element: ElemFunc => {
|
|
||||||
if !Content::new(element).can::<dyn Locatable>() {
|
|
||||||
Err(eco_format!("cannot query for {}s", element.name()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self(Selector::Elem(element, None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -23,8 +23,9 @@ pub use typst::geom::*;
|
|||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::model::{
|
pub use typst::model::{
|
||||||
element, Behave, Behaviour, Construct, Content, ElemFunc, Element, Finalize, Fold,
|
element, Behave, Behaviour, Construct, Content, ElemFunc, Element, Finalize, Fold,
|
||||||
Introspector, Label, Locatable, Location, MetaElem, Resolve, Selector, Set, Show,
|
Introspector, Label, Locatable, LocatableSelector, Location, MetaElem, Resolve,
|
||||||
StabilityProvider, StyleChain, StyleVec, Styles, Synthesize, Unlabellable, Vt,
|
Selector, Set, Show, StabilityProvider, StyleChain, StyleVec, Styles, Synthesize,
|
||||||
|
Unlabellable, Vt,
|
||||||
};
|
};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::syntax::{Span, Spanned};
|
pub use typst::syntax::{Span, Spanned};
|
||||||
|
@ -237,7 +237,7 @@ impl<T> Variadics for Vec<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Describes a possible value for a cast.
|
/// Describes a possible value for a cast.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd)]
|
||||||
pub enum CastInfo {
|
pub enum CastInfo {
|
||||||
/// Any value is okay.
|
/// Any value is okay.
|
||||||
Any,
|
Any,
|
||||||
@ -302,15 +302,23 @@ impl Add for CastInfo {
|
|||||||
fn add(self, rhs: Self) -> Self {
|
fn add(self, rhs: Self) -> Self {
|
||||||
Self::Union(match (self, rhs) {
|
Self::Union(match (self, rhs) {
|
||||||
(Self::Union(mut lhs), Self::Union(rhs)) => {
|
(Self::Union(mut lhs), Self::Union(rhs)) => {
|
||||||
lhs.extend(rhs);
|
for cast in rhs {
|
||||||
|
if !lhs.contains(&cast) {
|
||||||
|
lhs.push(cast);
|
||||||
|
}
|
||||||
|
}
|
||||||
lhs
|
lhs
|
||||||
}
|
}
|
||||||
(Self::Union(mut lhs), rhs) => {
|
(Self::Union(mut lhs), rhs) => {
|
||||||
lhs.push(rhs);
|
if !lhs.contains(&rhs) {
|
||||||
|
lhs.push(rhs);
|
||||||
|
}
|
||||||
lhs
|
lhs
|
||||||
}
|
}
|
||||||
(lhs, Self::Union(mut rhs)) => {
|
(lhs, Self::Union(mut rhs)) => {
|
||||||
rhs.insert(0, lhs);
|
if !rhs.contains(&lhs) {
|
||||||
|
rhs.insert(0, lhs);
|
||||||
|
}
|
||||||
rhs
|
rhs
|
||||||
}
|
}
|
||||||
(lhs, rhs) => vec![lhs, rhs],
|
(lhs, rhs) => vec![lhs, rhs],
|
||||||
|
@ -6,8 +6,9 @@ use std::mem;
|
|||||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||||
|
|
||||||
use super::{Content, ElemFunc, Element, Label, Vt};
|
use super::{Content, ElemFunc, Element, Label, Vt};
|
||||||
use crate::diag::{SourceResult, Trace, Tracepoint};
|
use crate::diag::{SourceResult, StrResult, Trace, Tracepoint};
|
||||||
use crate::eval::{cast_from_value, Args, Cast, Dict, Func, Regex, Value, Vm};
|
use crate::eval::{cast_from_value, Args, Cast, CastInfo, Dict, Func, Regex, Value, Vm};
|
||||||
|
use crate::model::Locatable;
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::util::pretty_array_like;
|
use crate::util::pretty_array_like;
|
||||||
|
|
||||||
@ -337,6 +338,51 @@ cast_from_value! {
|
|||||||
regex: Regex => Self::Regex(regex),
|
regex: Regex => Self::Regex(regex),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A selector that can be used with `query`. Hopefully, this is made obsolote
|
||||||
|
/// by a more powerful query mechanism in the future.
|
||||||
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
|
pub struct LocatableSelector(pub Selector);
|
||||||
|
|
||||||
|
impl Cast for LocatableSelector {
|
||||||
|
fn is(value: &Value) -> bool {
|
||||||
|
matches!(value, Value::Label(_) | Value::Func(_))
|
||||||
|
|| value.type_name() == "selector"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(value: Value) -> StrResult<Self> {
|
||||||
|
fn validate(selector: &Selector) -> StrResult<()> {
|
||||||
|
match &selector {
|
||||||
|
Selector::Elem(elem, _) if !elem.can::<dyn Locatable>() => {
|
||||||
|
Err(eco_format!("{} is not locatable", elem.name()))?
|
||||||
|
}
|
||||||
|
Selector::Regex(_) => Err("text is not locatable")?,
|
||||||
|
Selector::Any(list) | Selector::All(list) => {
|
||||||
|
for selector in list {
|
||||||
|
validate(selector)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Self::is(&value) {
|
||||||
|
return <Self as Cast>::error(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let selector = Selector::cast(value)?;
|
||||||
|
validate(&selector)?;
|
||||||
|
Ok(Self(selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe() -> CastInfo {
|
||||||
|
CastInfo::Union(vec![
|
||||||
|
CastInfo::Type("label"),
|
||||||
|
CastInfo::Type("function"),
|
||||||
|
CastInfo::Type("selector"),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
/// A show rule transformation that can be applied to a match.
|
/// A show rule transformation that can be applied to a match.
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
pub enum Transform {
|
pub enum Transform {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user