mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Property resolving
This commit is contained in:
parent
4bb6240b40
commit
e1d7edb7c1
@ -153,10 +153,12 @@ fn process_const(
|
|||||||
// The type of the property's value is what the user of our macro wrote
|
// The type of the property's value is what the user of our macro wrote
|
||||||
// as type of the const ...
|
// as type of the const ...
|
||||||
let value_ty = &item.ty;
|
let value_ty = &item.ty;
|
||||||
let output_ty = if property.fold {
|
let output_ty = if property.referenced {
|
||||||
parse_quote!(<#value_ty as eval::Fold>::Output)
|
|
||||||
} else if property.referenced {
|
|
||||||
parse_quote!(&'a #value_ty)
|
parse_quote!(&'a #value_ty)
|
||||||
|
} else if property.fold {
|
||||||
|
parse_quote!(<#value_ty as eval::Fold>::Output)
|
||||||
|
} else if property.resolve {
|
||||||
|
parse_quote!(<#value_ty as eval::Resolve>::Output)
|
||||||
} else {
|
} else {
|
||||||
value_ty.clone()
|
value_ty.clone()
|
||||||
};
|
};
|
||||||
@ -191,10 +193,15 @@ fn process_const(
|
|||||||
} else if property.fold {
|
} else if property.fold {
|
||||||
get = quote! {
|
get = quote! {
|
||||||
match values.next().cloned() {
|
match values.next().cloned() {
|
||||||
Some(inner) => eval::Fold::fold(inner, Self::get(values)),
|
Some(inner) => eval::Fold::fold(inner, Self::get(chain, values)),
|
||||||
None => #default,
|
None => #default,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
} else if property.resolve {
|
||||||
|
get = quote! {
|
||||||
|
let value = values.next().cloned().unwrap_or(#default);
|
||||||
|
eval::Resolve::resolve(value, chain)
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
get = quote! {
|
get = quote! {
|
||||||
values.next().copied().unwrap_or(#default)
|
values.next().copied().unwrap_or(#default)
|
||||||
@ -227,13 +234,17 @@ fn process_const(
|
|||||||
impl<'a, #params> eval::Key<'a> for #key {
|
impl<'a, #params> eval::Key<'a> for #key {
|
||||||
type Value = #value_ty;
|
type Value = #value_ty;
|
||||||
type Output = #output_ty;
|
type Output = #output_ty;
|
||||||
|
|
||||||
const NAME: &'static str = #name;
|
const NAME: &'static str = #name;
|
||||||
|
|
||||||
fn node() -> TypeId {
|
fn node() -> TypeId {
|
||||||
TypeId::of::<#self_ty>()
|
TypeId::of::<#self_ty>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(mut values: impl Iterator<Item = &'a Self::Value>) -> Self::Output {
|
fn get(
|
||||||
|
chain: StyleChain<'a>,
|
||||||
|
mut values: impl Iterator<Item = &'a Self::Value>,
|
||||||
|
) -> Self::Output {
|
||||||
#get
|
#get
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,6 +268,7 @@ struct Property {
|
|||||||
shorthand: bool,
|
shorthand: bool,
|
||||||
variadic: bool,
|
variadic: bool,
|
||||||
fold: bool,
|
fold: bool,
|
||||||
|
resolve: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a style property attribute.
|
/// Parse a style property attribute.
|
||||||
@ -268,6 +280,7 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
|||||||
shorthand: false,
|
shorthand: false,
|
||||||
variadic: false,
|
variadic: false,
|
||||||
fold: false,
|
fold: false,
|
||||||
|
resolve: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(idx) = item
|
if let Some(idx) = item
|
||||||
@ -284,6 +297,7 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
|||||||
"referenced" => property.referenced = true,
|
"referenced" => property.referenced = true,
|
||||||
"variadic" => property.variadic = true,
|
"variadic" => property.variadic = true,
|
||||||
"fold" => property.fold = true,
|
"fold" => property.fold = true,
|
||||||
|
"resolve" => property.resolve = true,
|
||||||
_ => return Err(Error::new(ident.span(), "invalid attribute")),
|
_ => return Err(Error::new(ident.span(), "invalid attribute")),
|
||||||
},
|
},
|
||||||
TokenTree::Punct(_) => {}
|
TokenTree::Punct(_) => {}
|
||||||
@ -300,10 +314,10 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if property.referenced && property.fold {
|
if property.referenced as u8 + property.fold as u8 + property.resolve as u8 > 1 {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
span,
|
span,
|
||||||
"referenced and fold are mutually exclusive",
|
"referenced, fold and resolve are mutually exclusive",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@ use std::hash::Hash;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Args, Content, Func, Layout, Node, Span, Value};
|
use super::{Args, Content, Func, Layout, Node, Smart, Span, Value};
|
||||||
use crate::diag::{At, TypResult};
|
use crate::diag::{At, TypResult};
|
||||||
|
use crate::geom::{Numeric, Relative, Sides, Spec};
|
||||||
use crate::library::layout::PageNode;
|
use crate::library::layout::PageNode;
|
||||||
use crate::library::text::{FontFamily, ParNode, TextNode};
|
use crate::library::text::{FontFamily, ParNode, TextNode};
|
||||||
use crate::util::Prehashed;
|
use crate::util::Prehashed;
|
||||||
@ -280,7 +281,10 @@ pub trait Key<'a>: 'static {
|
|||||||
|
|
||||||
/// Compute an output value from a sequence of values belong to this key,
|
/// Compute an output value from a sequence of values belong to this key,
|
||||||
/// folding if necessary.
|
/// folding if necessary.
|
||||||
fn get(values: impl Iterator<Item = &'a Self::Value>) -> Self::Output;
|
fn get(
|
||||||
|
chain: StyleChain<'a>,
|
||||||
|
values: impl Iterator<Item = &'a Self::Value>,
|
||||||
|
) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A property that is folded to determine its final value.
|
/// A property that is folded to determine its final value.
|
||||||
@ -292,6 +296,64 @@ pub trait Fold {
|
|||||||
fn fold(self, outer: Self::Output) -> Self::Output;
|
fn fold(self, outer: Self::Output) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A property that is resolved with other properties from the style chain.
|
||||||
|
pub trait Resolve {
|
||||||
|
/// The type of the resolved output.
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
/// Resolve the value using the style chain.
|
||||||
|
fn resolve(self, styles: StyleChain) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resolve> Resolve for Option<T> {
|
||||||
|
type Output = Option<T::Output>;
|
||||||
|
|
||||||
|
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||||
|
self.map(|v| v.resolve(styles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resolve> Resolve for Smart<T> {
|
||||||
|
type Output = Smart<T::Output>;
|
||||||
|
|
||||||
|
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||||
|
self.map(|v| v.resolve(styles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resolve> Resolve for Spec<T> {
|
||||||
|
type Output = Spec<T::Output>;
|
||||||
|
|
||||||
|
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||||
|
self.map(|v| v.resolve(styles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Resolve> Resolve for Sides<T> {
|
||||||
|
type Output = Sides<T::Output>;
|
||||||
|
|
||||||
|
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||||
|
Sides {
|
||||||
|
left: self.left.resolve(styles),
|
||||||
|
right: self.right.resolve(styles),
|
||||||
|
top: self.top.resolve(styles),
|
||||||
|
bottom: self.bottom.resolve(styles),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Resolve for Relative<T>
|
||||||
|
where
|
||||||
|
T: Resolve + Numeric,
|
||||||
|
<T as Resolve>::Output: Numeric,
|
||||||
|
{
|
||||||
|
type Output = Relative<<T as Resolve>::Output>;
|
||||||
|
|
||||||
|
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||||
|
self.map(|abs| abs.resolve(styles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A show rule recipe.
|
/// A show rule recipe.
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
struct Recipe {
|
struct Recipe {
|
||||||
@ -410,10 +472,10 @@ impl<'a> StyleChain<'a> {
|
|||||||
/// Get the output value of a style property.
|
/// Get the output value of a style property.
|
||||||
///
|
///
|
||||||
/// Returns the property's default value if no map in the chain contains an
|
/// Returns the property's default value if no map in the chain contains an
|
||||||
/// entry for it. Also takes care of folding and returns references where
|
/// entry for it. Also takes care of folding and resolving and returns
|
||||||
/// applicable.
|
/// references where applicable.
|
||||||
pub fn get<K: Key<'a>>(self, key: K) -> K::Output {
|
pub fn get<K: Key<'a>>(self, key: K) -> K::Output {
|
||||||
K::get(self.values(key))
|
K::get(self, self.values(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute and return the result of a user recipe for a node if there is
|
/// Execute and return the result of a user recipe for a node if there is
|
||||||
|
@ -590,6 +590,19 @@ impl<T: Cast> Cast<Spanned<Value>> for Spanned<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Cast> Cast for Option<T> {
|
||||||
|
fn is(value: &Value) -> bool {
|
||||||
|
matches!(value, Value::None) || T::is(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cast(value: Value) -> StrResult<Self> {
|
||||||
|
match value {
|
||||||
|
Value::None => Ok(None),
|
||||||
|
v => T::cast(v).map(Some).map_err(|msg| with_alternative(msg, "none")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A value that can be automatically determined.
|
/// A value that can be automatically determined.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub enum Smart<T> {
|
pub enum Smart<T> {
|
||||||
@ -601,6 +614,17 @@ pub enum Smart<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Smart<T> {
|
impl<T> Smart<T> {
|
||||||
|
/// Map the contained custom value with `f`.
|
||||||
|
pub fn map<F, U>(self, f: F) -> Smart<U>
|
||||||
|
where
|
||||||
|
F: FnOnce(T) -> U,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::Auto => Smart::Auto,
|
||||||
|
Self::Custom(x) => Smart::Custom(f(x)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the contained custom value or a provided default value.
|
/// Returns the contained custom value or a provided default value.
|
||||||
pub fn unwrap_or(self, default: T) -> T {
|
pub fn unwrap_or(self, default: T) -> T {
|
||||||
match self {
|
match self {
|
||||||
@ -627,19 +651,6 @@ impl<T> Default for Smart<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Cast> Cast for Option<T> {
|
|
||||||
fn is(value: &Value) -> bool {
|
|
||||||
matches!(value, Value::None) || T::is(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cast(value: Value) -> StrResult<Self> {
|
|
||||||
match value {
|
|
||||||
Value::None => Ok(None),
|
|
||||||
v => T::cast(v).map(Some).map_err(|msg| with_alternative(msg, "none")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Cast> Cast for Smart<T> {
|
impl<T: Cast> Cast for Smart<T> {
|
||||||
fn is(value: &Value) -> bool {
|
fn is(value: &Value) -> bool {
|
||||||
matches!(value, Value::Auto) || T::is(value)
|
matches!(value, Value::Auto) || T::is(value)
|
||||||
|
@ -34,6 +34,15 @@ impl<T: Numeric> Relative<T> {
|
|||||||
pub fn resolve(self, whole: T) -> T {
|
pub fn resolve(self, whole: T) -> T {
|
||||||
self.rel.resolve(whole) + self.abs
|
self.rel.resolve(whole) + self.abs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Map the absolute part with `f`.
|
||||||
|
pub fn map<F, U>(self, f: F) -> Relative<U>
|
||||||
|
where
|
||||||
|
F: FnOnce(T) -> U,
|
||||||
|
U: Numeric,
|
||||||
|
{
|
||||||
|
Relative { rel: self.rel, abs: f(self.abs) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Numeric> Debug for Relative<T> {
|
impl<T: Numeric> Debug for Relative<T> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user