From efb78831a7a9fe8c807c326a1bfa338b50579938 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 2 Aug 2020 11:06:45 +0200 Subject: [PATCH] =?UTF-8?q?Unify=20font=20and=20page=20functions=20?= =?UTF-8?q?=F0=9F=92=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removes font weight and width warnings for now, will be added again later - Adds a bit hacky get_first function for tuples, will be refactored soon anyway --- src/library/font.rs | 162 +++++++++----------------------------- src/library/mod.rs | 9 +-- src/library/page.rs | 29 ++----- src/syntax/expr.rs | 19 +++++ src/syntax/func/maps.rs | 2 +- src/syntax/func/mod.rs | 13 ++- src/syntax/func/values.rs | 14 ++-- tests/coma.typ | 3 +- tests/stack.typ | 3 +- 9 files changed, 83 insertions(+), 171 deletions(-) diff --git a/src/library/font.rs b/src/library/font.rs index a78f91241..be9b8c471 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -3,24 +3,33 @@ use crate::length::ScaleLength; use super::*; function! { - /// `font.family`: Set the font family. + /// `font`: Configure the font. #[derive(Debug, Clone, PartialEq)] - pub struct FontFamilyFunc { + pub struct FontFunc { body: Option, + size: Option, + style: Option, + weight: Option, + width: Option, list: Vec, classes: Vec<(String, Vec)>, } parse(header, body, ctx, f) { + let size = header.args.pos.get_first::(&mut f.diagnostics); + + let style = header.args.key.get::(&mut f.diagnostics, "style"); + let weight = header.args.key.get::(&mut f.diagnostics, "weight"); + let width = header.args.key.get::(&mut f.diagnostics, "width"); + let list = header.args.pos.get_all::(&mut f.diagnostics) .map(|s| s.0.to_lowercase()) .collect(); - let tuples: Vec<_> = header.args.key + let classes = header.args.key .get_all::(&mut f.diagnostics) - .collect(); - - let classes = tuples.into_iter() + .collect::>() + .into_iter() .map(|(class, mut tuple)| { let fallback = tuple.get_all::(&mut f.diagnostics) .map(|s| s.0.to_lowercase()) @@ -29,140 +38,41 @@ function! { }) .collect(); - FontFamilyFunc { + FontFunc { body: body!(opt: body, ctx, f), + size, list, classes, + style, + weight, + width, } } layout(self, ctx, f) { styled(&self.body, ctx, Some(()), - |s, _| { + |t, _| { + self.size.with(|s| match s { + ScaleLength::Absolute(length) => { + t.base_font_size = length.as_raw(); + t.font_scale = 1.0; + } + ScaleLength::Scaled(scale) => t.font_scale = scale, + }); + + self.style.with(|s| t.variant.style = s); + self.weight.with(|w| t.variant.weight = w); + self.width.with(|w| t.variant.width = w); + if !self.list.is_empty() { - *s.fallback.list_mut() = self.list.clone(); + *t.fallback.list_mut() = self.list.clone(); } for (class, fallback) in &self.classes { - s.fallback.set_class_list(class.clone(), fallback.clone()); + t.fallback.set_class_list(class.clone(), fallback.clone()); } - s.fallback.flatten(); + t.fallback.flatten(); }) } } - -function! { - /// `font.style`: Set the font style (normal / italic). - #[derive(Debug, Clone, PartialEq)] - pub struct FontStyleFunc { - body: Option, - style: Option, - } - - parse(header, body, ctx, f) { - FontStyleFunc { - body: body!(opt: body, ctx, f), - style: header.args.pos.get::(&mut f.diagnostics) - .or_missing(&mut f.diagnostics, header.name.span, "style"), - } - } - - layout(self, ctx, f) { - styled(&self.body, ctx, self.style, |t, s| t.variant.style = s) - } -} - -function! { - /// `font.weight`: Set text with a given weight. - #[derive(Debug, Clone, PartialEq)] - pub struct FontWeightFunc { - body: Option, - weight: Option, - } - - parse(header, body, ctx, f) { - let body = body!(opt: body, ctx, f); - let weight = header.args.pos.get::>(&mut f.diagnostics) - .map(|Spanned { v: (weight, is_clamped), span }| { - if is_clamped { - warning!( - @f, span, - "weight should be between 100 and 900, clamped to {}", - weight.0, - ); - } - - weight - }) - .or_missing(&mut f.diagnostics, header.name.span, "weight"); - - FontWeightFunc { body, weight } - } - - layout(self, ctx, f) { - styled(&self.body, ctx, self.weight, |t, w| t.variant.weight = w) - } -} - - -function! { - /// `font.width`: Set text with a given width. - #[derive(Debug, Clone, PartialEq)] - pub struct FontWidthFunc { - body: Option, - width: Option, - } - - parse(header, body, ctx, f) { - let body = body!(opt: body, ctx, f); - let width = header.args.pos.get::>(&mut f.diagnostics) - .map(|Spanned { v: (width, is_clamped), span }| { - if is_clamped { - warning!( - @f, span, - "width should be between 1 and 9, clamped to {}", - width.to_number(), - ); - } - - width - }) - .or_missing(&mut f.diagnostics, header.name.span, "width"); - - FontWidthFunc { body, width } - } - - layout(self, ctx, f) { - styled(&self.body, ctx, self.width, |t, w| t.variant.width = w) - } -} - -function! { - /// `font.size`: Sets the font size. - #[derive(Debug, Clone, PartialEq)] - pub struct FontSizeFunc { - body: Option, - size: Option, - } - - parse(header, body, ctx, f) { - FontSizeFunc { - body: body!(opt: body, ctx, f), - size: header.args.pos.get::(&mut f.diagnostics) - .or_missing(&mut f.diagnostics, header.name.span, "size") - } - } - - layout(self, ctx, f) { - styled(&self.body, ctx, self.size, |t, s| { - match s { - ScaleLength::Absolute(length) => { - t.base_font_size = length.as_raw(); - t.font_scale = 1.0; - } - ScaleLength::Scaled(scale) => t.font_scale = scale, - } - }) - } -} diff --git a/src/library/mod.rs b/src/library/mod.rs index 6e84362bc..eac455671 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -16,11 +16,7 @@ pub fn std() -> Scope { std.add::("val"); // Font setup - std.add::("font.family"); - std.add::("font.style"); - std.add::("font.weight"); - std.add::("font.width"); - std.add::("font.size"); + std.add::("font"); std.add_with_meta::("word.spacing", ContentKind::Word); // Layout @@ -40,8 +36,7 @@ pub fn std() -> Scope { std.add_with_meta::("v", Some(Vertical)); // Page setup - std.add::("page.size"); - std.add::("page.margins"); + std.add::("page"); std } diff --git a/src/library/page.rs b/src/library/page.rs index 43f916b33..85a061e9c 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -3,19 +3,21 @@ use crate::paper::{Paper, PaperClass}; use super::*; function! { - /// `page.size`: Set the size of pages. + /// `page`: Configure pages. #[derive(Debug, Clone, PartialEq)] - pub struct PageSizeFunc { + pub struct PageFunc { paper: Option, extents: AxisMap, + padding: PaddingMap, flip: bool, } parse(header, body, state, f) { body!(nope: body, f); - PageSizeFunc { + PageFunc { paper: header.args.pos.get::(&mut f.diagnostics), extents: AxisMap::parse::(&mut f.diagnostics, &mut header.args.key), + padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args), flip: header.args.key.get::(&mut f.diagnostics, "flip").unwrap_or(false), } } @@ -38,27 +40,8 @@ function! { style.dimensions.swap(); } - vec![SetPageStyle(style)] - } -} - -function! { - /// `page.margins`: Sets the page margins. - #[derive(Debug, Clone, PartialEq)] - pub struct PageMarginsFunc { - padding: PaddingMap, - } - - parse(header, body, state, f) { - body!(nope: body, f); - PageMarginsFunc { - padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args), - } - } - - layout(self, ctx, f) { - let mut style = ctx.style.page; self.padding.apply(&mut f.diagnostics, ctx.axes, &mut style.margins); + vec![SetPageStyle(style)] } } diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index d849366c9..51daf304d 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -268,6 +268,25 @@ impl Tuple { None } + /// Extract (and remove) the first matching value without removing and + /// generating diagnostics for all previous items that did not match. + pub fn get_first(&mut self, _: &mut Diagnostics) -> Option { + let mut i = 0; + while i < self.items.len() { + let expr = self.items[i].clone(); + match V::parse(expr) { + Ok(output) => { + self.items.remove(i); + return Some(output) + } + Err(_) => {}, + } + i += 1; + } + + None + } + /// Extract and return an iterator over all values that match and generate /// diagnostics for all items that do not match. pub fn get_all<'a, V: Value>(&'a mut self, diagnostics: &'a mut Diagnostics) diff --git a/src/syntax/func/maps.rs b/src/syntax/func/maps.rs index 59159ae11..bc290a9e4 100644 --- a/src/syntax/func/maps.rs +++ b/src/syntax/func/maps.rs @@ -178,7 +178,7 @@ impl PaddingMap { pub fn parse(diagnostics: &mut Diagnostics, args: &mut FuncArgs) -> PaddingMap { let mut map = DedupMap::new(); - let all = args.pos.get::>>(diagnostics); + let all = args.key.get::>>(diagnostics, "margins"); if let Some(Spanned { v, span }) = all { map.insert(diagnostics, Spanned { v: (PaddingKey::All, v.into()), span }); } diff --git a/src/syntax/func/mod.rs b/src/syntax/func/mod.rs index c26317275..37dccc3da 100644 --- a/src/syntax/func/mod.rs +++ b/src/syntax/func/mod.rs @@ -82,13 +82,22 @@ pub enum FuncArg { } /// Extra methods on [`Options`](Option) used for argument parsing. -pub trait OptionExt: Sized { +pub trait OptionExt: Sized { + /// Calls `f` with `val` if this is `Some(val)`. + fn with(self, f: impl FnOnce(T)); + /// Add an error about a missing argument `arg` with the given span if the /// option is `None`. fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self; } -impl OptionExt for Option { +impl OptionExt for Option { + fn with(self, f: impl FnOnce(T)) { + if let Some(val) = self { + f(val); + } + } + fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self { if self.is_none() { diagnostics.push(error!(span, "missing argument: {}", arg)); diff --git a/src/syntax/func/values.rs b/src/syntax/func/values.rs index 64d4d345d..d5e9c6e8e 100644 --- a/src/syntax/func/values.rs +++ b/src/syntax/func/values.rs @@ -143,22 +143,21 @@ impl Value for FontStyle { /// The additional boolean specifies whether a number was clamped into the range /// 100 - 900 to make it a valid font weight. -impl Value for (FontWeight, bool) { +impl Value for FontWeight { fn parse(expr: Spanned) -> Result { match expr.v { Expr::Number(weight) => { let weight = weight.round(); if weight >= 100.0 && weight <= 900.0 { - Ok((FontWeight(weight as u16), false)) + Ok(FontWeight(weight as u16)) } else { let clamped = weight.min(900.0).max(100.0); - Ok((FontWeight(clamped as u16), true)) + Ok(FontWeight(clamped as u16)) } } Expr::Ident(id) => { FontWeight::from_name(id.as_str()) .ok_or_else(|| error!("invalid font weight")) - .map(|weight| (weight, false)) } other => Err( error!("expected identifier or number, found {}", other.name()) @@ -169,22 +168,21 @@ impl Value for (FontWeight, bool) { /// The additional boolean specifies whether a number was clamped into the range /// 1 - 9 to make it a valid font width. -impl Value for (FontWidth, bool) { +impl Value for FontWidth { fn parse(expr: Spanned) -> Result { match expr.v { Expr::Number(width) => { let width = width.round(); if width >= 1.0 && width <= 9.0 { - Ok((FontWidth::new(width as u16).unwrap(), false)) + Ok(FontWidth::new(width as u16).unwrap()) } else { let clamped = width.min(9.0).max(1.0); - Ok((FontWidth::new(clamped as u16).unwrap(), true)) + Ok(FontWidth::new(clamped as u16).unwrap()) } } Expr::Ident(id) => { FontWidth::from_name(id.as_str()) .ok_or_else(|| error!("invalid font width")) - .map(|width| (width, false)) } other => Err( error!("expected identifier or number, found {}", other.name()) diff --git a/tests/coma.typ b/tests/coma.typ index c69cf8fd0..be59e6fd3 100644 --- a/tests/coma.typ +++ b/tests/coma.typ @@ -1,5 +1,4 @@ -[page.size: width=450pt, height=300pt] -[page.margins: 1cm] +[page: width=450pt, height=300pt, margins=1cm] [box][ *Technische Universität Berlin* [n] diff --git a/tests/stack.typ b/tests/stack.typ index cbca41dcf..78f6540e3 100644 --- a/tests/stack.typ +++ b/tests/stack.typ @@ -1,5 +1,4 @@ -[page.size: w=5cm, h=5cm] -[page.margins: 0cm] +[page: w=5cm, h=5cm, margins=0cm] // Test 1 [box: w=1, h=1, debug=false][