diff --git a/src/eval/value.rs b/src/eval/value.rs index ce89d453..2879e6d6 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -8,7 +8,6 @@ use super::*; use crate::color::Color; use crate::exec::ExecContext; use crate::geom::{Angle, Length, Linear, Relative}; -use crate::pretty::{Pretty, Printer}; use crate::syntax::Tree; /// A computational value. @@ -103,22 +102,6 @@ pub type ValueArray = Vec; /// A dictionary value: `(color: #f79143, pattern: dashed)`. pub type ValueDict = BTreeMap; -impl Pretty for ValueDict { - fn pretty(&self, p: &mut Printer) { - p.push('('); - if self.is_empty() { - p.push(':'); - } else { - p.join(self, ", ", |(key, value), p| { - p.push_str(key); - p.push_str(": "); - value.pretty(p); - }); - } - p.push(')'); - } -} - /// A template value: `[*Hi* there]`. pub type ValueTemplate = Vec; @@ -156,7 +139,6 @@ impl TemplateAny { Self { name: name.into(), f: Rc::new(f) } } - /// The name of the template node. pub fn name(&self) -> &str { &self.name @@ -358,13 +340,6 @@ impl ValueArgs { } } -// This is a workaround because `-> impl Trait + 'a + 'b` does not work. -// -// See also: https://github.com/rust-lang/rust/issues/49431 -#[doc(hidden)] -pub trait Captures<'a> {} -impl<'a, T: ?Sized> Captures<'a> for T {} - /// An argument to a function call: `12` or `draw: false`. #[derive(Debug, Clone, PartialEq)] pub struct ValueArg { @@ -681,80 +656,3 @@ macro_rules! typify { } }; } - -#[cfg(test)] -mod tests { - use super::*; - use crate::color::RgbaColor; - use crate::pretty::pretty; - - #[track_caller] - fn test_pretty(value: impl Into, exp: &str) { - assert_eq!(pretty(&value.into()), exp); - } - - #[test] - fn test_pretty_print_value() { - // Simple values. - test_pretty(Value::None, "none"); - test_pretty(false, "false"); - test_pretty(12, "12"); - test_pretty(3.14, "3.14"); - test_pretty(Length::pt(5.5), "5.5pt"); - test_pretty(Angle::deg(90.0), "90.0deg"); - test_pretty(Relative::ONE / 2.0, "50.0%"); - test_pretty(Relative::new(0.3) + Length::cm(2.0), "30.0% + 2.0cm"); - test_pretty(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101"); - test_pretty("hello", r#""hello""#); - - // Array. - test_pretty(Value::Array(vec![]), "()"); - test_pretty(vec![Value::None], "(none,)"); - test_pretty(vec![Value::Int(1), Value::Int(2)], "(1, 2)"); - - // Dictionary. - let mut dict = BTreeMap::new(); - test_pretty(dict.clone(), "(:)"); - dict.insert("one".into(), Value::Int(1)); - test_pretty(dict.clone(), "(one: 1)"); - dict.insert("two".into(), Value::Bool(false)); - test_pretty(dict, "(one: 1, two: false)"); - - // Template. - test_pretty( - vec![ - TemplateNode::Tree { - tree: Rc::new(vec![Node::Strong]), - map: HashMap::new(), - }, - TemplateNode::Any(TemplateAny::new("example", |_| {})), - ], - "[*]", - ); - - // Function and arguments. - test_pretty(ValueFunc::new("nil", |_, _| Value::None), ""); - test_pretty( - ValueArgs { - span: Span::ZERO, - items: vec![ - ValueArg { - name: Some(Spanned::zero("a".into())), - value: Spanned::zero(Value::Int(1)), - }, - ValueArg { - name: None, - value: Spanned::zero(Value::Int(2)), - }, - ], - }, - "", - ); - - // Any. - test_pretty(ValueAny::new(1), "1"); - - // Error. - test_pretty(Value::Error, ""); - } -} diff --git a/src/exec/mod.rs b/src/exec/mod.rs index a0ed946d..57fe8138 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -145,8 +145,8 @@ impl Exec for Value { impl Exec for ValueTemplate { fn exec(&self, ctx: &mut ExecContext) { - for part in self { - part.exec(ctx); + for node in self { + node.exec(ctx); } } } diff --git a/src/exec/state.rs b/src/exec/state.rs index 21fb7fb6..977b0b73 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -44,8 +44,8 @@ pub struct PageSettings { pub size: Size, /// Whether the expand the pages to the `size` or to fit the content. pub expand: Spec, - /// The amount of white space in the order [left, top, right, bottom]. If a - /// side is set to `None`, the default for the paper class is used. + /// The amount of white space on each side of the page. If a side is set to + /// `None`, the default for the paper class is used. pub margins: Sides>, } diff --git a/src/geom/length.rs b/src/geom/length.rs index db28761b..b0ca24df 100644 --- a/src/geom/length.rs +++ b/src/geom/length.rs @@ -107,7 +107,7 @@ impl Display for Length { use LengthUnit::*; // Format with the unit that yields the shortest output, preferring - // larger / metrics units when tied. + // larger / metric units when tied. let mut buf = ryu::Buffer::new(); let unit = [Cm, Mm, In, Pt] .iter() diff --git a/src/layout/background.rs b/src/layout/background.rs index 07248e02..6b605d7f 100644 --- a/src/layout/background.rs +++ b/src/layout/background.rs @@ -1,11 +1,11 @@ use super::*; -/// A node that represents a rectangular box. +/// A node that places a rectangular filled background behind another node. #[derive(Debug, Clone, PartialEq)] pub struct NodeBackground { /// The background fill. pub fill: Fill, - /// The child node to be filled in. + /// The child node to be filled. pub child: Node, } @@ -13,17 +13,12 @@ impl Layout for NodeBackground { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { let mut layouted = self.child.layout(ctx, areas); - if let Some(first) = layouted.frames_mut().first_mut() { - first.elements.insert( - 0, - ( - Point::ZERO, - Element::Geometry(Geometry { - shape: Shape::Rect(first.size), - fill: self.fill.clone(), - }), - ), - ) + for frame in layouted.frames_mut() { + let element = Element::Geometry(Geometry { + shape: Shape::Rect(frame.size), + fill: self.fill.clone(), + }); + frame.elements.insert(0, (Point::ZERO, element)) } layouted diff --git a/src/library/layout.rs b/src/library/layout.rs index 3577ee36..7fc7154f 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -293,6 +293,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { None }) }); + let width = args.get(ctx, "width"); let height = args.get(ctx, "height"); let margins = args.get(ctx, "margins"); @@ -352,7 +353,6 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { 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); diff --git a/src/pretty.rs b/src/pretty.rs index 025bc66c..2e16a914 100644 --- a/src/pretty.rs +++ b/src/pretty.rs @@ -508,6 +508,22 @@ impl Pretty for ValueArray { } } +impl Pretty for ValueDict { + fn pretty(&self, p: &mut Printer) { + p.push('('); + if self.is_empty() { + p.push(':'); + } else { + p.join(self, ", ", |(key, value), p| { + p.push_str(key); + p.push_str(": "); + value.pretty(p); + }); + } + p.push(')'); + } +} + impl Pretty for ValueTemplate { fn pretty(&self, p: &mut Printer) { p.push('['); @@ -529,7 +545,7 @@ impl Pretty for TemplateNode { impl Pretty for TemplateAny { fn pretty(&self, p: &mut Printer) { - p.push('<'); + p.push_str("'); } @@ -537,7 +553,7 @@ impl Pretty for TemplateAny { impl Pretty for ValueFunc { fn pretty(&self, p: &mut Printer) { - p.push('<'); + p.push_str("'); } @@ -545,9 +561,9 @@ impl Pretty for ValueFunc { impl Pretty for ValueArgs { fn pretty(&self, p: &mut Printer) { - p.push('<'); + p.push('('); p.join(&self.items, ", ", |item, p| item.pretty(p)); - p.push('>'); + p.push(')'); } } @@ -613,13 +629,22 @@ pretty_display! { #[cfg(test)] mod tests { + use std::collections::{BTreeMap, HashMap}; + use std::rc::Rc; + use super::*; + use crate::color::RgbaColor; use crate::env::Env; use crate::eval::eval; use crate::parse::parse; #[track_caller] - fn test(src: &str, exp: &str) { + fn roundtrip(src: &str) { + test_parse(src, src); + } + + #[track_caller] + fn test_parse(src: &str, exp: &str) { let tree = parse(src).output; let found = pretty(&tree); if exp != found { @@ -631,8 +656,8 @@ mod tests { } #[track_caller] - fn roundtrip(src: &str) { - test(src, src); + fn test_value(value: impl Into, exp: &str) { + assert_eq!(pretty(&value.into()), exp); } #[test] @@ -659,11 +684,11 @@ mod tests { roundtrip("```\n`\n```"); roundtrip("``` ` ```"); roundtrip("````\n```\n```\n````"); - test("```lang```", "```lang ```"); - test("```1 ```", "``"); - test("``` 1```", "`1`"); - test("``` 1 ```", "`1 `"); - test("```` ` ````", "``` ` ```"); + test_parse("```lang```", "```lang ```"); + test_parse("```1 ```", "``"); + test_parse("``` 1```", "`1`"); + test_parse("``` 1 ```", "`1 `"); + test_parse("```` ` ````", "``` ` ```"); } #[test] @@ -679,7 +704,7 @@ mod tests { roundtrip("{20.0%}"); roundtrip("{#abcdef}"); roundtrip(r#"{"hi"}"#); - test(r#"{"let's \" go"}"#, r#"{"let's \" go"}"#); + test_parse(r#"{"let's \" go"}"#, r#"{"let's \" go"}"#); // Arrays. roundtrip("{()}"); @@ -720,8 +745,8 @@ mod tests { roundtrip("#[v 1]"); roundtrip("#[v 1, 2][*Ok*]"); roundtrip("#[v 1 | f 2]"); - test("{#[v]}", "{v()}"); - test("#[v 1, #[f 2]]", "#[v 1 | f 2]"); + test_parse("{#[v]}", "{v()}"); + test_parse("#[v 1, #[f 2]]", "#[v 1 | f 2]"); // Keywords. roundtrip("#let x = 1 + 2"); @@ -738,9 +763,70 @@ mod tests { } #[test] - fn test_pretty_print_str() { - assert_eq!(pretty("\n"), r#""\n""#); - assert_eq!(pretty("\\"), r#""\\""#); - assert_eq!(pretty("\""), r#""\"""#); + fn test_pretty_print_value() { + // Simple values. + test_value(Value::None, "none"); + test_value(false, "false"); + test_value(12, "12"); + test_value(3.14, "3.14"); + test_value(Length::pt(5.5), "5.5pt"); + test_value(Angle::deg(90.0), "90.0deg"); + test_value(Relative::ONE / 2.0, "50.0%"); + test_value(Relative::new(0.3) + Length::cm(2.0), "30.0% + 2.0cm"); + test_value(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101"); + test_value("hello", r#""hello""#); + test_value("\n", r#""\n""#); + test_value("\\", r#""\\""#); + test_value("\"", r#""\"""#); + + // Array. + test_value(Value::Array(vec![]), "()"); + test_value(vec![Value::None], "(none,)"); + test_value(vec![Value::Int(1), Value::Int(2)], "(1, 2)"); + + // Dictionary. + let mut dict = BTreeMap::new(); + test_value(dict.clone(), "(:)"); + dict.insert("one".into(), Value::Int(1)); + test_value(dict.clone(), "(one: 1)"); + dict.insert("two".into(), Value::Bool(false)); + test_value(dict, "(one: 1, two: false)"); + + // Template. + test_value( + vec![ + TemplateNode::Tree { + tree: Rc::new(vec![Node::Strong]), + map: HashMap::new(), + }, + TemplateNode::Any(TemplateAny::new("example", |_| {})), + ], + "[*]", + ); + + // Function and arguments. + test_value(ValueFunc::new("nil", |_, _| Value::None), ""); + test_value( + ValueArgs { + span: Span::ZERO, + items: vec![ + ValueArg { + name: Some(Spanned::zero("a".into())), + value: Spanned::zero(Value::Int(1)), + }, + ValueArg { + name: None, + value: Spanned::zero(Value::Int(2)), + }, + ], + }, + "(a: 1, 2)", + ); + + // Any. + test_value(ValueAny::new(1), "1"); + + // Error. + test_value(Value::Error, ""); } } diff --git a/tests/lang/ref/call-args.png b/tests/lang/ref/call-args.png index 99acd84b..b288be22 100644 Binary files a/tests/lang/ref/call-args.png and b/tests/lang/ref/call-args.png differ diff --git a/tests/lang/ref/call-bracket.png b/tests/lang/ref/call-bracket.png index 347907b8..b5b2f767 100644 Binary files a/tests/lang/ref/call-bracket.png and b/tests/lang/ref/call-bracket.png differ diff --git a/tests/lang/ref/call-chain.png b/tests/lang/ref/call-chain.png deleted file mode 100644 index de5520b8..00000000 Binary files a/tests/lang/ref/call-chain.png and /dev/null differ diff --git a/tests/lang/ref/call-invalid.png b/tests/lang/ref/call-invalid.png index a60e037f..0bceebc1 100644 Binary files a/tests/lang/ref/call-invalid.png and b/tests/lang/ref/call-invalid.png differ diff --git a/tests/lang/ref/comment.png b/tests/lang/ref/comment.png index 0ab9a988..7ab48b3a 100644 Binary files a/tests/lang/ref/comment.png and b/tests/lang/ref/comment.png differ diff --git a/tests/lang/ref/repr.png b/tests/lang/ref/repr.png index 110e26c0..16c7ea95 100644 Binary files a/tests/lang/ref/repr.png and b/tests/lang/ref/repr.png differ diff --git a/tests/lang/typ/call-bracket.typ b/tests/lang/typ/call-bracket.typ index 2ee2c5d4..eb097094 100644 --- a/tests/lang/typ/call-bracket.typ +++ b/tests/lang/typ/call-bracket.typ @@ -1,11 +1,11 @@ // Test bracketed function calls. --- -// Whitespace insignificant. -#[f], #[ f ] +// Whitespace is insignificant. +#[ f ] -// Body and no body. -#[f][#[f]] +// Alternatives for embedding. +#[f f()], #[f #[f]], #[f][#[f]], // Tight functions. #[f]#[f] @@ -16,3 +16,33 @@ Second ] + +--- +// Chained once. +#[f | f] + +// Chained twice. +#[f|f|f] + +// With body. +// Error: 7-8 expected identifier, found integer +#[f | 1 | box][💕] + +// With actual functions. +#[box width: 1cm | image "res/rhino.png"] + +--- +// Error: 8-8 expected identifier +#[f 1 |] + +// Error: 4-4 expected identifier +#[ | f true] + +// Error: 2:3-2:3 expected identifier +// Error: 1:4-1:4 expected identifier +#[|][Nope] + +// Pipe wins over parens. +// Error: 2:6-2:6 expected closing paren +// Error: 1:9-1:10 expected expression, found closing paren +#[f (|f )] diff --git a/tests/lang/typ/call-chain.typ b/tests/lang/typ/call-chain.typ deleted file mode 100644 index 72899f95..00000000 --- a/tests/lang/typ/call-chain.typ +++ /dev/null @@ -1,31 +0,0 @@ -// Test bracket call chaining. - ---- -// Chained once. -#[f | f] - -// Chained twice. -#[f|f|f] - -// With body. -// Error: 7-8 expected identifier, found integer -#[f | 1 | box][💕] - -// With actual functions. -#[box width: 1cm | image "res/rhino.png"] - ---- -// Error: 8-8 expected identifier -#[f 1 |] - -// Error: 4-4 expected identifier -#[ | f true] - -// Error: 2:3-2:3 expected identifier -// Error: 1:4-1:4 expected identifier -#[|][Nope] - -// Pipe wins over parens. -// Error: 2:6-2:6 expected closing paren -// Error: 1:9-1:10 expected expression, found closing paren -#[f (|f )] diff --git a/tests/lang/typ/comment.typ b/tests/lang/typ/comment.typ index 524a24e3..8b394f1f 100644 --- a/tests/lang/typ/comment.typ +++ b/tests/lang/typ/comment.typ @@ -11,8 +11,8 @@ C/* */D // Works in code. -#[f /*1*/ a: "b" // -, 1] +#[test type /*1*/ (1) // +, "integer"] --- // End should not appear without start. diff --git a/tests/lang/typ/expr-binary.typ b/tests/lang/typ/expr-binary.typ index b0226998..e84cb282 100644 --- a/tests/lang/typ/expr-binary.typ +++ b/tests/lang/typ/expr-binary.typ @@ -54,7 +54,6 @@ } } - // Make sure length, relative and linear // - can all be added to / subtracted from each other, // - multiplied with integers and floats, diff --git a/tests/lang/typ/repr.typ b/tests/lang/typ/repr.typ index 1975a8e6..6229165a 100644 --- a/tests/lang/typ/repr.typ +++ b/tests/lang/typ/repr.typ @@ -44,4 +44,4 @@ --- // Templates. -{[*{"Hi"} #[f 1]*]} +{[*{"H" + "i"} there*]} diff --git a/tests/library/ref/box.png b/tests/library/ref/box.png index 37fd7d27..9827c6c3 100644 Binary files a/tests/library/ref/box.png and b/tests/library/ref/box.png differ diff --git a/tests/library/typ/box.typ b/tests/library/typ/box.typ index 03e5da54..67abe350 100644 --- a/tests/library/typ/box.typ +++ b/tests/library/typ/box.typ @@ -1,23 +1,20 @@ -#[page "a5", flip: true] +#[page "a7", flip: true] -// Rectangle with width, should have paragraph height -#[box width: 2cm, color: #9650D6][aa] +// Box with fixed width, should have text height. +#[box width: 2cm, color: #9650D6][A] -Sometimes there is no box +Sometimes there is no box. -// Rectangle with height, should span line -#[box height: 2cm, width: 100%, color: #734CED][bb] +// Box with fixed height, should span line. +#[box height: 2cm, width: 100%, color: #734CED][B] -// Empty rectangle with width and height +// Empty box with fixed width and height. #[box width: 6cm, height: 12pt, color: #CB4CED] -// This empty rectangle should not be displayed +// Not visiblem, but creates a gap between the boxes above and below. #[box width: 2in, color: #ff0000] -// This one should be -#[box height: 15mm, width: 100%, color: #494DE3] - // These are in a row! -#[box width: 2in, height: 10pt, color: #D6CD67] -#[box width: 2in, height: 10pt, color: #EDD466] -#[box width: 2in, height: 10pt, color: #E3BE62] +#[box width: 1in, height: 10pt, color: #D6CD67] +#[box width: 1in, height: 10pt, color: #EDD466] +#[box width: 1in, height: 10pt, color: #E3BE62]