diff --git a/src/parse/tests.rs b/src/parse/tests.rs index bb102476d..c87d43f74 100644 --- a/src/parse/tests.rs +++ b/src/parse/tests.rs @@ -261,128 +261,6 @@ fn test_parse_blocks() { S(4..5, "unexpected opening brace")]); } -#[test] -fn test_parse_bracket_funcs() { - // Basic. - t!("[function]" Call!("function")); - t!("[ v ]" Call!("v")); - - // Body and no body. - t!("[v][[f]]" Call!("v", Args![Template![Call!("f")]])); - t!("[v][v][v]" Call!("v", Args![Template![Text("v")]]), Call!("v")); - t!("[v] [f]" Call!("v"), Space, Call!("f")); - - // Spans. - t!("[v 1][📐]" - nodes: [S(0..11, Call!(S(1..2, "v"), S(3..4, Args![ - S(3..4, Int(1)), - S(5..11, Template![S(6..10, Text("📐"))]), - ])))], - spans: true); - - // No name and no closing bracket. - t!("[" - nodes: [Call!("")], - errors: [S(1..1, "expected function name"), - S(1..1, "expected closing bracket")]); - - // No name. - t!("[]" - nodes: [Call!("")], - errors: [S(1..1, "expected function name")]); - - // Bad name. - t!("[# 1]" - nodes: [Call!("", Args![Int(1)])], - errors: [S(1..2, "expected function name, found hex value")]); - - // String in header eats closing bracket. - t!(r#"[v "]"# - nodes: [Call!("v", Args![Str("]")])], - errors: [S(5..5, "expected quote"), - S(5..5, "expected closing bracket")]); - - // Raw in body eats closing bracket. - t!("[v][`a]`" - nodes: [Call!("v", Args![Template![Raw(None, &["a]"], true)]])], - errors: [S(8..8, "expected closing bracket")]); -} - -#[test] -fn test_parse_chaining() { - // Basic. - t!("[a | b]" Call!("a", Args![Template![Call!("b")]])); - t!("[a|b|c]" Call!("a", Args![Template![ - Call!("b", Args![Template![Call!("c")]]) - ]])); - - // With body and spans. - t!("[a|b][💕]" - nodes: [S(0..11, Call!(S(1..2, "a"), S(2..2, Args![ - S(3..11, Template![S(3..11, Call!(S(3..4, "b"), S(4..4, Args![ - S(5..11, Template![S(6..10, Text("💕"))]) - ])))]) - ])))], - spans: true); - - // No name in second subheader. - t!("[a 1|]" - nodes: [Call!("a", Args![Int(1), Template![Call!("")]])], - errors: [S(5..5, "expected function name")]); - - // No name in first subheader. - t!("[|a true]" - nodes: [Call!("", Args![Template![Call!("a", Args![Bool(true)])]])], - errors: [S(1..1, "expected function name")]); -} - -#[test] -fn test_parse_arguments() { - // Bracket functions. - t!("[v a]" Call!("v", Args![Id("a")])); - t!("[v 1,]" Call!("v", Args![Int(1)])); - t!("[v a:2]" Call!("v", Args!["a" => Int(2)])); - - // Parenthesized function with nested array literal. - t!(r#"{f(1, a: (2, 3), #004, b: "five")}"# Block!(Call!(@"f", Args![ - Int(1), - "a" => Array![Int(2), Int(3)], - Color(RgbaColor::new(0, 0, 0x44, 0xff)), - "b" => Str("five"), - ]))); - - // Bad expression. - t!("[v */]" - nodes: [Call!("v", Args![])], - errors: [S(3..5, "expected expression, found end of block comment")]); - - // Bad expression. - t!("[v a:1:]" - nodes: [Call!("v", Args!["a" => Int(1)])], - errors: [S(6..7, "expected expression, found colon")]); - - // Missing comma between arguments. - t!("[v 1 2]" - nodes: [Call!("v", Args![Int(1), Int(2)])], - errors: [S(4..4, "expected comma")]); - - // Name has to be identifier. - t!("[v 1:]" - nodes: [Call!("v", Args![])], - errors: [S(3..4, "expected identifier"), - S(5..5, "expected expression")]); - - // Name has to be identifier. - t!("[v 1:2]" - nodes: [Call!("v", Args![])], - errors: [S(3..4, "expected identifier")]); - - // Name has to be identifier. - t!("[v (x):1]" - nodes: [Call!("v", Args![])], - errors: [S(3..6, "expected identifier")]); -} - #[test] fn test_parse_expressions() { // Parentheses. diff --git a/tests/README.md b/tests/README.md index 429207a5e..91bf8f49e 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,10 +1,9 @@ # Tests +Directory structure: - `typ`: Input files. - `ref`: Reference images which the output is compared with to determine whether - a test passed or failed. To keep things small, please run - `oxipng -o max tests/ref/` when creating or updating reference - images (note that `` can be `*` to optimize all images). + a test passed or failed. - `res`: Resource files used by tests. - `png`: PNG files produced by tests. - `pdf`: PDF files produced by tests. @@ -13,3 +12,12 @@ The test files are split into three categories: - `full`: Tests of full documents. - `lang`: Tests for specific language features. - `library`: Tests for specific library functions. + +To keep things small, please optimize the reference images: +```bash +# One image +oxipng -o max tests/ref/image.png + +# All images +oxipng -r -o max tests/ref/* +``` diff --git a/tests/ref/lang/bracket-call.png b/tests/ref/lang/bracket-call.png new file mode 100644 index 000000000..e7ba46e31 Binary files /dev/null and b/tests/ref/lang/bracket-call.png differ diff --git a/tests/ref/lang/comments.png b/tests/ref/lang/comments.png index e3e42d231..df5e5b9cb 100644 Binary files a/tests/ref/lang/comments.png and b/tests/ref/lang/comments.png differ diff --git a/tests/typ/lang/bracket-call.typ b/tests/typ/lang/bracket-call.typ new file mode 100644 index 000000000..642d6426a --- /dev/null +++ b/tests/typ/lang/bracket-call.typ @@ -0,0 +1,86 @@ +// Basic call, whitespace insignificant. +[f], [ f ], [ + f +] + +[f bold] + +[f 1,] + +[f a:2] + +[f 1, a: (3, 4), 2, b: "5"] + +--- +// Body and no body. +[f][[f]] + +// Lots of potential bodies. +[f][f][f] + +// Multi-paragraph body. +[box][ + First + + Second +] + +--- +// Chained. +[f | f] + +// Multi-chain. +[f|f|f] + +// With body. +[f | box][💕] + +// Error: 1:2-1:2 expected function name +[|f true] + +// Error: 1:6-1:6 expected function name +[f 1|] + +// With actual functions. +[box width: 1cm | image "res/rhino.png"] + +--- +// Error: 1:4-1:6 expected expression, found end of block comment +[f */] + +// Error: 1:7-1:8 expected expression, found colon +[f a:1:] + +// Error: 1:5-1:5 expected comma +[f 1 2] + +// Error: 2:4-2:5 expected identifier +// Error: 1:6-1:6 expected expression +[f 1:] + +// Error: 1:4-1:5 expected identifier +[f 1:2] + +// Error: 1:4-1:7 expected identifier +[f (x):1] + +--- +// Error: 2:2-2:3 a value of type string is not callable +#let x = "string"; +[x] + +// Error: 1:2-1:3 expected function name, found hex value +[# 1] + +// Error: 4:1-4:1 expected function name +// Error: 3:1-3:1 expected closing bracket +[ + +--- +// Error: 3:1-3:1 expected closing bracket +[f][`a]` + +--- +// Error: 3:1-3:1 expected quote +// Error: 2:1-2:1 expected closing bracket +[f "] diff --git a/tests/typ/lang/comments.typ b/tests/typ/lang/comments.typ index 56906d0ca..c5b04967b 100644 --- a/tests/typ/lang/comments.typ +++ b/tests/typ/lang/comments.typ @@ -8,7 +8,7 @@ C/* */D // Test in expressions. -[dump /*1*/ a: "b" // +[f /*1*/ a: "b" // , 1] // Error: 1:7-1:9 unexpected end of block comment diff --git a/tests/typeset.rs b/tests/typeset.rs index 79a884ebf..3eaca6edf 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -16,12 +16,13 @@ use walkdir::WalkDir; use typst::diag::{Diag, Feedback, Level, Pass}; use typst::env::{Env, ImageResource, ResourceLoader, SharedEnv}; -use typst::eval::{Args, EvalContext, State, Value, ValueFunc}; +use typst::eval::{Args, EvalContext, Scope, State, Value, ValueFunc}; use typst::export::pdf; use typst::font::FontLoader; use typst::geom::{Length, Point, Sides, Size, Spec}; use typst::layout::{Element, Expansion, Frame, Image}; use typst::parse::{LineMap, Scanner}; +use typst::pretty::{Pretty, Printer}; use typst::shaping::Shaped; use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan}; use typst::typeset; @@ -183,13 +184,7 @@ fn test_part(i: usize, src: &str, env: &SharedEnv) -> (bool, Vec) { state.page.size = Size::new(Length::pt(120.0), Length::raw(f64::INFINITY)); state.page.expand = Spec::new(Expansion::Fill, Expansion::Fit); state.page.margins = Sides::uniform(Some(Length::pt(10.0).into())); - - pub fn dump(_: &mut EvalContext, args: &mut Args) -> Value { - let (array, dict) = args.drain(); - Value::Array(vec![Value::Array(array), Value::Dict(dict)]) - } - - Rc::make_mut(&mut state.scope).set("dump", ValueFunc::new("dump", dump)); + register_helpers(Rc::make_mut(&mut state.scope)); let Pass { output: mut frames, @@ -261,6 +256,31 @@ fn parse_metadata(src: &str, map: &LineMap) -> (bool, SpanVec) { (compare_ref, diags) } +fn register_helpers(scope: &mut Scope) { + pub fn f(_: &mut EvalContext, args: &mut Args) -> Value { + let (array, dict) = args.drain(); + let iter = array + .into_iter() + .map(|v| (None, v)) + .chain(dict.into_iter().map(|(k, v)| (Some(k), v))); + + let mut p = Printer::new(); + p.push_str("f("); + p.join(iter, ", ", |(key, value), p| { + if let Some(key) = key { + p.push_str(&key); + p.push_str(": "); + } + value.pretty(p); + }); + p.push_str(")"); + + Value::Str(p.finish()) + } + + scope.set("f", ValueFunc::new("f", f)); +} + fn print_diag(diag: &Spanned, map: &LineMap) { let start = map.location(diag.span.start).unwrap(); let end = map.location(diag.span.end).unwrap();