summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/library/font.rs162
-rw-r--r--src/library/mod.rs9
-rw-r--r--src/library/page.rs29
-rw-r--r--src/syntax/expr.rs19
-rw-r--r--src/syntax/func/maps.rs2
-rw-r--r--src/syntax/func/mod.rs13
-rw-r--r--src/syntax/func/values.rs14
7 files changed, 81 insertions, 167 deletions
diff --git a/src/library/font.rs b/src/library/font.rs
index a78f9124..be9b8c47 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<SyntaxModel>,
+ size: Option<ScaleLength>,
+ style: Option<FontStyle>,
+ weight: Option<FontWeight>,
+ width: Option<FontWidth>,
list: Vec<String>,
classes: Vec<(String, Vec<String>)>,
}
parse(header, body, ctx, f) {
+ let size = header.args.pos.get_first::<ScaleLength>(&mut f.diagnostics);
+
+ let style = header.args.key.get::<FontStyle>(&mut f.diagnostics, "style");
+ let weight = header.args.key.get::<FontWeight>(&mut f.diagnostics, "weight");
+ let width = header.args.key.get::<FontWidth>(&mut f.diagnostics, "width");
+
let list = header.args.pos.get_all::<StringLike>(&mut f.diagnostics)
.map(|s| s.0.to_lowercase())
.collect();
- let tuples: Vec<_> = header.args.key
+ let classes = header.args.key
.get_all::<String, Tuple>(&mut f.diagnostics)
- .collect();
-
- let classes = tuples.into_iter()
+ .collect::<Vec<_>>()
+ .into_iter()
.map(|(class, mut tuple)| {
let fallback = tuple.get_all::<StringLike>(&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());
- }
-
- s.fallback.flatten();
- })
- }
-}
-
-function! {
- /// `font.style`: Set the font style (normal / italic).
- #[derive(Debug, Clone, PartialEq)]
- pub struct FontStyleFunc {
- body: Option<SyntaxModel>,
- style: Option<FontStyle>,
- }
-
- parse(header, body, ctx, f) {
- FontStyleFunc {
- body: body!(opt: body, ctx, f),
- style: header.args.pos.get::<FontStyle>(&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<SyntaxModel>,
- weight: Option<FontWeight>,
- }
-
- parse(header, body, ctx, f) {
- let body = body!(opt: body, ctx, f);
- let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&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,
- );
+ t.fallback.set_class_list(class.clone(), fallback.clone());
}
- weight
+ t.fallback.flatten();
})
- .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<SyntaxModel>,
- width: Option<FontWidth>,
- }
-
- parse(header, body, ctx, f) {
- let body = body!(opt: body, ctx, f);
- let width = header.args.pos.get::<Spanned<(FontWidth, bool)>>(&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<SyntaxModel>,
- size: Option<ScaleLength>,
- }
-
- parse(header, body, ctx, f) {
- FontSizeFunc {
- body: body!(opt: body, ctx, f),
- size: header.args.pos.get::<ScaleLength>(&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 6e84362b..eac45567 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -16,11 +16,7 @@ pub fn std() -> Scope {
std.add::<ValFunc>("val");
// Font setup
- std.add::<FontFamilyFunc>("font.family");
- std.add::<FontStyleFunc>("font.style");
- std.add::<FontWeightFunc>("font.weight");
- std.add::<FontWidthFunc>("font.width");
- std.add::<FontSizeFunc>("font.size");
+ std.add::<FontFunc>("font");
std.add_with_meta::<ContentSpacingFunc>("word.spacing", ContentKind::Word);
// Layout
@@ -40,8 +36,7 @@ pub fn std() -> Scope {
std.add_with_meta::<SpacingFunc>("v", Some(Vertical));
// Page setup
- std.add::<PageSizeFunc>("page.size");
- std.add::<PageMarginsFunc>("page.margins");
+ std.add::<PageFunc>("page");
std
}
diff --git a/src/library/page.rs b/src/library/page.rs
index 43f916b3..85a061e9 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<Paper>,
extents: AxisMap<Length>,
+ padding: PaddingMap,
flip: bool,
}
parse(header, body, state, f) {
body!(nope: body, f);
- PageSizeFunc {
+ PageFunc {
paper: header.args.pos.get::<Paper>(&mut f.diagnostics),
extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
+ padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args),
flip: header.args.key.get::<bool>(&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 d849366c..51daf304 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<V: Value>(&mut self, _: &mut Diagnostics) -> Option<V> {
+ 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 59159ae1..bc290a9e 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::<Spanned<Defaultable<ScaleLength>>>(diagnostics);
+ let all = args.key.get::<Spanned<Defaultable<ScaleLength>>>(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 c2631727..37dccc3d 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<T>: 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<T> OptionExt for Option<T> {
+impl<T> OptionExt<T> for Option<T> {
+ 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 64d4d345..d5e9c6e8 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<Expr>) -> Result<Self, Diagnostic> {
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<Expr>) -> Result<Self, Diagnostic> {
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())