summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-02-09 19:46:57 +0100
committerLaurenz <laurmaedje@gmail.com>2021-02-09 19:46:57 +0100
commit06ca740d01b428f12f6bd327257cd05dce737b03 (patch)
tree995bf8ff3a606aedecf296c9e805e11e9cd0ae8e /src/library
parente35bbfffcb1f84b2fb0679759152ca0a5eabfad4 (diff)
Split evaluation and execution 🔪
Diffstat (limited to 'src/library')
-rw-r--r--src/library/insert.rs32
-rw-r--r--src/library/layout.rs376
-rw-r--r--src/library/style.rs113
3 files changed, 274 insertions, 247 deletions
diff --git a/src/library/insert.rs b/src/library/insert.rs
index 58e8a11c..eff54e91 100644
--- a/src/library/insert.rs
+++ b/src/library/insert.rs
@@ -15,23 +15,23 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> Value {
let width = args.get(ctx, "width");
let height = args.get(ctx, "height");
- if let Some(path) = path {
- let loaded = ctx.env.resources.load(path.v, ImageResource::parse);
- if let Some((res, img)) = loaded {
- let dimensions = img.buf.dimensions();
- ctx.push(NodeImage {
- res,
- dimensions,
- width,
- height,
- align: ctx.state.align,
- });
- } else {
- ctx.diag(error!(path.span, "failed to load image"));
+ Value::template(move |ctx| {
+ if let Some(path) = &path {
+ let loaded = ctx.env.resources.load(&path.v, ImageResource::parse);
+ if let Some((res, img)) = loaded {
+ let dimensions = img.buf.dimensions();
+ ctx.push(NodeImage {
+ res,
+ dimensions,
+ width,
+ height,
+ align: ctx.state.align,
+ });
+ } else {
+ ctx.diag(error!(path.span, "failed to load image"));
+ }
}
- }
-
- Value::None
+ })
}
/// An image node.
diff --git a/src/library/layout.rs b/src/library/layout.rs
index e4839953..44c98536 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -1,9 +1,9 @@
use std::fmt::{self, Display, Formatter};
-use crate::layout::{Expansion, Fill, NodeFixed, NodeSpacing, NodeStack};
+use crate::exec::Softness;
+use crate::layout::{Expansion, Fill, NodeBackground, NodeFixed, NodeSpacing, NodeStack};
use crate::paper::{Paper, PaperClass};
use crate::prelude::*;
-use crate::{eval::Softness, layout::NodeBackground};
/// `align`: Align content along the layouting axes.
///
@@ -27,88 +27,91 @@ use crate::{eval::Softness, layout::NodeBackground};
/// - `bottom`
/// - `center`
pub fn align(ctx: &mut EvalContext, args: &mut Args) -> Value {
- let snapshot = ctx.state.clone();
-
let first = args.find(ctx);
let second = args.find(ctx);
let hor = args.get(ctx, "horizontal");
let ver = args.get(ctx, "vertical");
-
- let mut had = Gen::uniform(false);
- let mut had_center = false;
-
- for (axis, Spanned { v: arg, span }) in first
- .into_iter()
- .chain(second.into_iter())
- .map(|arg: Spanned<Alignment>| (arg.v.axis(), arg))
- .chain(hor.into_iter().map(|arg| (Some(SpecAxis::Horizontal), arg)))
- .chain(ver.into_iter().map(|arg| (Some(SpecAxis::Vertical), arg)))
- {
- // Check whether we know which axis this alignment belongs to.
- if let Some(axis) = axis {
- // We know the axis.
- let gen_axis = axis.switch(ctx.state.dirs);
- let gen_align = arg.switch(ctx.state.dirs);
-
- if arg.axis().map_or(false, |a| a != axis) {
- ctx.diag(error!(span, "invalid alignment for {} axis", axis));
- } else if had.get(gen_axis) {
- ctx.diag(error!(span, "duplicate alignment for {} axis", axis));
- } else {
- *ctx.state.align.get_mut(gen_axis) = gen_align;
- *had.get_mut(gen_axis) = true;
- }
- } else {
- // We don't know the axis: This has to be a `center` alignment for a
- // positional argument.
- debug_assert_eq!(arg, Alignment::Center);
-
- if had.main && had.cross {
- ctx.diag(error!(span, "duplicate alignment"));
- } else if had_center {
- // Both this and the previous one are unspecified `center`
- // alignments. Both axes should be centered.
- ctx.state.align.main = Align::Center;
- ctx.state.align.cross = Align::Center;
- had = Gen::uniform(true);
+ let body = args.find::<ValueTemplate>(ctx);
+
+ Value::template(move |ctx| {
+ let snapshot = ctx.state.clone();
+
+ let mut had = Gen::uniform(false);
+ let mut had_center = false;
+
+ // Infer the axes alignments belong to.
+ for (axis, Spanned { v: arg, span }) in first
+ .into_iter()
+ .chain(second.into_iter())
+ .map(|arg: Spanned<Alignment>| (arg.v.axis(), arg))
+ .chain(hor.into_iter().map(|arg| (Some(SpecAxis::Horizontal), arg)))
+ .chain(ver.into_iter().map(|arg| (Some(SpecAxis::Vertical), arg)))
+ {
+ // Check whether we know which axis this alignment belongs to.
+ if let Some(axis) = axis {
+ // We know the axis.
+ let gen_axis = axis.switch(ctx.state.dirs);
+ let gen_align = arg.switch(ctx.state.dirs);
+
+ if arg.axis().map_or(false, |a| a != axis) {
+ ctx.diag(error!(span, "invalid alignment for {} axis", axis));
+ } else if had.get(gen_axis) {
+ ctx.diag(error!(span, "duplicate alignment for {} axis", axis));
+ } else {
+ *ctx.state.align.get_mut(gen_axis) = gen_align;
+ *had.get_mut(gen_axis) = true;
+ }
} else {
- had_center = true;
+ // We don't know the axis: This has to be a `center` alignment for a
+ // positional argument.
+ debug_assert_eq!(arg, Alignment::Center);
+
+ if had.main && had.cross {
+ ctx.diag(error!(span, "duplicate alignment"));
+ } else if had_center {
+ // Both this and the previous one are unspecified `center`
+ // alignments. Both axes should be centered.
+ ctx.state.align.main = Align::Center;
+ ctx.state.align.cross = Align::Center;
+ had = Gen::uniform(true);
+ } else {
+ had_center = true;
+ }
}
- }
- // If we we know the other alignment, we can handle the unspecified
- // `center` alignment.
- if had_center && (had.main || had.cross) {
- if had.main {
- ctx.state.align.cross = Align::Center;
- had.cross = true;
- } else {
- ctx.state.align.main = Align::Center;
- had.main = true;
+ // If we we know the other alignment, we can handle the unspecified
+ // `center` alignment.
+ if had_center && (had.main || had.cross) {
+ if had.main {
+ ctx.state.align.cross = Align::Center;
+ had.cross = true;
+ } else {
+ ctx.state.align.main = Align::Center;
+ had.main = true;
+ }
+ had_center = false;
}
- had_center = false;
}
- }
-
- // If `had_center` wasn't flushed by now, it's the only argument and then we
- // default to applying it to the cross axis.
- if had_center {
- ctx.state.align.cross = Align::Center;
- }
- if ctx.state.align.main != snapshot.align.main {
- ctx.end_par_group();
- ctx.start_par_group();
- }
+ // If `had_center` wasn't flushed by now, it's the only argument and then we
+ // default to applying it to the cross axis.
+ if had_center {
+ ctx.state.align.cross = Align::Center;
+ }
- if let Some(body) = args.find::<ValueTemplate>(ctx) {
- body.eval(ctx);
- ctx.state = snapshot;
- }
+ if ctx.state.align.main != snapshot.align.main {
+ ctx.end_par_group();
+ ctx.start_par_group();
+ }
- Value::None
+ if let Some(body) = &body {
+ body.exec(ctx);
+ ctx.state = snapshot;
+ }
+ })
}
+/// An alignment argument.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) enum Alignment {
Left,
@@ -173,10 +176,10 @@ impl Display for Alignment {
/// `box`: Layout content into a box.
///
/// # Named arguments
-/// - Width of the box: `width`, of type `linear` relative to parent width.
-/// - Height of the box: `height`, of type `linear` relative to parent height.
-/// - Main layouting direction: `main-dir`, of type `direction`.
-/// - Cross layouting direction: `cross-dir`, of type `direction`.
+/// - Width of the box: `width`, of type `linear` relative to parent width.
+/// - Height of the box: `height`, of type `linear` relative to parent height.
+/// - Main layouting direction: `main-dir`, of type `direction`.
+/// - Cross layouting direction: `cross-dir`, of type `direction`.
/// - Background color of the box: `color`, of type `color`.
///
/// # Relevant types and constants
@@ -186,47 +189,45 @@ impl Display for Alignment {
/// - `ttb` (top to bottom)
/// - `btt` (bottom to top)
pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
- let snapshot = ctx.state.clone();
-
let width = args.get(ctx, "width");
let height = args.get(ctx, "height");
let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir");
let color = args.get(ctx, "color");
+ let body = args.find::<ValueTemplate>(ctx);
- ctx.set_dirs(Gen::new(main, cross));
-
- let dirs = ctx.state.dirs;
- let align = ctx.state.align;
-
- ctx.start_content_group();
-
- if let Some(body) = args.find::<ValueTemplate>(ctx) {
- body.eval(ctx);
- }
+ Value::template(move |ctx| {
+ let snapshot = ctx.state.clone();
- let children = ctx.end_content_group();
+ ctx.set_dirs(Gen::new(main, cross));
+ let dirs = ctx.state.dirs;
+ let align = ctx.state.align;
- let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
- let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
-
- let fixed_node = NodeFixed {
- width,
- height,
- child: NodeStack { dirs, align, expand, children }.into(),
- };
+ ctx.start_content_group();
+ if let Some(body) = &body {
+ body.exec(ctx);
+ }
+ let children = ctx.end_content_group();
+
+ let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
+ let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
+ let fixed = NodeFixed {
+ width,
+ height,
+ child: NodeStack { dirs, align, expand, children }.into(),
+ };
- if let Some(color) = color {
- ctx.push(NodeBackground {
- fill: Fill::Color(color),
- child: fixed_node.into(),
- })
- } else {
- ctx.push(fixed_node);
- }
+ if let Some(color) = color {
+ ctx.push(NodeBackground {
+ fill: Fill::Color(color),
+ child: fixed.into(),
+ });
+ } else {
+ ctx.push(fixed);
+ }
- ctx.state = snapshot;
- Value::None
+ ctx.state = snapshot;
+ })
}
impl_type! {
@@ -253,19 +254,19 @@ pub fn v(ctx: &mut EvalContext, args: &mut Args) -> Value {
fn spacing(ctx: &mut EvalContext, args: &mut Args, axis: SpecAxis) -> Value {
let spacing: Option<Linear> = args.require(ctx, "spacing");
- if let Some(linear) = spacing {
- let amount = linear.resolve(ctx.state.font.font_size());
- let spacing = NodeSpacing { amount, softness: Softness::Hard };
- if axis == ctx.state.dirs.main.axis() {
- ctx.end_par_group();
- ctx.push(spacing);
- ctx.start_par_group();
- } else {
- ctx.push(spacing);
+ Value::template(move |ctx| {
+ if let Some(linear) = spacing {
+ let amount = linear.resolve(ctx.state.font.font_size());
+ let spacing = NodeSpacing { amount, softness: Softness::Hard };
+ if axis == ctx.state.dirs.main.axis() {
+ ctx.end_par_group();
+ ctx.push(spacing);
+ ctx.start_par_group();
+ } else {
+ ctx.push(spacing);
+ }
}
- }
-
- Value::None
+ })
}
/// `page`: Configure pages.
@@ -275,88 +276,103 @@ fn spacing(ctx: &mut EvalContext, args: &mut Args, axis: SpecAxis) -> Value {
/// full list of all paper names.
///
/// # Named arguments
-/// - Width of the page: `width`, of type `length`.
-/// - Height of the page: `height`, of type `length`.
-/// - Margins for all sides: `margins`, of type `linear` relative to sides.
-/// - Left margin: `left`, of type `linear` relative to width.
-/// - Right margin: `right`, of type `linear` relative to width.
-/// - Top margin: `top`, of type `linear` relative to height.
-/// - Bottom margin: `bottom`, of type `linear` relative to height.
-/// - Flip width and height: `flip`, of type `bool`.
+/// - Width of the page: `width`, of type `length`.
+/// - Height of the page: `height`, of type `length`.
+/// - Margins for all sides: `margins`, of type `linear` relative to sides.
+/// - Left margin: `left`, of type `linear` relative to width.
+/// - Right margin: `right`, of type `linear` relative to width.
+/// - Top margin: `top`, of type `linear` relative to height.
+/// - Bottom margin: `bottom`, of type `linear` relative to height.
+/// - Flip width and height: `flip`, of type `bool`.
+/// - Main layouting direction: `main-dir`, of type `direction`.
+/// - Cross layouting direction: `cross-dir`, of type `direction`.
pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value {
- let snapshot = ctx.state.clone();
+ let paper = args.find::<Spanned<String>>(ctx).and_then(|name| {
+ Paper::from_name(&name.v).or_else(|| {
+ ctx.diag(error!(name.span, "invalid paper name"));
+ None
+ })
+ });
+ let width = args.get(ctx, "width");
+ let height = args.get(ctx, "height");
+ let margins = args.get(ctx, "margins");
+ let left = args.get(ctx, "left");
+ let top = args.get(ctx, "top");
+ let right = args.get(ctx, "right");
+ let bottom = args.get(ctx, "bottom");
+ let flip = args.get(ctx, "flip");
+ let main = args.get(ctx, "main-dir");
+ let cross = args.get(ctx, "cross-dir");
+ let body = args.find::<ValueTemplate>(ctx);
+
+ Value::template(move |ctx| {
+ let snapshot = ctx.state.clone();
- if let Some(name) = args.find::<Spanned<String>>(ctx) {
- if let Some(paper) = Paper::from_name(&name.v) {
+ if let Some(paper) = paper {
ctx.state.page.class = paper.class;
ctx.state.page.size = paper.size();
ctx.state.page.expand = Spec::uniform(Expansion::Fill);
- } else {
- ctx.diag(error!(name.span, "invalid paper name"));
}
- }
-
- if let Some(width) = args.get(ctx, "width") {
- ctx.state.page.class = PaperClass::Custom;
- ctx.state.page.size.width = width;
- ctx.state.page.expand.horizontal = Expansion::Fill;
- }
- if let Some(height) = args.get(ctx, "height") {
- ctx.state.page.class = PaperClass::Custom;
- ctx.state.page.size.height = height;
- ctx.state.page.expand.vertical = Expansion::Fill;
- }
+ if let Some(width) = width {
+ ctx.state.page.class = PaperClass::Custom;
+ ctx.state.page.size.width = width;
+ ctx.state.page.expand.horizontal = Expansion::Fill;
+ }
- if let Some(margins) = args.get(ctx, "margins") {
- ctx.state.page.margins = Sides::uniform(Some(margins));
- }
+ if let Some(height) = height {
+ ctx.state.page.class = PaperClass::Custom;
+ ctx.state.page.size.height = height;
+ ctx.state.page.expand.vertical = Expansion::Fill;
+ }
- if let Some(left) = args.get(ctx, "left") {
- ctx.state.page.margins.left = Some(left);
- }
+ if let Some(margins) = margins {
+ ctx.state.page.margins = Sides::uniform(Some(margins));
+ }
- if let Some(top) = args.get(ctx, "top") {
- ctx.state.page.margins.top = Some(top);
- }
+ if let Some(left) = left {
+ ctx.state.page.margins.left = Some(left);
+ }
- if let Some(right) = args.get(ctx, "right") {
- ctx.state.page.margins.right = Some(right);
- }
+ if let Some(top) = top {
+ ctx.state.page.margins.top = Some(top);
+ }
- if let Some(bottom) = args.get(ctx, "bottom") {
- ctx.state.page.margins.bottom = Some(bottom);
- }
+ if let Some(right) = right {
+ ctx.state.page.margins.right = Some(right);
+ }
- if args.get(ctx, "flip").unwrap_or(false) {
- let page = &mut ctx.state.page;
- std::mem::swap(&mut page.size.width, &mut page.size.height);
- std::mem::swap(&mut page.expand.horizontal, &mut page.expand.vertical);
- }
+ if let Some(bottom) = bottom {
+ ctx.state.page.margins.bottom = Some(bottom);
+ }
- let main = args.get(ctx, "main-dir");
- let cross = args.get(ctx, "cross-dir");
+ if flip.unwrap_or(false) {
+ let page = &mut ctx.state.page;
+ std::mem::swap(&mut page.size.width, &mut page.size.height);
+ std::mem::swap(&mut page.expand.horizontal, &mut page.expand.vertical);
+ }
- ctx.set_dirs(Gen::new(main, cross));
- let mut softness = ctx.end_page_group(|_| false);
- if let Some(body) = args.find::<ValueTemplate>(ctx) {
- // TODO: Restrict body to a single page?
- ctx.start_page_group(Softness::Hard);
- body.eval(ctx);
- ctx.end_page_group(|s| s == Softness::Hard);
- softness = Softness::Soft;
- ctx.state = snapshot;
- }
+ ctx.set_dirs(Gen::new(main, cross));
- ctx.start_page_group(softness);
+ let mut softness = ctx.end_page_group(|_| false);
+ if let Some(body) = &body {
+ // TODO: Restrict body to a single page?
+ ctx.start_page_group(Softness::Hard);
+ body.exec(ctx);
+ ctx.end_page_group(|s| s == Softness::Hard);
+ softness = Softness::Soft;
+ ctx.state = snapshot;
+ }
- Value::None
+ ctx.start_page_group(softness);
+ })
}
/// `pagebreak`: Start a new page.
-pub fn pagebreak(ctx: &mut EvalContext, _: &mut Args) -> Value {
- ctx.end_page_group(|_| true);
- ctx.start_page_group(Softness::Hard);
- Value::None
+pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> Value {
+ Value::template(move |ctx| {
+ ctx.end_page_group(|_| true);
+ ctx.start_page_group(Softness::Hard);
+ })
}
diff --git a/src/library/style.rs b/src/library/style.rs
index 670104d6..23bd5298 100644
--- a/src/library/style.rs
+++ b/src/library/style.rs
@@ -55,68 +55,71 @@ use crate::prelude::*;
/// - `extra-expanded`
/// - `ultra-expanded`
pub fn font(ctx: &mut EvalContext, args: &mut Args) -> Value {
- let snapshot = ctx.state.clone();
-
- if let Some(linear) = args.find::<Linear>(ctx) {
- if linear.rel.is_zero() {
- ctx.state.font.size = linear.abs;
- ctx.state.font.scale = Relative::ONE.into();
- } else {
- ctx.state.font.scale = linear;
- }
- }
-
+ let size = args.find::<Linear>(ctx);
let list: Vec<_> = args.filter::<FontFamily>(ctx).map(|f| f.to_string()).collect();
- if !list.is_empty() {
- let families = ctx.state.font.families_mut();
- families.list = list;
- families.flatten();
- }
+ let style = args.get(ctx, "style");
+ let weight = args.get(ctx, "weight");
+ let stretch = args.get(ctx, "stretch");
+ let serif = args.get(ctx, "serif");
+ let sans_serif = args.get(ctx, "sans-serif");
+ let monospace = args.get(ctx, "monospace");
+ let body = args.find::<ValueTemplate>(ctx);
+
+ Value::template(move |ctx| {
+ let snapshot = ctx.state.clone();
+
+ if let Some(linear) = size {
+ if linear.rel.is_zero() {
+ ctx.state.font.size = linear.abs;
+ ctx.state.font.scale = Relative::ONE.into();
+ } else {
+ ctx.state.font.scale = linear;
+ }
+ }
- if let Some(style) = args.get(ctx, "style") {
- ctx.state.font.variant.style = style;
- }
+ if !list.is_empty() {
+ let families = ctx.state.font.families_mut();
+ families.list = list.clone();
+ families.flatten();
+ }
- if let Some(weight) = args.get(ctx, "weight") {
- ctx.state.font.variant.weight = weight;
- }
+ if let Some(style) = style {
+ ctx.state.font.variant.style = style;
+ }
- if let Some(stretch) = args.get(ctx, "stretch") {
- ctx.state.font.variant.stretch = stretch;
- }
+ if let Some(weight) = weight {
+ ctx.state.font.variant.weight = weight;
+ }
- for variant in FontFamily::VARIANTS {
- if let Some(FontFamilies(list)) = args.get(ctx, variant.as_str()) {
- let strings = list.into_iter().map(|f| f.to_string()).collect();
- let families = ctx.state.font.families_mut();
- families.update_class_list(variant.to_string(), strings);
- families.flatten();
+ if let Some(stretch) = stretch {
+ ctx.state.font.variant.stretch = stretch;
}
- }
- if let Some(body) = args.find::<ValueTemplate>(ctx) {
- body.eval(ctx);
- ctx.state = snapshot;
- }
+ for (variant, arg) in &[
+ (FontFamily::Serif, &serif),
+ (FontFamily::SansSerif, &sans_serif),
+ (FontFamily::Monospace, &monospace),
+ ] {
+ if let Some(FontFamilies(list)) = arg {
+ let strings = list.into_iter().map(|f| f.to_string()).collect();
+ let families = ctx.state.font.families_mut();
+ families.update_class_list(variant.to_string(), strings);
+ families.flatten();
+ }
+ }
- Value::None
+ if let Some(body) = &body {
+ body.exec(ctx);
+ ctx.state = snapshot;
+ }
+ })
}
/// A list of font families.
#[derive(Debug, Clone, PartialEq)]
struct FontFamilies(Vec<FontFamily>);
-impl_type! {
- FontFamilies: "font family or array of font families",
- Value::Str(string) => Self(vec![FontFamily::Named(string.to_lowercase())]),
- Value::Array(values) => Self(values
- .into_iter()
- .filter_map(|v| v.cast().ok())
- .collect()
- ),
- #(family: FontFamily) => Self(vec![family]),
-}
-
+/// A single font family.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) enum FontFamily {
Serif,
@@ -126,9 +129,6 @@ pub(crate) enum FontFamily {
}
impl FontFamily {
- pub const VARIANTS: &'static [Self] =
- &[Self::Serif, Self::SansSerif, Self::Monospace];
-
pub fn as_str(&self) -> &str {
match self {
Self::Serif => "serif",
@@ -146,6 +146,17 @@ impl Display for FontFamily {
}
impl_type! {
+ FontFamilies: "font family or array of font families",
+ Value::Str(string) => Self(vec![FontFamily::Named(string.to_lowercase())]),
+ Value::Array(values) => Self(values
+ .into_iter()
+ .filter_map(|v| v.cast().ok())
+ .collect()
+ ),
+ #(family: FontFamily) => Self(vec![family]),
+}
+
+impl_type! {
FontFamily: "font family",
Value::Str(string) => Self::Named(string.to_lowercase())
}