Cleaning 🧹
@ -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<Value>;
|
||||
/// A dictionary value: `(color: #f79143, pattern: dashed)`.
|
||||
pub type ValueDict = BTreeMap<String, Value>;
|
||||
|
||||
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<TemplateNode>;
|
||||
|
||||
@ -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<Value>, 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", |_| {})),
|
||||
],
|
||||
"[*<example>]",
|
||||
);
|
||||
|
||||
// Function and arguments.
|
||||
test_pretty(ValueFunc::new("nil", |_, _| Value::None), "<nil>");
|
||||
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)),
|
||||
},
|
||||
],
|
||||
},
|
||||
"<a: 1, 2>",
|
||||
);
|
||||
|
||||
// Any.
|
||||
test_pretty(ValueAny::new(1), "1");
|
||||
|
||||
// Error.
|
||||
test_pretty(Value::Error, "<error>");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Expansion>,
|
||||
/// 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<Option<Linear>>,
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
124
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("<node ");
|
||||
p.push_str(self.name());
|
||||
p.push('>');
|
||||
}
|
||||
@ -537,7 +553,7 @@ impl Pretty for TemplateAny {
|
||||
|
||||
impl Pretty for ValueFunc {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
p.push('<');
|
||||
p.push_str("<function ");
|
||||
p.push_str(self.name());
|
||||
p.push('>');
|
||||
}
|
||||
@ -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<Value>, 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", |_| {})),
|
||||
],
|
||||
"[*<node example>]",
|
||||
);
|
||||
|
||||
// Function and arguments.
|
||||
test_value(ValueFunc::new("nil", |_, _| Value::None), "<function nil>");
|
||||
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, "<error>");
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 682 B |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.3 KiB |
@ -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 )]
|
||||
|
@ -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 )]
|
@ -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.
|
||||
|
@ -54,7 +54,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Make sure length, relative and linear
|
||||
// - can all be added to / subtracted from each other,
|
||||
// - multiplied with integers and floats,
|
||||
|
@ -44,4 +44,4 @@
|
||||
|
||||
---
|
||||
// Templates.
|
||||
{[*{"Hi"} #[f 1]*]}
|
||||
{[*{"H" + "i"} there*]}
|
||||
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 2.6 KiB |
@ -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]
|
||||
|