summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-02 16:31:34 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-02 16:31:34 +0200
commit533374db14087ac54fdc86afa5f009487ac1b850 (patch)
tree0970eb1ca893fe45369d622b5bc1f226f0f66004 /src/library
parent2188ef6b899cc10c84ed985e9ad9049fcc3eb662 (diff)
Refactor argument parsing 🔬
Diffstat (limited to 'src/library')
-rw-r--r--src/library/font.rs61
-rw-r--r--src/library/layout.rs165
-rw-r--r--src/library/mod.rs32
-rw-r--r--src/library/page.rs38
-rw-r--r--src/library/spacing.rs71
5 files changed, 139 insertions, 228 deletions
diff --git a/src/library/font.rs b/src/library/font.rs
index be9b8c47..a5ee7d9c 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -16,63 +16,64 @@ function! {
}
parse(header, body, ctx, f) {
- let size = header.args.pos.get_first::<ScaleLength>(&mut f.diagnostics);
+ let size = header.args.pos.get::<ScaleLength>();
- 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 style = header.args.key.get::<FontStyle>("style", f);
+ let weight = header.args.key.get::<FontWeight>("weight", f);
+ let width = header.args.key.get::<FontWidth>("width", f);
- let list = header.args.pos.get_all::<StringLike>(&mut f.diagnostics)
+ let list = header.args.pos.all::<StringLike>()
.map(|s| s.0.to_lowercase())
.collect();
let classes = header.args.key
- .get_all::<String, Tuple>(&mut f.diagnostics)
+ .all::<Tuple>()
.collect::<Vec<_>>()
.into_iter()
.map(|(class, mut tuple)| {
- let fallback = tuple.get_all::<StringLike>(&mut f.diagnostics)
+ let fallback = tuple.all::<StringLike>()
.map(|s| s.0.to_lowercase())
.collect();
- (class.to_lowercase(), fallback)
+ (class.v.0, fallback)
})
.collect();
FontFunc {
body: body!(opt: body, ctx, f),
size,
- list,
- classes,
style,
weight,
width,
+ list,
+ classes,
}
}
layout(self, ctx, f) {
- styled(&self.body, ctx, Some(()),
- |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,
- });
+ styled(&self.body, ctx, Some(()), |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);
+ 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() {
- *t.fallback.list_mut() = self.list.clone();
- }
+ if !self.list.is_empty() {
+ *t.fallback.list_mut() = self.list.iter()
+ .map(|s| s.to_lowercase())
+ .collect();
+ }
- for (class, fallback) in &self.classes {
- t.fallback.set_class_list(class.clone(), fallback.clone());
- }
+ for (class, fallback) in &self.classes {
+ t.fallback.set_class_list(class.clone(), fallback.clone());
+ }
- t.fallback.flatten();
- })
+ t.fallback.flatten();
+ })
}
}
diff --git a/src/library/layout.rs b/src/library/layout.rs
index 7d989555..ed936816 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -2,88 +2,100 @@ use crate::length::ScaleLength;
use super::*;
function! {
- /// `align`: Aligns content along the layouting axes.
+ /// `box`: Layouts content into a box.
#[derive(Debug, Clone, PartialEq)]
- pub struct AlignFunc {
- body: Option<SyntaxModel>,
- map: PosAxisMap<AlignmentValue>,
+ pub struct BoxFunc {
+ body: SyntaxModel,
+ width: Option<ScaleLength>,
+ height: Option<ScaleLength>,
+ debug: Option<bool>,
}
parse(header, body, ctx, f) {
- AlignFunc {
- body: body!(opt: body, ctx, f),
- map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
+ BoxFunc {
+ body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
+ width: header.args.key.get::<ScaleLength>("width", f),
+ height: header.args.key.get::<ScaleLength>("height", f),
+ debug: header.args.key.get::<bool>("debug", f),
}
}
layout(self, ctx, f) {
- ctx.base = ctx.spaces[0].dimensions;
+ ctx.repeat = false;
+ ctx.spaces.truncate(1);
- let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |alignment| {
- alignment.axis().map(|s| s.to_generic(ctx.axes))
+ if let Some(debug) = self.debug {
+ ctx.debug = debug;
+ }
+
+ self.width.with(|v| {
+ let length = v.raw_scaled(ctx.base.x);
+ ctx.base.x = length;
+ ctx.spaces[0].dimensions.x = length;
+ ctx.spaces[0].expansion.horizontal = true;
});
- for &axis in &[Primary, Secondary] {
- if let Some(Spanned { v: alignment, span }) = map.get_spanned(axis) {
- if let Some(generic) = alignment.to_generic(ctx.axes, axis) {
- *ctx.alignment.get_mut(axis) = generic;
- } else {
- error!(
- @f, span,
- "invalid alignment `{}` for {} axis", alignment, axis,
- );
- }
- }
- }
+ self.height.with(|v| {
+ let length = v.raw_scaled(ctx.base.y);
+ ctx.base.y = length;
+ ctx.spaces[0].dimensions.y = length;
+ ctx.spaces[0].expansion.vertical = true;
+ });
- match &self.body {
- Some(body) => {
- let layouted = layout(body, ctx).await;
- f.extend(layouted.feedback);
- vec![AddMultiple(layouted.output)]
- }
- None => vec![SetAlignment(ctx.alignment)],
- }
+ let layouted = layout(&self.body, ctx).await;
+ let layout = layouted.output.into_iter().next().unwrap();
+ f.extend(layouted.feedback);
+
+ vec![Add(layout)]
}
}
function! {
- /// `direction`: Sets the directions of the layouting axes.
+ /// `align`: Aligns content along the layouting axes.
#[derive(Debug, Clone, PartialEq)]
- pub struct DirectionFunc {
- name_span: Span,
+ pub struct AlignFunc {
body: Option<SyntaxModel>,
- map: PosAxisMap<Direction>,
+ aligns: Vec<Spanned<SpecAlign>>,
+ h: Option<Spanned<SpecAlign>>,
+ v: Option<Spanned<SpecAlign>>,
}
parse(header, body, ctx, f) {
- DirectionFunc {
- name_span: header.name.span,
+ AlignFunc {
body: body!(opt: body, ctx, f),
- map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
+ aligns: header.args.pos.all::<Spanned<SpecAlign>>().collect(),
+ h: header.args.key.get::<Spanned<SpecAlign>>("horizontal", f),
+ v: header.args.key.get::<Spanned<SpecAlign>>("vertical", f),
}
}
layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions;
- let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |direction| {
- Some(direction.axis().to_generic(ctx.axes))
- });
-
- let mut axes = ctx.axes;
-
- map.with(Primary, |&dir| axes.primary = dir);
- map.with(Secondary, |&dir| axes.secondary = dir);
-
- if axes.primary.axis() == axes.secondary.axis() {
- error!(
- @f, self.name_span,
- "invalid aligned primary and secondary axes: `{}`, `{}`",
- ctx.axes.primary, ctx.axes.secondary,
- );
- } else {
- ctx.axes = axes;
+ let axes = ctx.axes;
+ let all = self.aligns.iter()
+ .map(|align| {
+ let spec = align.v.axis().unwrap_or(axes.primary.axis());
+ (spec, align)
+ })
+ .chain(self.h.iter().map(|align| (Horizontal, align)))
+ .chain(self.v.iter().map(|align| (Vertical, align)));
+
+ let mut had = [false; 2];
+ for (axis, align) in all {
+ if align.v.axis().map(|a| a != axis).unwrap_or(false) {
+ error!(
+ @f, align.span,
+ "invalid alignment {} for {} axis", align.v, axis,
+ );
+ } else if had[axis as usize] {
+ error!(@f, align.span, "duplicate alignment for {} axis", axis);
+ } else {
+ had[axis as usize] = true;
+ let gen_axis = axis.to_generic(ctx.axes);
+ let gen_align = align.v.to_generic(ctx.axes);
+ *ctx.align.get_mut(gen_axis) = gen_align;
+ }
}
match &self.body {
@@ -92,50 +104,7 @@ function! {
f.extend(layouted.feedback);
vec![AddMultiple(layouted.output)]
}
- None => vec![SetAxes(ctx.axes)],
- }
- }
-}
-
-function! {
- /// `box`: Layouts content into a box.
- #[derive(Debug, Clone, PartialEq)]
- pub struct BoxFunc {
- body: SyntaxModel,
- extents: AxisMap<ScaleLength>,
- debug: Option<bool>,
- }
-
- parse(header, body, ctx, f) {
- BoxFunc {
- body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
- extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
- debug: header.args.key.get::<bool>(&mut f.diagnostics, "debug"),
+ None => vec![SetAlignment(ctx.align)],
}
}
-
- layout(self, ctx, f) {
- ctx.repeat = false;
- ctx.spaces.truncate(1);
-
- if let Some(debug) = self.debug {
- ctx.debug = debug;
- }
-
- let map = self.extents.dedup(&mut f.diagnostics, ctx.axes);
- for &axis in &[Horizontal, Vertical] {
- if let Some(scale) = map.get(axis) {
- let length = scale.raw_scaled(ctx.base.get(axis));
- *ctx.base.get_mut(axis) = length;
- *ctx.spaces[0].dimensions.get_mut(axis) = length;
- *ctx.spaces[0].expansion.get_mut(axis) = true;
- }
- }
-
- let layouted = layout(&self.body, ctx).await;
- let layout = layouted.output.into_iter().next().unwrap();
- f.extend(layouted.feedback);
-
- vec![Add(layout)]
- }
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index eac45567..7b7034a0 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -1,6 +1,6 @@
//! The _Typst_ standard library.
-use crate::syntax::Scope;
+use crate::syntax::scope::Scope;
use crate::func::prelude::*;
pub_use_mod!(font);
@@ -12,31 +12,15 @@ pub_use_mod!(spacing);
pub fn std() -> Scope {
let mut std = Scope::new::<ValFunc>();
- // Basics
std.add::<ValFunc>("val");
-
- // Font setup
std.add::<FontFunc>("font");
- std.add_with_meta::<ContentSpacingFunc>("word.spacing", ContentKind::Word);
-
- // Layout
- std.add_with_meta::<ContentSpacingFunc>("line.spacing", ContentKind::Line);
- std.add_with_meta::<ContentSpacingFunc>("par.spacing", ContentKind::Paragraph);
+ std.add::<PageFunc>("page");
std.add::<AlignFunc>("align");
- std.add::<DirectionFunc>("direction");
std.add::<BoxFunc>("box");
-
- // Spacing
- std.add::<LineBreakFunc>("n");
- std.add::<LineBreakFunc>("line.break");
- std.add::<ParBreakFunc>("par.break");
- std.add::<PageBreakFunc>("page.break");
- std.add_with_meta::<SpacingFunc>("spacing", None);
- std.add_with_meta::<SpacingFunc>("h", Some(Horizontal));
- std.add_with_meta::<SpacingFunc>("v", Some(Vertical));
-
- // Page setup
- std.add::<PageFunc>("page");
+ std.add_with_meta::<SpacingFunc>("h", Horizontal);
+ std.add_with_meta::<SpacingFunc>("v", Vertical);
+ std.add::<ParBreakFunc>("parbreak");
+ std.add::<PageBreakFunc>("pagebreak");
std
}
@@ -49,8 +33,8 @@ function! {
}
parse(header, body, state, f) {
- header.args.pos.items.clear();
- header.args.key.pairs.clear();
+ header.args.pos.0.clear();
+ header.args.key.0.clear();
ValFunc { body: body!(opt: body, state, f) }
}
diff --git a/src/library/page.rs b/src/library/page.rs
index 85a061e9..f1dcc9bc 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -1,4 +1,4 @@
-use crate::length::Length;
+use crate::length::{Length, ScaleLength};
use crate::paper::{Paper, PaperClass};
use super::*;
@@ -7,18 +7,28 @@ function! {
#[derive(Debug, Clone, PartialEq)]
pub struct PageFunc {
paper: Option<Paper>,
- extents: AxisMap<Length>,
- padding: PaddingMap,
+ width: Option<Length>,
+ height: Option<Length>,
+ margins: Option<ScaleLength>,
+ left: Option<ScaleLength>,
+ right: Option<ScaleLength>,
+ top: Option<ScaleLength>,
+ bottom: Option<ScaleLength>,
flip: bool,
}
parse(header, body, state, f) {
body!(nope: body, f);
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),
+ paper: header.args.pos.get::<Paper>(),
+ width: header.args.key.get::<Length>("width", f),
+ height: header.args.key.get::<Length>("height", f),
+ margins: header.args.key.get::<ScaleLength>("margins", f),
+ left: header.args.key.get::<ScaleLength>("left", f),
+ right: header.args.key.get::<ScaleLength>("right", f),
+ top: header.args.key.get::<ScaleLength>("top", f),
+ bottom: header.args.key.get::<ScaleLength>("bottom", f),
+ flip: header.args.key.get::<bool>("flip", f).unwrap_or(false),
}
}
@@ -28,20 +38,22 @@ function! {
if let Some(paper) = self.paper {
style.class = paper.class;
style.dimensions = paper.size();
- } else {
+ } else if self.width.is_some() || self.height.is_some() {
style.class = PaperClass::Custom;
}
- let map = self.extents.dedup(&mut f.diagnostics, ctx.axes);
- map.with(Horizontal, |&width| style.dimensions.x = width.as_raw());
- map.with(Vertical, |&height| style.dimensions.y = height.as_raw());
+ self.width.with(|v| style.dimensions.x = v.as_raw());
+ self.height.with(|v| style.dimensions.y = v.as_raw());
+ self.margins.with(|v| style.margins.set_all(Some(v)));
+ self.left.with(|v| style.margins.left = Some(v));
+ self.right.with(|v| style.margins.right = Some(v));
+ self.top.with(|v| style.margins.top = Some(v));
+ self.bottom.with(|v| style.margins.bottom = Some(v));
if self.flip {
style.dimensions.swap();
}
- self.padding.apply(&mut f.diagnostics, ctx.axes, &mut style.margins);
-
vec![SetPageStyle(style)]
}
}
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index c632bdb4..68ef27f2 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -1,20 +1,9 @@
use crate::length::ScaleLength;
use crate::layout::SpacingKind;
-
use super::*;
-use self::ContentKind::*;
function! {
- /// `line.break`, `n`: Ends the current line.
- #[derive(Debug, Default, Clone, PartialEq)]
- pub struct LineBreakFunc;
-
- parse(default)
- layout(self, ctx, f) { vec![BreakLine] }
-}
-
-function! {
- /// `par.break`: Ends the current paragraph.
+ /// `parbreak`: Ends the current paragraph.
///
/// self has the same effect as two subsequent newlines.
#[derive(Debug, Default, Clone, PartialEq)]
@@ -25,7 +14,7 @@ function! {
}
function! {
- /// `page.break`: Ends the current page.
+ /// `pagebreak`: Ends the current page.
#[derive(Debug, Default, Clone, PartialEq)]
pub struct PageBreakFunc;
@@ -34,64 +23,20 @@ function! {
}
function! {
- /// `word.spacing`, `line.spacing`, `par.spacing`: The spacing between
- /// words, lines or paragraphs as a multiple of the font size.
- #[derive(Debug, Clone, PartialEq)]
- pub struct ContentSpacingFunc {
- body: Option<SyntaxModel>,
- content: ContentKind,
- spacing: Option<f64>,
- }
-
- type Meta = ContentKind;
-
- parse(header, body, state, f, meta) {
- ContentSpacingFunc {
- body: body!(opt: body, state, f),
- content: meta,
- spacing: header.args.pos.get::<f64>(&mut f.diagnostics)
- .map(|num| num as f64)
- .or_missing(&mut f.diagnostics, header.name.span, "spacing"),
- }
- }
-
- layout(self, ctx, f) {
- styled(&self.body, ctx, self.spacing, |t, s| match self.content {
- Word => t.word_spacing_scale = s,
- Line => t.line_spacing_scale = s,
- Paragraph => t.paragraph_spacing_scale = s,
- })
- }
-}
-
-/// The different kinds of content that can be spaced. Used as a metadata type
-/// for the [`ContentSpacingFunc`].
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-#[allow(missing_docs)]
-pub enum ContentKind {
- Word,
- Line,
- Paragraph,
-}
-
-function! {
- /// `spacing`, `h`, `v`: Adds spacing along an axis.
+ /// `h` and `v`: Add spacing along an axis.
#[derive(Debug, Clone, PartialEq)]
pub struct SpacingFunc {
- spacing: Option<(AxisKey, ScaleLength)>,
+ spacing: Option<(SpecAxis, ScaleLength)>,
}
- type Meta = Option<SpecificAxis>;
+ type Meta = SpecAxis;
parse(header, body, state, f, meta) {
body!(nope: body, f);
SpacingFunc {
- spacing: if let Some(axis) = meta {
- header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
- .map(|s| (AxisKey::Specific(axis), s))
- } else {
- header.args.key.get_with_key::<AxisKey, ScaleLength>(&mut f.diagnostics)
- }.or_missing(&mut f.diagnostics, header.name.span, "spacing"),
+ spacing: header.args.pos.expect::<ScaleLength>(f)
+ .map(|s| (meta, s))
+ .or_missing(header.name.span, "spacing", f),
}
}