diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-08-02 16:31:34 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-08-02 16:31:34 +0200 |
| commit | 533374db14087ac54fdc86afa5f009487ac1b850 (patch) | |
| tree | 0970eb1ca893fe45369d622b5bc1f226f0f66004 /src/library | |
| parent | 2188ef6b899cc10c84ed985e9ad9049fcc3eb662 (diff) | |
Refactor argument parsing 🔬
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/font.rs | 61 | ||||
| -rw-r--r-- | src/library/layout.rs | 165 | ||||
| -rw-r--r-- | src/library/mod.rs | 32 | ||||
| -rw-r--r-- | src/library/page.rs | 38 | ||||
| -rw-r--r-- | src/library/spacing.rs | 71 |
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), } } |
