From e089b6ea40015e012302dc55ac5d6cb42ca4876e Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 8 Feb 2022 16:39:37 +0100 Subject: [PATCH] Set rules for everything --- macros/src/lib.rs | 92 ++++++++--- src/eval/collapse.rs | 26 ++- src/eval/mod.rs | 150 ++++------------- src/eval/show.rs | 96 +++++++++++ src/eval/styles.rs | 30 ++-- src/eval/template.rs | 217 +++++++++++++++---------- src/eval/value.rs | 2 +- src/layout/mod.rs | 78 ++++----- src/library/align.rs | 4 +- src/library/columns.rs | 7 +- src/library/container.rs | 2 +- src/library/deco.rs | 76 +++++---- src/library/flow.rs | 34 ++-- src/library/grid.rs | 8 +- src/library/heading.rs | 80 ++++----- src/library/hide.rs | 2 +- src/library/image.rs | 5 - src/library/link.rs | 34 +++- src/library/list.rs | 57 ++++--- src/library/math.rs | 32 ++++ src/library/mod.rs | 20 ++- src/library/pad.rs | 4 +- src/library/page.rs | 296 ++++++++++++++-------------------- src/library/par.rs | 2 +- src/library/place.rs | 4 +- src/library/raw.rs | 117 ++++++++++++++ src/library/shape.rs | 10 +- src/library/spacing.rs | 6 + src/library/stack.rs | 4 +- src/library/table.rs | 19 +-- src/library/text.rs | 125 +++++++------- src/library/transform.rs | 7 +- src/util/mod.rs | 2 + src/util/prehashed.rs | 72 +++++++++ tests/ref/style/construct.png | Bin 2029 -> 4651 bytes tests/ref/text/links.png | Bin 34979 -> 35262 bytes tests/ref/text/par.png | Bin 12918 -> 11934 bytes tests/typ/style/construct.typ | 16 ++ tests/typ/text/links.typ | 18 ++- tests/typ/text/par.typ | 12 -- 40 files changed, 1034 insertions(+), 732 deletions(-) create mode 100644 src/eval/show.rs create mode 100644 src/library/math.rs create mode 100644 src/library/raw.rs create mode 100644 src/util/prehashed.rs diff --git a/macros/src/lib.rs b/macros/src/lib.rs index efaf8be8a..0757a201d 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -5,7 +5,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::parse_quote; use syn::spanned::Spanned; -use syn::{Error, Result}; +use syn::{Error, Ident, Result}; /// Turn a node into a class. #[proc_macro_attribute] @@ -24,15 +24,17 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result { let module = quote::format_ident!("{}_types", self_name); let mut key_modules = vec![]; + let mut properties = vec![]; let mut construct = None; let mut set = None; for item in std::mem::take(&mut impl_block.items) { match item { syn::ImplItem::Const(mut item) => { - key_modules.push(process_const( - &mut item, params, self_ty, &self_name, &self_args, - )?); + let (property, module) = + process_const(&mut item, params, self_ty, &self_name, &self_args)?; + properties.push(property); + key_modules.push(module); impl_block.items.push(syn::ImplItem::Const(item)); } syn::ImplItem::Method(method) => { @@ -49,17 +51,34 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result { let construct = construct.ok_or_else(|| Error::new(impl_block.span(), "missing constructor"))?; - let set = if impl_block.items.is_empty() { - set.unwrap_or_else(|| { - parse_quote! { - fn set(_: &mut Args, _: &mut StyleMap) -> TypResult<()> { - Ok(()) + let set = set.unwrap_or_else(|| { + let sets = properties.into_iter().filter(|p| !p.skip).map(|property| { + let name = property.name; + let string = name.to_string().replace("_", "-").to_lowercase(); + + let alternative = if property.variadic { + quote! { + .or_else(|| { + let list: Vec<_> = args.all().collect(); + (!list.is_empty()).then(|| list) + }) } + } else if property.shorthand { + quote! { .or_else(|| args.find()) } + } else { + quote! {} + }; + + quote! { styles.set_opt(Self::#name, args.named(#string)? #alternative); } + }); + + parse_quote! { + fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> { + #(#sets)* + Ok(()) } - }) - } else { - set.ok_or_else(|| Error::new(impl_block.span(), "missing set method"))? - }; + } + }); // Put everything into a module with a hopefully unique type to isolate // it from the outside. @@ -87,6 +106,14 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result { }) } +/// A style property. +struct Property { + name: Ident, + shorthand: bool, + variadic: bool, + skip: bool, +} + /// Parse the name and generic type arguments of the node type. fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> { // Extract the node type for which we want to generate properties. @@ -120,7 +147,7 @@ fn process_const( self_ty: &syn::Type, self_name: &str, self_args: &[&syn::Type], -) -> Result { +) -> Result<(Property, syn::ItemMod)> { // The module that will contain the `Key` type. let module_name = &item.ident; @@ -139,13 +166,16 @@ fn process_const( let default = &item.expr; let mut folder = None; - let mut nonfolding = Some(quote! { - impl<#params> Nonfolding for Key<#key_args> {} - }); + let mut property = Property { + name: item.ident.clone(), + shorthand: false, + variadic: false, + skip: false, + }; - // Look for a folding function like `#[fold(u64::add)]`. - for attr in &item.attrs { + 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; @@ -155,10 +185,30 @@ fn process_const( f(inner, outer) } }); - nonfolding = None; + } 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); } } + if property.shorthand && property.variadic { + return Err(Error::new( + property.name.span(), + "shorthand and variadic are mutually exclusive", + )); + } + + let nonfolding = folder.is_none().then(|| { + quote! { + impl<#params> Nonfolding for Key<#key_args> {} + } + }); + // Generate the module code. let module = parse_quote! { #[allow(non_snake_case)] @@ -204,5 +254,5 @@ fn process_const( item.ty = parse_quote! { #module_name::Key<#key_args> }; item.expr = parse_quote! { #module_name::Key(PhantomData) }; - Ok(module) + Ok((property, module)) } diff --git a/src/eval/collapse.rs b/src/eval/collapse.rs index 0e91cae68..ef8a5255e 100644 --- a/src/eval/collapse.rs +++ b/src/eval/collapse.rs @@ -3,7 +3,7 @@ use super::{StyleChain, StyleVec, StyleVecBuilder}; /// A wrapper around a [`StyleVecBuilder`] that allows to collapse items. pub struct CollapsingBuilder<'a, T> { builder: StyleVecBuilder<'a, T>, - staged: Vec<(T, StyleChain<'a>, bool)>, + staged: Vec<(T, StyleChain<'a>, Option)>, last: Last, } @@ -29,9 +29,21 @@ impl<'a, T: Merge> CollapsingBuilder<'a, T> { /// and to its right, with no destructive items or weak items in between to /// its left and no destructive items in between to its right. There may be /// ignorant items in between in both directions. - pub fn weak(&mut self, item: T, styles: StyleChain<'a>) { - if self.last == Last::Supportive { - self.staged.push((item, styles, true)); + pub fn weak(&mut self, item: T, strength: u8, styles: StyleChain<'a>) { + if self.last != Last::Destructive { + if self.last == Last::Weak { + if let Some(i) = self + .staged + .iter() + .position(|(.., prev)| prev.map_or(false, |p| p < strength)) + { + self.staged.remove(i); + } else { + return; + } + } + + self.staged.push((item, styles, Some(strength))); self.last = Last::Weak; } } @@ -52,7 +64,7 @@ impl<'a, T: Merge> CollapsingBuilder<'a, T> { /// Has no influence on other items. pub fn ignorant(&mut self, item: T, styles: StyleChain<'a>) { - self.staged.push((item, styles, false)); + self.staged.push((item, styles, None)); } /// Return the finish style vec and the common prefix chain. @@ -63,8 +75,8 @@ impl<'a, T: Merge> CollapsingBuilder<'a, T> { /// Push the staged items, filtering out weak items if `supportive` is false. fn flush(&mut self, supportive: bool) { - for (item, styles, weak) in self.staged.drain(..) { - if !weak || supportive { + for (item, styles, strength) in self.staged.drain(..) { + if supportive || strength.is_none() { push_merging(&mut self.builder, item, styles); } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 98b0152c0..5a67555c0 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -14,6 +14,7 @@ mod collapse; mod func; mod ops; mod scope; +mod show; mod template; pub use array::*; @@ -23,6 +24,7 @@ pub use collapse::*; pub use dict::*; pub use func::*; pub use scope::*; +pub use show::*; pub use styles::*; pub use template::*; pub use value::*; @@ -33,34 +35,23 @@ use std::io; use std::mem; use std::path::PathBuf; -use once_cell::sync::Lazy; -use syntect::easy::HighlightLines; -use syntect::highlighting::{FontStyle, Highlighter, Style as SynStyle, Theme, ThemeSet}; -use syntect::parsing::SyntaxSet; use unicode_segmentation::UnicodeSegmentation; use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; -use crate::geom::{Angle, Color, Fractional, Length, Paint, Relative}; +use crate::geom::{Angle, Fractional, Length, Relative}; use crate::image::ImageStore; use crate::layout::Layout; -use crate::library::{self, DecoLine, TextNode}; +use crate::library::{self}; use crate::loading::Loader; -use crate::parse; use crate::source::{SourceId, SourceStore}; -use crate::syntax; use crate::syntax::ast::*; -use crate::syntax::{RedNode, Span, Spanned}; +use crate::syntax::{Span, Spanned}; use crate::util::{EcoString, RefMutExt}; use crate::Context; -static THEME: Lazy = - Lazy::new(|| ThemeSet::load_defaults().themes.remove("InspiredGitHub").unwrap()); - -static SYNTAXES: Lazy = Lazy::new(|| SyntaxSet::load_defaults_newlines()); - /// An evaluated module, ready for importing or conversion to a root layout /// tree. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Clone)] pub struct Module { /// The top-level definitions that were bound in this module. pub scope: Scope, @@ -194,17 +185,13 @@ fn eval_markup( MarkupNode::Expr(Expr::Wrap(wrap)) => { let tail = eval_markup(ctx, nodes)?; ctx.scopes.def_mut(wrap.binding().take(), tail); - wrap.body().eval(ctx)?.show() + wrap.body().eval(ctx)?.display() } _ => node.eval(ctx)?, }); } - if seq.len() == 1 { - Ok(seq.into_iter().next().unwrap()) - } else { - Ok(Template::Sequence(seq)) - } + Ok(Template::sequence(seq)) } impl Eval for MarkupNode { @@ -223,7 +210,7 @@ impl Eval for MarkupNode { Self::Heading(heading) => heading.eval(ctx)?, Self::List(list) => list.eval(ctx)?, Self::Enum(enum_) => enum_.eval(ctx)?, - Self::Expr(expr) => expr.eval(ctx)?.show(), + Self::Expr(expr) => expr.eval(ctx)?.display(), }) } } @@ -232,7 +219,7 @@ impl Eval for StrongNode { type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - Ok(self.body().eval(ctx)?.styled(TextNode::STRONG, true)) + Ok(Template::show(library::StrongNode(self.body().eval(ctx)?))) } } @@ -240,7 +227,7 @@ impl Eval for EmphNode { type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> TypResult { - Ok(self.body().eval(ctx)?.styled(TextNode::EMPH, true)) + Ok(Template::show(library::EmphNode(self.body().eval(ctx)?))) } } @@ -248,104 +235,25 @@ impl Eval for RawNode { type Output = Template; fn eval(&self, _: &mut EvalContext) -> TypResult { - let code = self.highlighted(); - Ok(if self.block { Template::Block(code.pack()) } else { code }) + let template = Template::show(library::RawNode { + text: self.text.clone(), + block: self.block, + }); + Ok(match self.lang { + Some(_) => template.styled(library::RawNode::LANG, self.lang.clone()), + None => template, + }) } } -impl RawNode { - /// Styled template for a code block, with optional syntax highlighting. - pub fn highlighted(&self) -> Template { - let mut seq: Vec