diff --git a/macros/src/lib.rs b/macros/src/lib.rs index ab9fbf1b3..774770a3b 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -4,6 +4,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::parse_quote; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{Error, Ident, Result}; @@ -118,7 +119,9 @@ struct Property { } /// Parse the name and generic type arguments of the node type. -fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> { +fn parse_self( + self_ty: &syn::Type, +) -> Result<(String, Punctuated)> { // Extract the node type for which we want to generate properties. let path = match self_ty { syn::Type::Path(path) => path, @@ -129,15 +132,8 @@ fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> { let last = path.path.segments.last().unwrap(); let self_name = last.ident.to_string(); let self_args = match &last.arguments { - syn::PathArguments::AngleBracketed(args) => args - .args - .iter() - .filter_map(|arg| match arg { - syn::GenericArgument::Type(ty) => Some(ty), - _ => None, - }) - .collect(), - _ => vec![], + syn::PathArguments::AngleBracketed(args) => args.args.clone(), + _ => Punctuated::new(), }; Ok((self_name, self_args)) @@ -146,29 +142,35 @@ fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> { /// Process a single const item. fn process_const( item: &mut syn::ImplItemConst, - params: &syn::punctuated::Punctuated, + params: &Punctuated, self_ty: &syn::Type, self_name: &str, - self_args: &[&syn::Type], + self_args: &Punctuated, ) -> Result<(Property, syn::ItemMod)> { - // The module that will contain the `Key` type. - let module_name = &item.ident; + // The display name, e.g. `TextNode::STRONG`. + let name = format!("{}::{}", self_name, &item.ident); // The type of the property's value is what the user of our macro wrote // as type of the const ... let value_ty = &item.ty; - // ... but the real type of the const becomes Key<#key_args>. - let key_args = quote! { #value_ty #(, #self_args)* }; - - // The display name, e.g. `TextNode::STRONG`. - let name = format!("{}::{}", self_name, &item.ident); + // ... but the real type of the const becomes this.. + let key = quote! { Key<#value_ty, #self_args> }; + let phantom_args = self_args.iter().filter(|arg| match arg { + syn::GenericArgument::Type(syn::Type::Path(path)) => { + params.iter().all(|param| match param { + syn::GenericParam::Const(c) => !path.path.is_ident(&c.ident), + _ => true, + }) + } + _ => true, + }); // The default value of the property is what the user wrote as // initialization value of the const. let default = &item.expr; - let mut folder = None; + let mut fold = None; let mut property = Property { name: item.ident.clone(), shorthand: false, @@ -177,25 +179,23 @@ fn process_const( }; for attr in std::mem::take(&mut item.attrs) { - if attr.path.is_ident("fold") { - // Look for a folding function like `#[fold(u64::add)]`. - let func: syn::Expr = attr.parse_args()?; - folder = Some(quote! { - const FOLDING: bool = true; + match attr.path.get_ident().map(ToString::to_string).as_deref() { + Some("fold") => { + // Look for a folding function like `#[fold(u64::add)]`. + let func: syn::Expr = attr.parse_args()?; + fold = Some(quote! { + const FOLDING: bool = true; - fn fold(inner: Self::Value, outer: Self::Value) -> Self::Value { - let f: fn(Self::Value, Self::Value) -> Self::Value = #func; - f(inner, outer) - } - }); - } else if attr.path.is_ident("shorthand") { - property.shorthand = true; - } else if attr.path.is_ident("variadic") { - property.variadic = true; - } else if attr.path.is_ident("skip") { - property.skip = true; - } else { - item.attrs.push(attr); + fn fold(inner: Self::Value, outer: Self::Value) -> Self::Value { + let f: fn(Self::Value, Self::Value) -> Self::Value = #func; + f(inner, outer) + } + }); + } + Some("shorthand") => property.shorthand = true, + Some("variadic") => property.variadic = true, + Some("skip") => property.skip = true, + _ => item.attrs.push(attr), } } @@ -206,28 +206,27 @@ fn process_const( )); } - let nonfolding = folder.is_none().then(|| { - quote! { - impl<#params> Nonfolding for Key<#key_args> {} - } + let nonfolding = fold.is_none().then(|| { + quote! { impl<#params> Nonfolding for #key {} } }); // Generate the module code. + let module_name = &item.ident; let module = parse_quote! { #[allow(non_snake_case)] mod #module_name { use super::*; - pub struct Key(pub PhantomData<(VALUE, #key_args)>); + pub struct Key(pub PhantomData<(VALUE, #(#phantom_args,)*)>); - impl<#params> Copy for Key<#key_args> {} - impl<#params> Clone for Key<#key_args> { + impl<#params> Copy for #key {} + impl<#params> Clone for #key { fn clone(&self) -> Self { *self } } - impl<#params> Property for Key<#key_args> { + impl<#params> Property for #key { type Value = #value_ty; const NAME: &'static str = #name; @@ -245,7 +244,7 @@ fn process_const( &*LAZY } - #folder + #fold } #nonfolding @@ -253,8 +252,7 @@ fn process_const( }; // Replace type and initializer expression with the `Key`. - item.attrs.retain(|attr| !attr.path.is_ident("fold")); - item.ty = parse_quote! { #module_name::Key<#key_args> }; + item.ty = parse_quote! { #module_name::#key }; item.expr = parse_quote! { #module_name::Key(PhantomData) }; Ok((property, module)) diff --git a/src/diag.rs b/src/diag.rs index bd91df267..70a5bdaf8 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -7,7 +7,7 @@ use crate::syntax::{Span, Spanned}; /// Early-return with a vec-boxed [`Error`]. macro_rules! bail { ($span:expr, $message:expr $(,)?) => { - return Err($crate::diag::Error::boxed($span, $message,)) + return Err($crate::diag::Error::boxed($span, $message)) }; ($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => { diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 5a67555c0..9ad23538b 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -41,7 +41,7 @@ use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; use crate::geom::{Angle, Fractional, Length, Relative}; use crate::image::ImageStore; use crate::layout::Layout; -use crate::library::{self}; +use crate::library::{self, ORDERED, UNORDERED}; use crate::loading::Loader; use crate::source::{SourceId, SourceStore}; use crate::syntax::ast::*; @@ -272,9 +272,9 @@ impl Eval for ListNode { type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - Ok(Template::show(library::ListNode { + Ok(Template::show(library::ListNode:: { + number: None, child: self.body().eval(ctx)?.pack(), - label: library::Unordered, })) } } @@ -283,9 +283,9 @@ impl Eval for EnumNode { type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - Ok(Template::show(library::ListNode { + Ok(Template::show(library::ListNode:: { + number: self.number(), child: self.body().eval(ctx)?.pack(), - label: library::Ordered(self.number()), })) } } diff --git a/src/eval/template.rs b/src/eval/template.rs index 84888b953..f953287d4 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -14,7 +14,7 @@ use crate::layout::{Layout, LayoutNode}; use crate::library::prelude::*; use crate::library::{ DecoNode, FlowChild, FlowNode, PageNode, ParChild, ParNode, PlaceNode, SpacingKind, - TextNode, Underline, + TextNode, UNDERLINE, }; use crate::util::EcoString; use crate::Context; @@ -145,7 +145,7 @@ impl Template { /// Underline this template. pub fn underlined(self) -> Self { - Self::show(DecoNode { kind: Underline, body: self }) + Self::show(DecoNode::(self)) } /// Create a new sequence template. diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 7755f84c0..c334433e9 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -18,9 +18,9 @@ use std::sync::Arc; use crate::eval::StyleChain; use crate::font::FontStore; use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; -use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec}; +use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform}; use crate::image::ImageStore; -use crate::library::{AlignNode, Move, PadNode, TransformNode}; +use crate::library::{AlignNode, PadNode, TransformNode, MOVE}; use crate::util::Prehashed; use crate::Context; @@ -150,8 +150,8 @@ impl LayoutNode { /// Transform this node's contents without affecting layout. pub fn moved(self, offset: Point) -> Self { if !offset.is_zero() { - TransformNode { - kind: Move(offset.x, offset.y), + TransformNode:: { + transform: Transform::translation(offset.x, offset.y), child: self, } .pack() diff --git a/src/library/deco.rs b/src/library/deco.rs index 40ef4e73e..5e438d54a 100644 --- a/src/library/deco.rs +++ b/src/library/deco.rs @@ -5,15 +5,10 @@ use super::TextNode; /// Typeset underline, striken-through or overlined text. #[derive(Debug, Hash)] -pub struct DecoNode { - /// The kind of line. - pub kind: L, - /// The decorated contents. - pub body: Template, -} +pub struct DecoNode(pub Template); #[class] -impl DecoNode { +impl DecoNode { /// Stroke color of the line, defaults to the text color if `None`. #[shorthand] pub const STROKE: Option = None; @@ -32,17 +27,14 @@ impl DecoNode { pub const EVADE: bool = true; fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult