roc/crates/compiler/test_syntax/tests/test_fmt.rs
2025-01-28 21:00:30 -06:00

7163 lines
142 KiB
Rust

#[macro_use]
extern crate indoc;
#[cfg(test)]
mod test_fmt {
use bumpalo::Bump;
use roc_fmt::def::fmt_defs;
use roc_fmt::header::fmt_header;
use roc_fmt::{Buf, MigrationFlags};
use roc_parse::ast::{Defs, Header, SpacesBefore};
use roc_parse::header::{self, parse_module_defs};
use roc_parse::state::State;
use roc_test_utils::assert_multiline_str_eq;
use roc_test_utils_dir::workspace_root;
use test_syntax::test_helpers::Input;
fn check_formatting(expected: &'_ str) -> impl Fn(Input) + '_ {
let expected = expected.trim();
move |output| {
assert_multiline_str_eq!(expected, output.as_str());
}
}
fn expr_formats_to(input: &str, expected: &str) {
Input::Expr(input.trim()).check_invariants(
check_formatting(expected.trim()),
true,
Some(false),
)
}
fn expr_formats_to_with_flags(input: &str, expected: &str, flags: MigrationFlags) {
Input::Expr(input.trim()).check_invariants_with_flags(
check_formatting(expected.trim()),
true,
Some(false),
flags,
)
}
fn expr_formats_same(input: &str) {
Input::Expr(input.trim()).check_invariants(
check_formatting(input.trim()),
true,
Some(false),
)
}
fn pattern_formats_same(input: &str) {
Input::Pattern(input.trim()).check_invariants(
check_formatting(input.trim()),
true,
Some(false),
);
}
fn expr_formats_same_with_flags(input: &str, flags: MigrationFlags) {
Input::Expr(input.trim()).check_invariants_with_flags(
check_formatting(input.trim()),
true,
Some(false),
flags,
)
}
fn fmt_module_and_defs<'a>(
arena: &Bump,
src: &str,
header: &SpacesBefore<'a, Header<'a>>,
state: State<'a>,
buf: &mut Buf<'_>,
) {
fmt_header(buf, header);
match parse_module_defs(arena, state, Defs::default()) {
Ok(loc_defs) => {
fmt_defs(buf, &loc_defs, 0);
}
Err(error) => {
let src = if src.len() > 1000 {
"<source too long to display>"
} else {
src
};
panic!(
"Unexpected parse failure when parsing this for defs formatting:\n\n{src}\n\nParse error was:\n\n{error:?}\n\n"
)
}
}
}
// Not intended to be used directly in tests; please use module_formats_to or module_formats_same
fn expect_format_module_helper(src: &str, expected: &str) {
let arena = Bump::new();
let src = src.trim();
let expected = expected.trim();
match header::parse_header(&arena, State::new(src.as_bytes())) {
Ok((actual, state)) => {
use roc_parse::normalize::Normalize;
let flags = MigrationFlags { snakify: false, parens_and_commas: false };
let mut buf = Buf::new_in(&arena, flags);
fmt_module_and_defs(&arena, src, &actual, state, &mut buf);
let output = buf.as_str().trim();
let (reparsed_ast, state) = header::parse_header(&arena, State::new(output.as_bytes())).unwrap_or_else(|err| {
panic!(
"After formatting, the source code no longer parsed!\n\nParse error was: {err:?}\n\nThe code that failed to parse:\n\n{output}\n\n"
);
});
let ast_normalized = actual.normalize(&arena);
let reparsed_ast_normalized = reparsed_ast.normalize(&arena);
// HACK!
// We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast,
// the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same.
// I don't have the patience to debug this right now, so let's leave it for another day...
// TODO: fix PartialEq impl on ast types
if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") {
panic!(
"Formatting bug; formatting didn't reparse to the same AST (after removing spaces)\n\n\
* * * Source code before formatting:\n{src}\n\n\
* * * Source code after formatting:\n{output}\n\n"
);
}
// Now verify that the resultant formatting is _stable_ - i.e. that it doesn't change again if re-formatted
let mut reformatted_buf = Buf::new_in(&arena, flags);
fmt_module_and_defs(&arena, output, &reparsed_ast, state, &mut reformatted_buf);
let reformatted = reformatted_buf.as_str().trim();
if output != reformatted {
eprintln!("Formatting bug; formatting is not stable. Reformatting the formatted code changed it again, as follows:\n\n");
assert_multiline_str_eq!(output, reformatted);
}
// If everything was idempotent re-parsing worked, finally assert
// that the formatted code was what we expected it to be.
//
// Do this last because if there were any serious problems with the
// formatter (e.g. it wasn't idempotent), we want to know about
// those more than we want to know that the expectation failed!
assert_multiline_str_eq!(expected, output);
}
Err(error) => panic!("Unexpected parse failure when parsing this for module header formatting:\n\n{src:?}\n\nParse error was:\n\n{error:?}\n\n")
};
}
fn module_formats_to(input: &str, expected: &str) {
// First check that input formats to the expected version
expect_format_module_helper(input, expected);
// Parse the expected result format it, asserting that it doesn't change
// It's important that formatting be stable / idempotent
expect_format_module_helper(expected, expected);
}
fn module_formats_same(input: &str) {
module_formats_to(input, input);
}
// STRING LITERALS
#[test]
fn empty_string() {
expr_formats_same(indoc!(
r#"
""
"#
));
}
#[test]
fn def_with_comment() {
expr_formats_same(indoc!(
r#"
# This variable is for greeting
a = "Hello"
a
"#
));
}
#[test]
fn shebang_comment() {
// Correct shebangs are left alone
expr_formats_same(indoc!(
r#"
#!/usr/bin/env roc
x = 0
x
"#
));
// Incorrect shebang formatting from the past is fixed
expr_formats_to(
indoc!(
r#"
# !/usr/bin/env roc
x = 0
x
"#
),
indoc!(
r#"
#!/usr/bin/env roc
x = 0
x
"#
),
);
// Other whitespace from the user is left alone
expr_formats_to(
&format!(
indoc!(
r#"
# !/usr/bin/env roc{space}
x = 0
x
"#
),
space = " "
),
indoc!(
r#"
# !/usr/bin/env roc
x = 0
x
"#
),
);
}
#[test]
fn comment_with_trailing_space() {
expr_formats_to(
&format!(
indoc!(
r"
# first comment{space}
x = 0 # second comment{space}
x
"
),
space = " ",
),
indoc!(
r"
# first comment
x = 0 # second comment
x
"
),
);
}
#[test]
fn def_with_inline_comment() {
expr_formats_same(indoc!(
r"
x = 0 # comment
x
"
));
expr_formats_to(
indoc!(
r"
x = 0# comment
x
"
),
indoc!(
r"
x = 0 # comment
x
"
),
);
expr_formats_to(
indoc!(
r"
x = 0# comment
x
"
),
indoc!(
r"
x = 0 # comment
x
"
),
);
expr_formats_to(
indoc!(
r"
x = 0 # comment
x
"
),
indoc!(
r"
x = 0 # comment
x
"
),
);
}
#[test]
fn def_with_comment_and_extra_space() {
expr_formats_to(
indoc!(
r#"
# This variable is for greeting
a = "Hello"
a
"#
),
indoc!(
r#"
# This variable is for greeting
a = "Hello"
a
"#
),
);
}
#[test]
fn type_annotation_allow_blank_line_before_and_after_comment() {
expr_formats_same(indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
));
expr_formats_to_with_flags(
indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
",
),
indoc!(
r"
person : {
first_name : Str,
# comment
last_name : Str,
}
person
",
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_same(indoc!(
r"
person : {
first_name : Str,
# comment
last_name : Str,
}
person
",
));
expr_formats_same_with_flags(
indoc!(
r"
person : {
first_name : Str,
# comment
last_name : Str,
}
person
",
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_same(indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
));
expr_formats_same(indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
));
expr_formats_same(indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
));
expr_formats_same(indoc!(
r"
person : {
firstName : Str,
# comment 1
lastName : Str,
# comment 2
# comment 3
}
person
"
));
expr_formats_same(indoc!(
r"
person : {
firstName : Str,
# comment 1
lastName : Str,
# comment 2
# comment 3
}
person
"
));
expr_formats_to(
indoc!(
r"
person : {
# comment
firstName : Str,
lastName : Str,
}
person
"
),
indoc!(
r"
person : {
# comment
firstName : Str,
lastName : Str,
}
person
"
),
);
expr_formats_to(
indoc!(
r"
person : {
firstName : Str,
lastName : Str,
# comment
}
person
"
),
indoc!(
r"
person : {
firstName : Str,
lastName : Str,
# comment
}
person
"
),
);
expr_formats_to(
indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
),
indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
),
);
expr_formats_to(
indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
),
indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
),
);
expr_formats_to(
indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
),
indoc!(
r"
person : {
firstName : Str,
# comment
lastName : Str,
}
person
"
),
);
}
#[test]
fn record_allow_blank_line_before_and_after_comment() {
expr_formats_same(indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
));
expr_formats_to_with_flags(
indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
),
indoc!(
r#"
person = {
first_name: "first",
# comment 1
last_name: "last",
}
person
"#
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_same_with_flags(
indoc!(
r#"
person = {
first_name: "first",
# comment 1
last_name: "last",
}
person
"#
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_same(indoc!(
r#"
person = {
first_name: "first",
# comment 1
last_name: "last",
}
person
"#
));
expr_formats_same(indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
));
expr_formats_same(indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
));
expr_formats_same(indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
));
expr_formats_same(indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
# comment 2
# comment 3
}
person
"#
));
expr_formats_same(indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
# comment 2
# comment 3
}
person
"#
));
expr_formats_to(
indoc!(
r#"
person = {
# comment
firstName: "first",
lastName: "last",
}
person
"#
),
indoc!(
r#"
person = {
# comment
firstName: "first",
lastName: "last",
}
person
"#
),
);
expr_formats_to(
indoc!(
r#"
person = {
firstName: "first",
lastName: "last",
# comment
}
person
"#
),
indoc!(
r#"
person = {
firstName: "first",
lastName: "last",
# comment
}
person
"#
),
);
expr_formats_to(
indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
),
indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
),
);
expr_formats_to(
indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
),
indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
),
);
expr_formats_to(
indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
),
indoc!(
r#"
person = {
firstName: "first",
# comment 1
lastName: "last",
}
person
"#
),
);
}
#[test]
fn list_allow_blank_line_before_and_after_comment() {
expr_formats_same(indoc!(
r"
list = [
0,
# comment
1,
]
list
"
));
expr_formats_same(indoc!(
r"
list = [
0,
# comment
1,
]
list
"
));
expr_formats_same(indoc!(
r"
list = [
0,
# comment
1,
]
list
"
));
expr_formats_same(indoc!(
r"
list = [
0,
# comment
1,
]
list
"
));
expr_formats_same(indoc!(
r"
list = [
0,
# comment 1
1,
# comment 2
# comment 3
]
list
"
));
expr_formats_same(indoc!(
r"
list = [
0,
# comment 1
1,
# comment 2
# comment 3
]
list
"
));
expr_formats_to(
indoc!(
r"
list = [
# comment
0,
1,
]
list
"
),
indoc!(
r"
list = [
# comment
0,
1,
]
list
"
),
);
expr_formats_to(
indoc!(
r"
list = [
0,
1,
# comment
]
list
"
),
indoc!(
r"
list = [
0,
1,
# comment
]
list
"
),
);
expr_formats_to(
indoc!(
r"
list = [
0,
# comment
1,
]
list
"
),
indoc!(
r"
list = [
0,
# comment
1,
]
list
"
),
);
expr_formats_to(
indoc!(
r"
list = [
0,
# comment
1,
]
list
"
),
indoc!(
r"
list = [
0,
# comment
1,
]
list
"
),
);
expr_formats_to(
indoc!(
r"
list = [
0,
# comment
1,
]
list
"
),
indoc!(
r"
list = [
0,
# comment
1,
]
list
"
),
);
}
#[test]
fn force_space_at_beginning_of_comment() {
expr_formats_to(
indoc!(
r"
#comment
f
"
),
indoc!(
r"
# comment
f
"
),
);
}
#[test]
fn func_def() {
// New syntax
expr_formats_same(indoc!(
r"
f = |x, y|
x
f 4
"
));
// Old syntax
expr_formats_to(
indoc!(
r"
f = \x, y ->
x
f 4
"
),
indoc!(
r"
f = |x, y|
x
f 4
"
),
);
}
#[test]
fn new_line_above_return() {
expr_formats_to(
indoc!(
r#"
f = |x, y|
y = 4
z = 8
x
"string"
"#
),
indoc!(
r#"
f = |x, y|
y = 4
z = 8
x
"string"
"#
),
);
expr_formats_same(indoc!(
r#"
f = |x, y|
a = 3
b = 6
c
"string"
"#
));
}
#[test]
fn basic_string() {
expr_formats_same(indoc!(
r#"
"blah"
"#
));
}
#[test]
fn escaped_unicode_string() {
expr_formats_same(indoc!(
r#"
"unicode: \u(A00A)!"
"#
));
}
#[test]
fn escaped_quote_string() {
expr_formats_same(indoc!(
r#"
"\""
"#
));
}
#[test]
fn empty_block_string() {
expr_formats_same(indoc!(
r#"
"""
"""
"#
));
}
#[test]
fn oneline_empty_block_string() {
expr_formats_to(
indoc!(
r#"
""""""
"#
),
indoc!(
r#"
"""
"""
"#
),
);
}
#[test]
fn basic_block_string() {
expr_formats_to(
indoc!(
r#"
"""griffin"""
"#
),
indoc!(
r#"
"griffin"
"#
),
);
}
#[test]
fn multiline_basic_block_string() {
expr_formats_to(
indoc!(
r#"
"""griffin
harpy"""
"#
),
indoc!(
r#"
"""
griffin
harpy
"""
"#
),
);
}
#[test]
fn newlines_block_string() {
expr_formats_to(
indoc!(
r#"
"""griffin
harpy
phoenix"""
"#
),
indoc!(
r#"
"""
griffin
harpy
phoenix
"""
"#
),
);
}
#[test]
fn quotes_block_string_single_segment() {
expr_formats_same(indoc!(
r#"
"""
"griffin"
"""
"#
));
}
#[test]
fn quotes_block_string() {
expr_formats_same(indoc!(
r#"
"""
"" \""" ""\"
"""
"#
));
}
#[test]
fn zero() {
expr_formats_same(indoc!(
r"
0
"
));
}
#[test]
fn zero_point_zero() {
expr_formats_same(indoc!(
r"
0.0
"
));
}
#[test]
fn int_with_underscores() {
expr_formats_same(indoc!(
r"
1_23_456
"
));
}
#[test]
fn float_with_underscores() {
expr_formats_same(indoc!(
r"
1_23_456.7_89_10
"
));
}
#[test]
fn multi_arg_closure() {
expr_formats_same(indoc!(
r"
|a, b, c| a b c
"
));
}
#[test]
fn destructure_tag_closure() {
// New syntax
expr_formats_same(indoc!(
r"
|Foo a| Foo a
"
));
expr_formats_same(indoc!(
r"
|Foo someVar| Foo someVar
"
));
expr_formats_to_with_flags(
indoc!(
r"
|Foo someVar| Foo someVar
"
),
indoc!(
r"
|Foo some_var| Foo some_var
"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
// Old syntax
expr_formats_to(
indoc!(
r"
\Foo a -> Foo a
"
),
indoc!(
r"
|Foo a| Foo a
"
),
);
expr_formats_to(
indoc!(
r"
\Foo someVar -> Foo someVar
"
),
indoc!(
r"
|Foo someVar| Foo someVar
"
),
);
expr_formats_to_with_flags(
indoc!(
r"
\Foo someVar -> Foo someVar
"
),
indoc!(
r"
|Foo some_var| Foo some_var
"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
}
#[test]
fn destructure_nested_tag_closure() {
expr_formats_same(indoc!(
r"
|Foo (Bar a)| Foo (Bar a)
"
));
expr_formats_to(
indoc!(
r"
\Foo (Bar a) -> Foo (Bar a)
"
),
indoc!(
r"
|Foo (Bar a)| Foo (Bar a)
"
),
);
}
// DEFS
#[test]
fn single_def() {
expr_formats_same(indoc!(
r"
x = 5
42
"
));
}
#[test]
fn two_defs() {
expr_formats_same(indoc!(
r"
x = 5
y = 10
42
"
));
expr_formats_to(
indoc!(
r"
x = 5
y = 10
42
"
),
indoc!(
r"
x = 5
y = 10
42
"
),
);
}
#[test]
fn excess_parens() {
expr_formats_to(
indoc!(
r"
x = (5)
y = ((10))
42
"
),
indoc!(
r"
x = 5
y = 10
42
"
),
);
}
#[test]
fn defs_with_defs() {
expr_formats_same(indoc!(
r"
x =
y = 4
z = 8
w
x
"
));
}
#[test]
fn comment_between_two_defs() {
expr_formats_same(indoc!(
r"
x = 5
# Hello
y = 10
42
"
));
expr_formats_same(indoc!(
r"
x = 5
# Hello
# two comments
y = 10
42
"
));
expr_formats_same(indoc!(
r"
x = 5
# Hello
# two comments
y = 10
# v-- This is the return value
42
"
));
}
#[test]
fn space_between_comments() {
expr_formats_to(
indoc!(
r"
# 9
# A
# B
# C
9
"
),
indoc!(
r"
# 9
# A
# B
# C
9
"
),
);
}
#[test]
fn reduce_space_between_comments() {
expr_formats_to(
indoc!(
r"
# First
# Second
x
"
),
indoc!(
r"
# First
# Second
x
"
),
);
expr_formats_to(
indoc!(
r"
f = |x|
# 1st
# 2nd
x
f 4
"
),
indoc!(
r"
f = |x|
# 1st
# 2nd
x
f 4
"
),
);
}
#[test]
fn doesnt_detect_comment_in_comment() {
expr_formats_same(indoc!(
r"
# One Comment # Still one Comment
9
"
));
}
#[test]
fn parenthetical_def() {
expr_formats_same(indoc!(
r"
(UserId user_id) = 5
y = 10
42
"
));
expr_formats_same(indoc!(
r"
# A
(UserId user_id) = 5
# B
y = 10
42
"
));
}
#[test]
fn record_destructuring() {
expr_formats_same(indoc!(
r"
{ x, y } = 5
{ x: 5 } = { x: 5 }
42
"
));
}
#[test]
fn record_field_destructuring() {
expr_formats_same(indoc!(
r"
when foo is
{ x: 5 } ->
42
"
));
}
#[test]
fn lambda_returns_record() {
// New syntax
expr_formats_same(indoc!(
r"
to_record = |_| {
x: 1,
y: 2,
z: 3,
}
to_record
"
));
expr_formats_same(indoc!(
r"
func = |_|
{ x: 1, y: 2, z: 3 }
func
"
));
expr_formats_same(indoc!(
r"
to_record = |_|
val = 0
{
x: 1,
y: 2,
z: 3,
}
to_record
"
));
expr_formats_to(
indoc!(
r"
to_record = |_|
{
x: 1,
y: 2,
z: 3,
}
to_record
"
),
indoc!(
r"
to_record = |_| {
x: 1,
y: 2,
z: 3,
}
to_record
"
),
);
}
#[test]
fn lambda_returns_list() {
expr_formats_same(indoc!(
r"
to_list = |_| [
1,
2,
3,
]
to_list
"
));
expr_formats_same(indoc!(
r"
func = |_|
[1, 2, 3]
func
"
));
expr_formats_same(indoc!(
r"
to_list = |_|
val = 0
[
1,
2,
3,
]
to_list
"
));
expr_formats_to(
indoc!(
r"
to_list = |_|
[
1,
2,
3,
]
to_list
"
),
indoc!(
r"
to_list = |_| [
1,
2,
3,
]
to_list
"
),
);
}
#[test]
fn multiline_list_func_arg() {
expr_formats_same(indoc!(
r"
result = func arg [
1,
2,
3,
]
result
"
));
expr_formats_to(
indoc!(
r"
result = func arg
[ 1, 2, 3 ]
result
"
),
indoc!(
r"
result = func
arg
[1, 2, 3]
result
"
),
);
expr_formats_to(
indoc!(
r"
result = func arg [
1,
2,
3,
]
result
"
),
indoc!(
r"
result = func arg [
1,
2,
3,
]
result
"
),
);
expr_formats_to(
indoc!(
r"
result = func [
1,
2,
3,
]
arg
result
"
),
indoc!(
r"
result = func
[
1,
2,
3,
]
arg
result
"
),
);
// TODO: do we want to override the user's intent like this?
expr_formats_to(
indoc!(
r"
result = func arg
[
1,
2,
3,
]
result
"
),
indoc!(
r"
result = func arg [
1,
2,
3,
]
result
"
),
);
expr_formats_same(indoc!(
r"
result = func
arg
[
1,
2,
3,
]
result
"
));
}
#[test]
fn multiline_record_func_arg() {
expr_formats_same(indoc!(
r"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"
));
expr_formats_to(
indoc!(
r"
result = func arg
{ x: 1, y: 2, z: 3 }
result
"
),
indoc!(
r"
result = func
arg
{ x: 1, y: 2, z: 3 }
result
"
),
);
expr_formats_to(
indoc!(
r"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"
),
indoc!(
r"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"
),
);
expr_formats_to(
indoc!(
r"
result = func {
x: 1,
y: 2,
z: 3,
}
arg
result
"
),
indoc!(
r"
result = func
{
x: 1,
y: 2,
z: 3,
}
arg
result
"
),
);
// TODO: do we want to override the user's intent like this?
expr_formats_to(
indoc!(
r"
result = func arg
{
x: 1,
y: 2,
z: 3,
}
result
"
),
indoc!(
r"
result = func arg {
x: 1,
y: 2,
z: 3,
}
result
"
),
);
expr_formats_same(indoc!(
r"
result = func
arg
{
x: 1,
y: 2,
z: 3,
}
result
"
));
}
#[test]
fn record_updating() {
expr_formats_same(indoc!(
r"
{ shoes & leftShoe: nothing }
"
));
expr_formats_to_with_flags(
indoc!(
r"
{ shoes & leftShoe: nothing }
"
),
indoc!(
r"
{ shoes & left_shoe: nothing }
"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_same(indoc!(
r"
{ shoes & left_shoe: nothing }
"
));
expr_formats_to(
indoc!(
r"
{ shoes & rightShoe : nothing }
"
),
indoc!(
r"
{ shoes & rightShoe: nothing }
"
),
);
expr_formats_to_with_flags(
indoc!(
r"
{ shoes & rightShoe : nothing }
"
),
indoc!(
r"
{ shoes & right_shoe: nothing }
"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_same(indoc!(
r"
{ shoes &
rightShoe: newRightShoe,
leftShoe: newLeftShoe,
}
"
));
expr_formats_to(
indoc!(
r"
{ shoes
& rightShoe: bareFoot
, leftShoe: bareFoot }
"
),
indoc!(
r"
{ shoes &
rightShoe: bareFoot,
leftShoe: bareFoot,
}
"
),
);
}
#[test]
fn record_builder() {
expr_formats_same(indoc!(
r"
{ shoes <- leftShoe: nothing }
"
));
expr_formats_to(
indoc!(
r"
{ shoes <- rightShoe : nothing }
"
),
indoc!(
r"
{ shoes <- rightShoe: nothing }
"
),
);
expr_formats_to(
indoc!(
r"
{ shoes <- rightShoe : nothing }
"
),
indoc!(
r"
{ shoes <- rightShoe: nothing }
"
),
);
expr_formats_same(indoc!(
r"
{ shoes <-
rightShoe,
leftShoe: newLeftShoe,
}
"
));
expr_formats_same(indoc!(
r"
{ shoes <-
# some comment
rightShoe,
# some other comment
leftShoe: newLeftShoe,
}
"
));
expr_formats_to(
indoc!(
r"
{ shoes
<- rightShoe: bareFoot
, leftShoe: bareFoot }
"
),
indoc!(
r"
{ shoes <-
rightShoe: bareFoot,
leftShoe: bareFoot,
}
"
),
);
expr_formats_to(
indoc!(
r"
{ shoes
<- rightShoe: bareFoot, # some comment
leftShoe: bareFoot }
"
),
indoc!(
r"
{ shoes <-
rightShoe: bareFoot,
# some comment
leftShoe: bareFoot,
}
"
),
);
}
#[test]
fn final_comments_in_records() {
expr_formats_same(indoc!(
r"
{
x: 42,
# comment
}"
));
expr_formats_same(indoc!(
r"
{
x: 42,
# comment
# other comment
}"
));
}
#[test]
fn final_comments_without_comma_in_records() {
expr_formats_to(
indoc!(
r"
{
y: 41,
# comment 1
x: 42 # comment 2
}"
),
indoc!(
r"
{
y: 41,
# comment 1
x: 42,
# comment 2
}"
),
);
}
#[test]
fn multiple_final_comments_without_comma_in_records() {
expr_formats_to(
indoc!(
r"
{
y: 41,
x: 42 # comment 1
# comment 2
}"
),
indoc!(
r"
{
y: 41,
x: 42,
# comment 1
# comment 2
}"
),
);
}
#[test]
fn multiple_final_comments_with_comma_in_records() {
expr_formats_to(
indoc!(
r"
{
y: 41,
x: 42, # comment 1
# comment 2
}"
),
indoc!(
r"
{
y: 41,
x: 42,
# comment 1
# comment 2
}"
),
);
}
#[test]
fn trailing_comma_in_record_annotation() {
expr_formats_to(
indoc!(
r"
f: { y : Int *,
x : Int * ,
}
f"
),
indoc!(
r"
f : {
y : Int *,
x : Int *,
}
f"
),
);
}
#[test]
fn trailing_comma_in_record_annotation_same() {
expr_formats_same(indoc!(
r"
f : {
y : Int *,
x : Int *,
}
f
"
));
expr_formats_to(
indoc!(
r"
f :
{
y : Int *,
x : Int *,
}
f
"
),
indoc!(
r"
f : {
y : Int *,
x : Int *,
}
f
"
),
);
}
#[test]
fn multiline_type_definition() {
expr_formats_same(indoc!(
r"
f :
Int *
f"
));
}
#[test]
fn multiline_empty_record_type_definition() {
expr_formats_same(indoc!(
r"
f :
{}
f
"
));
expr_formats_to(
indoc!(
r"
f :
{
}
f
"
),
indoc!(
r"
f : {
}
f
"
),
);
}
#[test]
fn type_definition_comment_after_colon() {
expr_formats_to(
indoc!(
r"
f : # comment
{}
f"
),
indoc!(
r"
f :
# comment
{}
f"
),
);
}
#[test]
fn type_definition_add_space_around_optional_record() {
expr_formats_to(
indoc!(
r"
f : { a ??Str }
f"
),
indoc!(
r"
f : { a ?? Str }
f"
),
);
expr_formats_to(
indoc!(
r"
f : {
a ??Str,
}
f"
),
indoc!(
r"
f : {
a ?? Str,
}
f"
),
);
}
#[test]
#[ignore]
fn final_comment_in_empty_record_type_definition() {
expr_formats_to(
indoc!(
r"
f :
{ # comment
}
f"
),
indoc!(
r"
f : {
# comment
}
f"
),
);
}
#[test]
fn multiline_curly_brace_type() {
expr_formats_same(indoc!(
r"
x : {
a : Int,
}
x
"
));
expr_formats_same(indoc!(
r"
x :
{ a : Int }
x
"
));
}
#[test]
fn multiline_brace_type() {
expr_formats_same(indoc!(
r"
x : [
Int,
]
x
"
));
expr_formats_same(indoc!(
r"
x :
[Int]
x
"
));
}
#[test]
fn multiline_fn_signature() {
expr_formats_same(indoc!(
r"
foo :
Str,
U64
-> Bool
foo
"
));
expr_formats_same(indoc!(
r"
foo :
Str, Int, U64 -> Bool
foo
"
));
expr_formats_to(
indoc!(
r"
foo :
Str,
U64 -> Bool
foo
"
),
indoc!(
r"
foo :
Str,
U64
-> Bool
foo
"
),
);
expr_formats_to(
indoc!(
r"
foo :
Str,
U64
-> Bool
foo
"
),
indoc!(
r"
foo :
Str,
U64
-> Bool
foo
"
),
);
expr_formats_to(
indoc!(
r"
foo :
Str, U64 -> Bool
foo
"
),
indoc!(
r"
foo :
Str, U64 -> Bool
foo
"
),
);
}
#[test]
fn final_comment_record_annotation() {
expr_formats_to(
indoc!(
r"
f :
{
x: Int * # comment 1
,
# comment 2
}
f"
),
indoc!(
r"
f : {
x : Int *, # comment 1
# comment 2
}
f"
),
);
expr_formats_to_with_flags(
indoc!(
r"
f :
{
someField: Int * # comment 1
,
# comment 2
}
f"
),
indoc!(
r"
f : {
some_field : Int *, # comment 1
# comment 2
}
f"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_to_with_flags(
indoc!(
r"
f :
{
someField ?? Int * # comment 1
,
# comment 2
}
f"
),
indoc!(
r"
f : {
some_field ?? Int *, # comment 1
# comment 2
}
f"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
}
#[test]
fn def_closure() {
// Starting from new syntax
expr_formats_same(indoc!(
r"
identity = |a| a
identity 42
"
));
expr_formats_same(indoc!(
r"
identity = |a|
a
identity 44
"
));
expr_formats_same(indoc!(
r"
identity = |a| a
# Hello
identity 40
"
));
expr_formats_to(
indoc!(
r"
identity = |a
| a
identity 41
"
),
indoc!(
r"
identity = |a| a
identity 41
"
),
);
expr_formats_to(
indoc!(
r"
identity = |a
|
a + b
identity 4010
"
),
indoc!(
r"
identity = |a|
a + b
identity 4010
"
),
);
expr_formats_same(indoc!(
r"
identity = |a, b| a
identity 43
"
));
// expr_formats_same(indoc!(
// r"
// identity =
// \{
// x,
// y
// }
// -> a
//
// identity 43
// "
// ));
expr_formats_same(indoc!(
r"
identity = |a,
b,
# it's c!!
c
| a
identity 43
"
));
// Starting from old syntax
expr_formats_to(
indoc!(
r"
identity = \a -> a
identity 42
"
),
indoc!(
r"
identity = |a| a
identity 42
"
),
);
expr_formats_to(
indoc!(
r"
identity = \a ->
a
identity 44
"
),
indoc!(
r"
identity = |a|
a
identity 44
"
),
);
expr_formats_to(
indoc!(
r"
identity = \a -> a
# Hello
identity 40
"
),
indoc!(
r"
identity = |a| a
# Hello
identity 40
"
),
);
expr_formats_to(
indoc!(
r"
identity = \a
-> a
identity 41
"
),
indoc!(
r"
identity = |a| a
identity 41
"
),
);
expr_formats_to(
indoc!(
r"
identity = \a
->
a + b
identity 4010
"
),
indoc!(
r"
identity = |a|
a + b
identity 4010
"
),
);
expr_formats_to(
indoc!(
r"
identity = \a, b -> a
identity 43
"
),
indoc!(
r"
identity = |a, b| a
identity 43
"
),
);
expr_formats_to(
indoc!(
r"
identity =
|{
x,
y
}| a
identity 43
"
),
indoc!(
r"
identity =
|{ x, y }| a
identity 43
"
),
);
expr_formats_to(
indoc!(
r"
identity =
\{
x,
y
} -> a
identity 43
"
),
indoc!(
r"
identity =
|{ x, y }| a
identity 43
"
),
);
expr_formats_to(
indoc!(
r"
identity = \a,
b,
# it's c!!
c
-> a
identity 43
"
),
indoc!(
r"
identity = |a,
b,
# it's c!!
c
| a
identity 43
"
),
);
}
#[test]
fn closure_multiline_pattern() {
expr_formats_same(indoc!(
r"
identity = |a,
b,
# it's c!!
c
| a
identity 43
"
));
expr_formats_to(
indoc!(
r"
identity = \a,
b,
# it's c!!
c
-> a
identity 43
"
),
indoc!(
r"
identity = |a,
b,
# it's c!!
c
| a
identity 43
"
),
);
}
// LIST
#[test]
fn empty_list() {
expr_formats_same("[]");
expr_formats_to("[ ]", "[]");
}
#[test]
fn one_item_list() {
expr_formats_same(indoc!("[4]"));
expr_formats_to(indoc!("[ 4 ]"), indoc!("[4]"));
}
#[test]
fn two_item_list() {
expr_formats_same(indoc!("[7, 8]"));
expr_formats_to(indoc!("[ 7 , 8 ]"), indoc!("[7, 8]"));
}
#[test]
fn multi_line_list() {
expr_formats_same(indoc!(
r"
[
7,
8,
9,
]
"
));
expr_formats_to(
indoc!(
r"
[ 17
, 18
, 19
]
"
),
indoc!(
r"
[
17,
18,
19,
]
"
),
);
expr_formats_to(
indoc!(
r"
[ 27
, 28
, 29
]
"
),
indoc!(
r"
[
27,
28,
29,
]
"
),
);
expr_formats_to(
indoc!(
r"
[
157, 158,
159
]
"
),
indoc!(
r"
[
157,
158,
159,
]
"
),
);
expr_formats_to(
indoc!(
r"
[
557, 648,
759, 837
]
"
),
indoc!(
r"
[
557,
648,
759,
837,
]
"
),
);
expr_formats_to(
indoc!(
r"
[
257, 358,
# Hey!
459
]
"
),
indoc!(
r"
[
257,
358,
# Hey!
459,
]
"
),
);
expr_formats_to(
indoc!(
r"
[
# Thirty Seven
37
# Thirty Eight
, 38
, 39
]
"
),
indoc!(
r"
[
# Thirty Seven
37,
# Thirty Eight
38,
39,
]
"
),
);
expr_formats_to(
indoc!(
r"
[ # 47!
# Top 47
47
# Bottom 47
# Top 48
, 48
# Bottom 48
# Top 49
, 49
# Bottom 49
# 49!
]
"
),
indoc!(
r"
[
# 47!
# Top 47
47,
# Bottom 47
# Top 48
48,
# Bottom 48
# Top 49
49,
# Bottom 49
# 49!
]
"
),
);
}
#[test]
fn ending_comments_in_list() {
expr_formats_to(
indoc!(
r"
[ # Top 49
49
# Bottom 49
,
# 49!
]
"
),
indoc!(
r"
[
# Top 49
49,
# Bottom 49
# 49!
]
"
),
);
}
#[test]
fn multi_line_list_def() {
expr_formats_same(indoc!(
r"
l = [
1,
2,
]
l
"
));
expr_formats_same(indoc!(
r"
l =
[1, 2]
l
"
));
expr_formats_to(
indoc!(
r"
l =
[
1,
2,
]
l
"
),
indoc!(
r"
l = [
1,
2,
]
l
"
),
);
expr_formats_to(
indoc!(
r"
results = [
Ok 4,
Ok 5
]
allOks results
"
),
indoc!(
r"
results = [
Ok 4,
Ok 5,
]
allOks results
"
),
);
expr_formats_to(
indoc!(
r"
results =
# Let's count past 6
[
Ok 6,
Err CountError
]
allOks results
"
),
indoc!(
r"
results =
# Let's count past 6
[
Ok 6,
Err CountError,
]
allOks results
"
),
);
}
// RECORD LITERALS
#[test]
fn empty_record() {
expr_formats_same("{}");
expr_formats_to("{ }", "{}");
}
#[test]
fn empty_record_patterns() {
// New syntax
expr_formats_to(
indoc!(
r#"
f = |{ }| "Hello World"
f
"#
),
indoc!(
r#"
f = |{}| "Hello World"
f
"#
),
);
expr_formats_to(
indoc!(
r"
f = |a, b| { }
f
"
),
indoc!(
r"
f = |a, b| {}
f
"
),
);
// Old syntax
expr_formats_to(
indoc!(
r#"
f = \{ } -> "Hello World"
f
"#
),
indoc!(
r#"
f = |{}| "Hello World"
f
"#
),
);
expr_formats_to(
indoc!(
r"
f = \a, b -> { }
f
"
),
indoc!(
r"
f = |a, b| {}
f
"
),
);
}
#[test]
#[ignore]
fn empty_record_with_comment() {
expr_formats_same(indoc!(
r"
{
# comment
}"
));
}
#[test]
#[ignore]
fn empty_record_with_newline() {
expr_formats_to(
indoc!(
r"
{
}"
),
"{}",
);
}
#[test]
fn one_field() {
expr_formats_same("{ x: 4 }");
}
#[test]
fn two_fields() {
expr_formats_same("{ x: 4, y: 42 }");
}
#[test]
fn two_fields_newline() {
expr_formats_same(indoc!(
r"
{
x: 4,
y: 42,
}
"
));
}
#[test]
fn multi_line_record_def() {
expr_formats_same(indoc!(
r"
pos = {
x: 4,
y: 11,
z: 16,
}
pos
"
));
expr_formats_same(indoc!(
r"
pos =
{ x: 4, y: 11, z: 16 }
pos
"
));
expr_formats_same(indoc!(
r"
my_def =
list = [
a,
b,
]
{
c,
d,
}
my_def
"
));
expr_formats_to(
indoc!(
r"
pos =
{
x: 4,
y: 11,
z: 16,
}
pos
"
),
indoc!(
r"
pos = {
x: 4,
y: 11,
z: 16,
}
pos
"
),
);
expr_formats_to(
indoc!(
r"
pos = {
x: 5,
y: 10,
}
pos
"
),
indoc!(
r"
pos = {
x: 5,
y: 10,
}
pos
"
),
);
}
#[test]
fn two_fields_center_newline() {
expr_formats_to(
indoc!(
r"
{ x: 4,
y: 42
}
"
),
indoc!(
r"
{
x: 4,
y: 42,
}
"
),
);
}
#[test]
fn one_unnamed_field() {
expr_formats_same(indoc!(
r"
foo = 4
{ foo }
"
));
}
// IF
#[test]
fn single_line_if() {
expr_formats_same(indoc!(
r"
if foo bar then a b c else d e f
"
));
expr_formats_same(indoc!(
r"
if foo (a b c) then a b c else d e f
"
));
}
#[test]
fn multi_line_if_condition() {
expr_formats_same(indoc!(
r"
if
waterWillBoil pressure temperature
then
turnOnAc
else
identity
"
));
}
#[test]
fn multi_line_if_condition_with_spaces() {
expr_formats_to(
indoc!(
r"
if
willBoil home water
then
|_| leave
else
identity
"
),
indoc!(
r"
if
willBoil home water
then
|_| leave
else
identity
"
),
);
}
#[test]
fn multi_line_if_condition_with_multi_line_expr_1() {
expr_formats_same(indoc!(
r"
if
snowWillFall
pressure
temperature
then
bundleUp
else
identity
"
));
}
#[test]
fn multi_line_if_condition_with_multi_line_expr_2() {
expr_formats_same(indoc!(
r#"
if
1
== 2
then
"yes"
else
"no"
"#
));
}
#[test]
fn if_removes_newlines_from_else() {
expr_formats_to(
indoc!(
r"
if
isPrime 8
then
nothing
else
# C
# D
# E
# F
just (div 1 8)
"
),
indoc!(
r"
if
isPrime 8
then
nothing
else
# C
# D
# E
# F
just (div 1 8)
"
),
);
}
#[test]
fn if_removes_newlines_from_then() {
expr_formats_to(
indoc!(
r"
if
isPrime 9
then
# EE
# FF
nothing
# GG
else
just (div 1 9)
"
),
indoc!(
r"
if
isPrime 9
then
# EE
# FF
nothing
# GG
else
just (div 1 9)
"
),
);
}
#[test]
fn if_removes_newlines_from_condition() {
expr_formats_to(
indoc!(
r"
if
# Is
# It
isPrime 10
# Prime?
then
nothing
else
just (div 1 10)
"
),
indoc!(
r"
if
# Is
# It
isPrime 10
# Prime?
then
nothing
else
just (div 1 10)
"
),
);
}
#[test]
fn multi_line_if() {
expr_formats_to(
indoc!(
r"
if lessThan four five then
four
else
five
"
),
indoc!(
r"
if lessThan four five then
four
else
five
"
),
);
expr_formats_to(
indoc!(
r"
if lessThan three four then
three
else
four
"
),
indoc!(
r"
if lessThan three four then
three
else
four
"
),
);
expr_formats_same(indoc!(
r"
if foo bar then
a b c
else
d e f
"
));
}
#[test]
fn early_return_else() {
expr_formats_to(
indoc!(
r"
if foo then
bar
else
baz
"
),
indoc!(
r"
if foo then
bar
else
baz
"
),
);
expr_formats_to(
indoc!(
r"
if thing then
whatever
else
too close
"
),
indoc!(
r"
if thing then
whatever
else
too close
"
),
);
expr_formats_to(
indoc!(
r"
if isGrowing plant then
LetBe
else
Water
"
),
indoc!(
r"
if isGrowing plant then
LetBe
else
Water
"
),
);
}
#[test]
fn multi_line_application() {
expr_formats_same(indoc!(
r"
combine
peanutButter
chocolate
"
));
expr_formats_same(indoc!(
r"
combine(
peanutButter,
chocolate,
)
"
));
expr_formats_to_with_flags(
indoc!(
r"
combine
peanutButter
chocolate
"
),
indoc!(
r"
combine(
peanut_butter,
chocolate,
)
"
),
MigrationFlags {
snakify: true,
parens_and_commas: true,
},
);
}
#[test]
fn zero_arg_application_with_parens() {
expr_formats_same(indoc!(
r"
a()
"
));
}
#[test]
fn try_then_application_with_parens() {
expr_formats_same(indoc!(
r"
try something!(arg)
"
));
}
#[test]
fn dbg_then_application_with_parens() {
expr_formats_same(indoc!(
r"
dbg something!(arg)
"
));
}
#[test]
fn single_line_application_with_parens() {
expr_formats_same(indoc!(
r"
combine(peanut_butter, chocolate)
"
));
}
#[test]
fn pipe_pnc_application_with_comment_no_args() {
expr_formats_same(indoc!(
r"
combine( # This is a comment
)
"
));
}
#[test]
fn single_line_nested_application_with_parens() {
expr_formats_same(indoc!(
r"
combine(combine(peanut_butter, honey), chocolate)
"
));
}
#[test]
fn multi_line_application_with_parens() {
expr_formats_same(indoc!(
r"
combine(
peanut_butter,
chocolate,
)
"
));
}
#[test]
fn multi_line_nested_application_with_parens() {
expr_formats_same(indoc!(
r"
combine(
combine(peanut_butter, honey),
chocolate,
)
"
));
}
#[test]
fn partial_multi_line_application() {
expr_formats_to(
indoc!(
r"
mix vodka
tonic
"
),
indoc!(
r"
mix
vodka
tonic
"
),
);
expr_formats_to(
indoc!(
r"
f
a b c
"
),
indoc!(
r"
f
a
b
c
"
),
);
}
// WHEN
#[test]
fn integer_when() {
expr_formats_same(indoc!(
r"
when b is
1 ->
1
_ ->
2
"
));
}
#[test]
fn integer_when_with_space() {
expr_formats_to(
indoc!(
r"
when year is
1999 ->
1
_ ->
0
"
),
indoc!(
r"
when year is
1999 ->
1
_ ->
0
"
),
);
}
#[test]
fn when_with_comments() {
expr_formats_same(indoc!(
r"
when b is
# look at cases
1 -> # when 1
1
# important
# fall through
_ ->
# case 2
# more comment
2
"
));
}
#[test]
fn when_with_integer_comments() {
expr_formats_same(indoc!(
r#"
when 0 is
1 # comment
| 2 -> "a"
_ -> "b"
"#
));
}
#[test]
fn nested_when() {
expr_formats_same(indoc!(
r"
when b is
_ ->
when c is
_ ->
1
"
));
}
#[test]
fn def_when() {
expr_formats_same(indoc!(
r"
my_long_function_name = |x|
when b is
1 | 2 ->
when c is
6 | 7 ->
8
3 | 4 ->
5
123
"
));
expr_formats_to(
indoc!(
r"
my_long_function_name = \x ->
when b is
1 | 2 ->
when c is
6 | 7 ->
8
3 | 4 ->
5
123
"
),
indoc!(
r"
my_long_function_name = |x|
when b is
1 | 2 ->
when c is
6 | 7 ->
8
3 | 4 ->
5
123
"
),
);
}
#[test]
#[ignore] // TODO: reformat when-in-function-body with extra newline
fn def_when_with_python_indentation() {
expr_formats_to(
// vvv Currently this input formats to _itself_ :( vvv
// Instead, if the body of the `when` is multiline (the overwhelmingly common case)
// we want to make sure the `when` is at the beginning of the line, inserting
// a newline if necessary.
indoc!(
r"
myLongFunctionName = \x -> when b is
1 | 2 ->
when c is
6 | 7 ->
8
3 | 4 ->
5
123
"
),
indoc!(
r"
myLongFunctionName = \x ->
when b is
1 | 2 ->
when c is
6 | 7 ->
8
3 | 4 ->
5
123
"
),
);
}
#[test]
fn when_with_alternatives_1() {
expr_formats_same(indoc!(
r"
when b is
1 | 2 ->
when c is
6 | 7 ->
8
3 | 4 ->
5
"
));
}
#[test]
fn when_with_alternatives_2() {
expr_formats_same(indoc!(
r"
when b is
# a comment here
1 | 2 ->
# a comment there
1
"
));
}
#[test]
fn when_with_alternatives_3() {
expr_formats_to(
indoc!(
r"
when b is
1 | 2 |3 ->
1
"
),
indoc!(
r"
when b is
1 | 2 | 3 ->
1
"
),
);
}
#[test]
fn when_with_alternatives_4() {
expr_formats_to(
indoc!(
r"
when b is
1 | 2 |
3
->
4
5 | 6 | 7 ->
8
9
| 10 -> 11
12 | 13 ->
when c is
14 | 15 -> 16
17
| 18 -> 19
20 -> 21
"
),
indoc!(
r"
when b is
1
| 2
| 3 ->
4
5 | 6 | 7 ->
8
9
| 10 -> 11
12 | 13 ->
when c is
14 | 15 -> 16
17
| 18 -> 19
20 -> 21
"
),
);
}
#[test]
#[ignore]
fn with_multiline_pattern_indentation() {
expr_formats_to(
indoc!(
r"
when b is 3->4
9
|8->9
"
),
indoc!(
r"
when b is
3 -> 4
9
| 8 -> 9
"
),
);
}
#[test]
fn multi_line_when_condition_1() {
expr_formats_same(indoc!(
r"
when
complexFunction a b c
is
1 ->
Nothing
_ ->
Just True
"
));
}
#[test]
fn multi_line_when_condition_2() {
expr_formats_same(indoc!(
r"
when
# this is quite complicated
complexFunction a b c
# Watch out
is
Complex x y ->
simplify x y
Simple z ->
z
"
));
}
#[test]
fn multi_line_when_condition_2_pnc() {
expr_formats_same(indoc!(
r"
when
# this is quite complicated
complexFunction(a, b, c)
# Watch out
is
Complex(x, y) ->
simplify(x, y)
Simple(z) ->
z
"
));
}
#[test]
fn anthony_testing() {
expr_formats_same(indoc!(
r"
when alter (Ok value) is
Ok newValue ->
bucket = listGetUnsafe buckets bucketIndex
newData = List.set data (Num.toU64 bucket.dataIndex) (key, newValue)
@Dict { buckets, data: newData, maxBucketCapacity, maxLoadFactor, shifts }
Err Missing ->
removeBucket (@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }) bucketIndex
"
));
}
#[test]
fn multi_line_when_condition_3() {
expr_formats_to(
indoc!(
r"
x = 2
y = 3
when 1
+ 1 is
2 ->
x
_ ->
y
"
),
indoc!(
r"
x = 2
y = 3
when
1
+ 1
is
2 ->
x
_ ->
y
"
),
);
}
#[test]
fn multi_line_when_condition_4() {
expr_formats_to(
indoc!(
r"
x = 2
y = 3
when 2
+ 2
is
4 ->
x
_ ->
y
"
),
indoc!(
r"
x = 2
y = 3
when
2
+ 2
is
4 ->
x
_ ->
y
"
),
);
}
#[test]
fn multi_line_when_branch() {
expr_formats_to(
indoc!(
r#"
when x is
Foo -> bar
"arg1" "arg2"
Bar -> 2
"#
),
indoc!(
r#"
when x is
Foo ->
bar
"arg1"
"arg2"
Bar -> 2
"#
),
);
}
#[test]
fn single_line_when_patterns() {
expr_formats_same(indoc!(
r"
when x is
Foo -> 1
Bar -> 2
"
));
expr_formats_same(indoc!(
r"
when x is
Foo -> 1
Bar ->
2
"
));
expr_formats_to(
indoc!(
r"
when x is
Foo -> 1
Bar ->
2
"
),
indoc!(
r"
when x is
Foo -> 1
Bar ->
2
"
),
);
expr_formats_to(
indoc!(
r"
when x is
Foo -> 1
Bar -> 2
"
),
indoc!(
r"
when x is
Foo -> 1
Bar -> 2
"
),
);
}
#[test]
fn when_with_single_quote_char() {
expr_formats_same(indoc!(
r"
when x is
'0' -> 0
'1' -> 1
"
));
}
// NEWLINES
#[test]
fn multiple_blank_lines_collapse_to_one() {
expr_formats_to(
indoc!(
r"
x = 5
y = 10
42
"
),
indoc!(
r"
x = 5
y = 10
42
"
),
);
}
#[test]
fn def_returning_closure() {
expr_formats_same(indoc!(
r"
f = |x| x
g = |x| x
|x|
a = f x
b = f x
x
"
));
expr_formats_to(
indoc!(
r"
f = \x -> x
g = \x -> x
\x ->
a = f x
b = f x
x
"
),
indoc!(
r"
f = |x| x
g = |x| x
|x|
a = f x
b = f x
x
"
),
);
}
#[test]
fn inner_def_with_triple_newline_before() {
// The triple newline used to cause the code in add_spaces to not indent the next line,
// which of course is not the same tree (and nor does it parse)
expr_formats_to(
indoc!(
r"
|x|
m = 2
m1 = insert m n powerOf10
42
"
),
indoc!(
r"
|x|
m = 2
m1 = insert m n powerOf10
42
"
),
);
}
#[test]
fn when_guard() {
expr_formats_same(indoc!(
r"
when maybeScore is
Just score if score > 21 ->
win
_ ->
nextRound
"
));
}
#[test]
fn when_guard_using_function() {
expr_formats_same(indoc!(
r"
when authenticationResponse is
Ok user if hasPermission user ->
loadPage route user
Ok user ->
PageNotFound
Err _ ->
ErrorPage
"
));
}
// ACCESSOR
#[test]
fn accessor() {
expr_formats_same(indoc!(
r"
.id
"
));
expr_formats_same(indoc!(
r"
user.name
"
));
expr_formats_same(indoc!(
r"
(getUser userId users).name
"
));
}
// PRECEDENCE CONFLICT
#[test]
fn precedence_conflict() {
expr_formats_same(indoc!(
r"
if True == False == True then
False
else
True
"
));
}
#[test]
fn multi_line_precedence_conflict_1() {
expr_formats_to(
indoc!(
r"
if True
== False == True
then
False
else
True
"
),
indoc!(
r"
if
True
== False
== True
then
False
else
True
"
),
);
}
#[test]
fn multi_line_precedence_conflict_2() {
expr_formats_to(
indoc!(
r#"
if False
== False == False then
"true"
else
"false"
"#
),
indoc!(
r#"
if
False
== False
== False
then
"true"
else
"false"
"#
),
);
}
#[test]
fn precedence_conflict_functions() {
expr_formats_same(indoc!(
r"
when f x == g y == h z is
True ->
Ok 1
False ->
Err 2
"
));
}
#[test]
fn binop_parens() {
expr_formats_same(indoc!(
r#"
if 4 == (6 ^ 6 ^ 7 ^ 8) then
"Hard to believe"
else
"Naturally"
"#
));
expr_formats_same(indoc!(
r#"
if 5 == 1 ^ 1 ^ 1 ^ 1 then
"Not buying it"
else
"True"
"#
));
expr_formats_to(
indoc!(
r#"
if (1 == 1)
&& (2 == 1) && (3 == 2) then
"true"
else
"false"
"#
),
indoc!(
r#"
if
(1 == 1)
and (2 == 1)
and (3 == 2)
then
"true"
else
"false"
"#
),
);
}
#[test]
fn multiline_binop_with_comments() {
expr_formats_to(
indoc!(
r"
x = 1
+ 1 # comment 1
- 1 # comment 2
* 1 # comment 3
x
"
),
indoc!(
r"
x =
1
+ 1 # comment 1
- 1 # comment 2
* 1 # comment 3
x
"
),
);
expr_formats_to(
indoc!(
r"
x = 1
+ 1 # comment 1
* 1 # comment 2
x
"
),
indoc!(
r"
x =
1
+ 1 # comment 1
* 1 # comment 2
x
"
),
);
expr_formats_to(
indoc!(
r"
x = 1
+ 1 # comment
x
"
),
indoc!(
r"
x =
1
+ 1 # comment
x
"
),
);
expr_formats_to(
indoc!(
r"
x = 1
* 1
+ 1 # comment
x
"
),
indoc!(
r"
x =
1
* 1
+ 1 # comment
x
"
),
);
expr_formats_to(
indoc!(
r"
x = 1
- 1
* 1
+ 1
x
"
),
indoc!(
r"
x =
1
- 1
* 1
+ 1
x
"
),
);
}
#[test]
fn multiline_binop_if_with_comments() {
expr_formats_same(indoc!(
r"
if
x
+ 1 # comment 1
> 0 # comment 2
then
y
* 2 # comment 3
< 1 # comment 4
else
42
"
));
}
#[test]
fn multiline_binop_when_with_comments() {
expr_formats_to(
indoc!(
r#"
when
x
+ 1 # comment 1
> 0 # comment 2
is
y ->
3
* 2 # comment 3
< 1 # comment 4
z ->
4
/ 5 # comment 5
< 1 # comment 6
46 # first pattern comment
| 95 # alternative comment 1
| 126 # alternative comment 2
| 150 -> # This comment came after the ->
# This comment is for the expr
foo bar
|> Result.withDefault "" # one last comment
_ ->
42
"#
),
indoc!(
r#"
when
x
+ 1 # comment 1
> 0 # comment 2
is
y ->
3
* 2 # comment 3
< 1 # comment 4
z ->
4
/ 5 # comment 5
< 1 # comment 6
46 # first pattern comment
| 95 # alternative comment 1
| 126 # alternative comment 2
| 150 -> # This comment came after the ->
# This comment is for the expr
foo bar
|> Result.withDefault "" # one last comment
_ ->
42
"#
),
);
}
#[test]
fn precedence_conflict_greater_than() {
expr_formats_same(indoc!(
r"
3 > 4 > 10
"
));
}
#[test]
fn precedence_conflict_greater_than_and_less_than() {
expr_formats_same(indoc!(
r"
1 < 4 > 1
"
));
}
#[test]
fn binop_if() {
expr_formats_same(indoc!(
r"
5 * (if x > 0 then 1 else 2)
"
));
}
// UNARY OP
#[test]
fn unary_op() {
expr_formats_same(indoc!(
r"
y = -4
!x
"
));
}
#[test]
fn unary_call_parens() {
expr_formats_same(indoc!(
r"
!(f 1)
"
));
}
#[test]
fn unary_call_no_parens() {
// TIL: Negating a function "does what you might expect"... which is cool!
expr_formats_same(indoc!(
r"
!f 1
"
));
}
// BINARY OP
#[test]
fn binary_op() {
expr_formats_same(indoc!(
r"
1 == 1
"
));
}
#[test]
fn binary_op_with_spaces() {
expr_formats_to(
indoc!(
r"
2 != 3
"
),
indoc!(
r"
2 != 3
"
),
);
}
#[test]
fn multi_line_binary_op_and() {
expr_formats_to(
indoc!(
r"
is_last
&& is_empty
&& is_loaded
"
),
indoc!(
r"
is_last
and is_empty
and is_loaded
"
),
);
expr_formats_same(indoc!(
r"
is_last
and is_empty
and is_loaded
"
));
}
#[test]
fn multi_line_binary_op_or() {
expr_formats_to(
indoc!(
r"
is_last
|| is_empty
|| is_loaded
"
),
indoc!(
r"
is_last
or is_empty
or is_loaded
"
),
);
expr_formats_same(indoc!(
r"
is_last
or is_empty
or is_loaded
"
));
}
#[test]
fn multi_line_binary_op_symbol() {
expr_formats_to(
indoc!(
r"
x = 1
< 2
f x
"
),
indoc!(
r"
x =
1
< 2
f x
"
),
);
}
#[test]
fn multi_line_binary_op_with_comments() {
expr_formats_to(
indoc!(
r"
1
* 2
/ 3
// 4
"
),
indoc!(
r"
1
* 2
/ 3
// 4
"
),
);
}
#[test]
fn partial_multi_line_binary_op_1() {
expr_formats_to(
indoc!(
r"
2 % 3
// 5
+ 7
"
),
indoc!(
r"
2
% 3
// 5
+ 7
"
),
);
}
#[test]
fn partial_multi_line_binary_op_2() {
expr_formats_to(
indoc!(
r"
is_green_light
&& is_red_light && is_yellow_light
"
),
indoc!(
r"
is_green_light
and is_red_light
and is_yellow_light
"
),
);
}
#[test]
fn pipline_op_with_apply() {
expr_formats_same(indoc!(
r"
output
|> List.set (offset + 0) b
|> List.set (offset + 1) a
"
));
}
#[test]
fn apply_lambda() {
expr_formats_same(indoc!(
r"
List.map
xs
(|i|
i + length)
"
));
expr_formats_to(
indoc!(
r"
List.map
xs
(\i ->
i + length)
"
),
indoc!(
r"
List.map
xs
(|i|
i + length)
"
),
);
}
#[test]
fn pipline_apply_lambda_1() {
expr_formats_same(indoc!(
r"
shout
|> List.map
xs
(|i| i)
"
));
}
#[test]
fn pipline_apply_lambda_2() {
expr_formats_same(indoc!(
r"
shout
|> List.map
xs
(|i| i)
|> List.join
"
));
}
#[test]
fn comment_between_multiline_ann_args() {
expr_formats_same(indoc!(
r"
blah :
(Str -> Str)
-> Str
42
"
))
}
#[test]
fn pipeline_apply_lambda_multiline() {
expr_formats_same(indoc!(
r"
example = |model|
model
|> withModel
(|result|
when result is
Err _ ->
Err {}
Ok val ->
Ok {}
)
example
"
));
expr_formats_to(
indoc!(
r"
example = |model|
model
|> withModel
(|result|
when result is
Err _ ->
Err {}
Ok val ->
Ok {}
)
example
"
),
indoc!(
r"
example = |model|
model
|> withModel
(|result|
when result is
Err _ ->
Err {}
Ok val ->
Ok {}
)
example
"
),
);
}
#[test]
fn pnc_apply_with_try_suffix_after_fn() {
expr_formats_to_with_flags(
indoc!("some_fn?(arg1, arg2)"),
indoc!("some_fn(arg1, arg2)?"),
MigrationFlags {
snakify: true,
parens_and_commas: true,
},
);
}
#[test]
fn ws_apply_with_try_suffix_after_fn() {
expr_formats_to_with_flags(
indoc!("some_fn? arg1 arg2"),
indoc!("some_fn(arg1, arg2)?"),
MigrationFlags {
snakify: true,
parens_and_commas: true,
},
);
}
#[test]
fn func_call_trailing_multiline_lambda() {
// New syntax
expr_formats_same(indoc!(
r"
list = List.map [1, 2, 3] |x|
x + 1
list
"
));
// Old Syntax
expr_formats_to(
indoc!(
r"
list = List.map [1, 2, 3] \x ->
x + 1
list
"
),
indoc!(
r"
list = List.map [1, 2, 3] |x|
x + 1
list
"
),
);
}
// MODULES
#[test]
fn single_line_module() {
module_formats_same(indoc!(
r"
module []"
));
}
#[test]
fn defs_with_trailing_comment() {
// TODO: make the formatter add a space between '42' and # below:
module_formats_to(
indoc!(
r"
module []
a = 42 # Yay greetings"
),
indoc!(
r"
module []
a = 42 # Yay greetings
"
),
);
}
#[test]
fn module_exposing() {
module_formats_same(indoc!(
r"
module [Bar, Baz, a, b]"
));
}
#[test]
fn module_exposing_multiline() {
module_formats_same(indoc!(
r"
module [
Stuff,
Things,
somethingElse,
]
import Blah
import Baz exposing [stuff, things]"
));
}
#[test]
fn old_style_app_header_is_upgraded() {
module_formats_to(
indoc!(
"
app \"test\"
packages {
pf: \"platform/main.roc\"
}
provides [main] to pf
"
),
indoc!(
"
app [main] {
pf: platform \"platform/main.roc\",
}
"
),
);
}
#[test]
fn old_style_package_header_is_upgraded() {
module_formats_to(
indoc!(
"
package \"csv\"
exposes [Csv]
packages {
parser: \"parser/main.roc\"
}
"
),
indoc!(
"
package [Csv] {
parser: \"parser/main.roc\",
}
"
),
);
}
#[test]
fn single_line_app() {
module_formats_same(indoc!(
r#"
app [main] { pf: platform "platform/main.roc" }"#
));
}
#[test]
fn single_line_platform() {
module_formats_same(
"platform \"folkertdev/foo\" \
requires { Model, Msg } { main : Task {} [] } \
exposes [] \
packages {} \
imports [] \
provides [main_for_host]",
);
}
#[test]
fn module_defs_with_comments() {
module_formats_to(
&format!(
indoc!(
r#"
module []
# comment 1{space}
def = "" # comment 2{space}
# comment 3{space}
"#
),
space = " "
),
indoc!(
r#"
module []
# comment 1
def = "" # comment 2
# comment 3
"#
),
);
}
#[test]
fn format_tui_package_config() {
// At one point this failed to reformat.
module_formats_to(
indoc!(
r#"
platform "tui"
requires { Model } { main : { init : ({} -> Model), update : (Model, Str -> Model), view : (Model -> Str) } }
exposes []
packages {}
imports []
provides [ main_for_host ]
main_for_host : { init : ({} -> Model) as Init, update : (Model, Str -> Model) as Update, view : (Model -> Str) as View }
main_for_host = main
"#
),
indoc!(
r#"
platform "tui"
requires { Model } { main : { init : {} -> Model, update : Model, Str -> Model, view : Model -> Str } }
exposes []
packages {}
imports []
provides [main_for_host]
main_for_host : { init : ({} -> Model) as Init, update : (Model, Str -> Model) as Update, view : (Model -> Str) as View }
main_for_host = main
"#
),
);
}
#[test]
fn single_line_hosted() {
module_formats_to(
indoc!(
r"
hosted Foo exposes [] imports []"
),
indoc!(
r"
hosted []"
),
);
}
#[test]
fn multi_line_hosted() {
module_formats_to(
indoc!(
r"
hosted Foo
exposes [
Stuff,
Things,
somethingElse,
]
imports []"
),
indoc!(
r"
hosted [
Stuff,
Things,
somethingElse,
]"
),
);
}
/// Annotations and aliases
#[test]
fn list_alias() {
expr_formats_same(indoc!(
r"
ConsList a : [Cons a (ConsList a), Nil]
f : ConsList a -> ConsList a
f = |_| Nil
f
"
));
}
#[test]
fn wildcard() {
expr_formats_same(indoc!(
r"
f : List *
f = []
a
"
));
}
#[test]
fn identity() {
expr_formats_same(indoc!(
r"
f : a -> a
f = []
a
"
));
}
#[test]
fn weird_triple_string() {
expr_formats_to(
indoc!(
r#"
_""""w"""
"#
),
indoc!(
r#"
_
"""
"w
"""
"#
),
);
}
#[test]
fn multiline_tag_union_annotation_no_comments() {
expr_formats_same(indoc!(
r"
b : [
True,
False,
]
b
"
));
expr_formats_same(indoc!(
r"
b :
[True, False]
b
"
));
expr_formats_to(
indoc!(
r"
b :
[
True,
False,
]
b
"
),
indoc!(
r"
b : [
True,
False,
]
b
"
),
);
expr_formats_to(
indoc!(
r"
b : [
True,
False,
]
b
"
),
indoc!(
r"
b : [
True,
False,
]
b
"
),
);
}
#[test]
fn multiline_tag_union_annotation_beginning_on_same_line() {
expr_formats_same(indoc!(
r"
Expr : [
Add Expr Expr,
Mul Expr Expr,
Val I64,
Var I64,
]
Expr"
));
}
#[test]
fn multiline_tag_union_annotation_with_final_comment() {
expr_formats_to(
indoc!(
r"
b :
[
True,
# comment 1
False # comment 2
,
# comment 3
]
b
"
),
indoc!(
r"
b : [
True,
# comment 1
False, # comment 2
# comment 3
]
b
"
),
);
}
#[test]
fn tag_union() {
expr_formats_same(indoc!(
r"
f : [True, False] -> [True, False]
f = |x| x
a
"
));
}
// TODO: the current formatting seems a bit odd for multiline function annotations
// (beside weird indentation, note the trailing space after the "->")
// #[test]
// fn multiline_tag_union_function_annotation() {
// expr_formats_same(indoc!(
// r"
// f :
// [
// True,
// False,
// ] ->
// [
// True,
// False,
// ]
// f = \x -> x
// a
// "
// ));
// }
#[test]
fn recursive_tag_union() {
expr_formats_same(indoc!(
r"
f : [Cons a (ConsList a), Nil] as ConsList a -> [Just a, Nothing]
f = |list|
when list is
Nil ->
Nothing
Cons first _ ->
Just first
f
"
));
expr_formats_to_with_flags(
indoc!(
r"
f : [Cons aVar (ConsList aVar), Nil] as ConsList aVar -> [Just aVar, Nothing]
f = |list|
when list is
Nil ->
Nothing
Cons first _ ->
Just first
f
"
),
indoc!(
r"
f : [Cons a_var (ConsList a_var), Nil] as ConsList a_var -> [Just a_var, Nothing]
f = |list|
when list is
Nil ->
Nothing
Cons first _ ->
Just first
f
"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
}
#[test]
fn function_application_package_type() {
expr_formats_same(indoc!(
r"
main : Task.Task {} []
main = 42
main
"
));
}
#[test]
fn record_type() {
expr_formats_same(indoc!(
r"
f : { foo : Int * }
f = { foo: 1000 }
a
"
));
}
#[test]
fn record_pattern_with_apply_guard() {
expr_formats_same(indoc!(
r"
when { x: 1 } is
{ x: Just 4 } ->
4
"
));
expr_formats_to_with_flags(
indoc!(
r"
when { x: 1 } is
{ x: Just 4 } ->
4
"
),
indoc!(
r"
when { x: 1 } is
{ x: Just(4) } ->
4
"
),
MigrationFlags {
snakify: false,
parens_and_commas: true,
},
);
}
#[test]
fn record_pattern_with_record_guard() {
expr_formats_same(indoc!(
r"
when { x: 1 } is
{ x: { x: True } } ->
4
"
));
}
#[test]
fn body_starts_with_spaces_multiline() {
expr_formats_same(indoc!(
r"
y =
Foo
1
2
y
"
));
}
#[test]
fn multiline_higher_order_function() {
expr_formats_same(indoc!(
r"
foo :
(Str -> Bool)
-> Bool
42
"
));
expr_formats_same(indoc!(
r"
foo :
(Str -> Bool),
Str
-> Bool
foo = |bar, baz|
42
42
"
));
expr_formats_same(indoc!(
r"
foo :
(Str -> Bool) -> Bool
42
"
));
expr_formats_same(indoc!(
r"
foo :
(Str -> Bool), Str -> Bool
foo = |bar, baz|
42
42
"
));
expr_formats_same(indoc!(
r"
foo :
(Str -> Bool), Str -> Bool # comment
foo = |bar, baz|
42
42
"
));
}
#[test]
fn multiline_opaque_tag_union() {
expr_formats_same(indoc!(
r"
A := [
B,
C,
]
0
"
));
}
#[test]
fn opaque_implements_clause() {
expr_formats_same(indoc!(
r"
A := U8 implements [Eq, Hash]
0
"
));
expr_formats_same(indoc!(
r"
A :=
U8
implements [Eq, Hash]
0
"
));
expr_formats_to(
indoc!(
r"
A := a where a implements Hash implements [ Eq, Hash ]
0
"
),
indoc!(
r"
A := a where a implements Hash
implements [Eq, Hash]
0
"
),
);
expr_formats_to_with_flags(
indoc!(
r"
A := aVar where aVar implements Hash implements [ Eq, Hash ]
0
"
),
indoc!(
r"
A := a_var where a_var implements Hash
implements [Eq, Hash]
0
"
),
MigrationFlags {
snakify: true,
parens_and_commas: false,
},
);
expr_formats_to(
indoc!(
r"
A := U8 implements []
0
"
),
indoc!(
r"
A := U8 implements []
0
"
),
);
}
#[test]
fn comma_prefixed_indented_record() {
expr_formats_to(
indoc!(
r"
Model position :
{ evaluated : Set position
, openSet : Set position
, costs : Dict.Dict position F64
, cameFrom : Dict.Dict position position
}
a
",
),
indoc!(
r"
Model position : {
evaluated : Set position,
openSet : Set position,
costs : Dict.Dict position F64,
cameFrom : Dict.Dict position position,
}
a
",
),
);
}
#[test]
fn opaque_implements_with_impls() {
expr_formats_same(indoc!(
r"
A := U8 implements [Eq { eq }, Hash { hash }]
0
"
));
expr_formats_same(indoc!(
r"
A := U8 implements [Eq { eq, eq1 }]
0
"
));
expr_formats_to(
indoc!(
r"
A := U8 implements [Eq { eq, eq1 }]
A := U8 implements [Eq {
eq,
eq1
}]
0
"
),
indoc!(
r"
A := U8 implements [Eq { eq, eq1 }]
A := U8 implements [
Eq {
eq,
eq1,
},
]
0
"
),
);
expr_formats_same(indoc!(
r"
A := a where a implements Other
implements [Eq { eq }, Hash { hash }]
0
"
));
expr_formats_same(indoc!(
r"
A := U8 implements [Eq {}]
0
"
));
}
#[test]
fn comments_in_multiline_tag_union_annotation() {
expr_formats_to(
indoc!(
r"
UnionAnn : [
Foo, # comment 1
Bar, # comment 2
Baz, # comment 3
# comment 4 line 1
# comment 4 line 2
]
0
"
),
indoc!(
r"
UnionAnn : [
Foo, # comment 1
Bar, # comment 2
Baz, # comment 3
# comment 4 line 1
# comment 4 line 2
]
0
"
),
);
}
#[test]
fn test_where_after() {
expr_formats_same(indoc!(
r"
Dict k v := {
metadata : List I8,
dataIndices : List U64,
data : List (T k v),
size : U64,
} where k implements Hash & Eq
a
"
));
}
#[test]
/// Test that builtins are formatted correctly
/// If this test fails on your diff, it probably means you need to re-format a builtin.
/// Try this:
/// `cargo run -- format $(find crates/compiler/builtins/roc -name \*.roc)`
fn test_fmt_builtins() {
let mut count = 0;
let builtins_path = workspace_root()
.join("crates")
.join("compiler")
.join("builtins")
.join("roc");
for entry in walkdir::WalkDir::new(&builtins_path) {
let entry = entry.unwrap();
let path = entry.path();
if path.extension() == Some(std::ffi::OsStr::new("roc")) {
count += 1;
let src = std::fs::read_to_string(path).unwrap();
println!("Now trying to format {}", path.display());
module_formats_same(&src);
}
}
assert!(
count > 0,
"Expecting to find at least 1 .roc file to format under {}",
builtins_path.display()
);
}
#[test]
fn expect_single_line() {
expr_formats_same(indoc!(
r"
x = 5
expect x == y
expect y == z
42
"
));
module_formats_same(indoc!(
r"
module []
expect x == y
expect y == z
foo = bar
"
));
}
#[test]
fn expect_multiline() {
expr_formats_same(indoc!(
r"
x = 5
expect
foo bar
|> baz
42
"
));
module_formats_same(indoc!(
r"
module []
expect
foo bar
|> baz
expect
blah
etc
foo = bar
"
));
}
#[test]
fn single_line_string_literal_in_pattern() {
expr_formats_same(indoc!(
r#"
when foo is
"abc" -> ""
"#
));
}
#[test]
fn multi_line_string_literal_in_pattern() {
expr_formats_same(indoc!(
r#"
when foo is
"""
abc
def
""" -> ""
"#
));
}
#[test]
fn multi_line_string_literal_that_can_be_single_line_in_pattern() {
expr_formats_to(
indoc!(
r#"
when foo is
"""
abc
""" -> ""
"#
),
indoc!(
r#"
when foo is
"abc" -> ""
"#
),
);
}
#[test]
fn format_chars() {
expr_formats_same(indoc!(
r"
' '
"
));
expr_formats_same(indoc!(
r"
'\n'
"
));
}
#[test]
fn format_char_pattern() {
expr_formats_same(indoc!(
r"
when x is
' ' -> x
'\n' -> x
'\t' -> x
"
));
}
#[test]
fn format_nested_pipeline() {
expr_formats_same(indoc!(
r"
(a |> b) |> c
"
));
expr_formats_same(indoc!(
r"
a |> b |> c
"
));
}
#[test]
fn ability_member_doc_comments() {
module_formats_same(indoc!(
r"
module []
A implements
## This is member ab
ab : a -> a where a implements A
## This is member de
de : a -> a where a implements A
f = g
"
));
}
#[test]
fn leading_comments_preserved() {
module_formats_same(indoc!(
r"
# hello world
module []
"
));
module_formats_same(indoc!(
r#"
# hello world
app [] { pf: platform "./platform" }
"#
));
module_formats_same(indoc!(
r#"
# hello world
platform "hello-world"
requires {} { main : Str }
exposes []
packages {}
imports []
provides [main_for_host]
"#
));
}
#[test]
fn comments_before_exposes_preserved() {
module_formats_same(indoc!(
r"
module
# comment
[a, b]
"
));
}
#[test]
fn clauses_with_multiple_abilities() {
expr_formats_same(indoc!(
r"
f : {} -> a where a implements Eq & Hash & Decode
f
"
));
expr_formats_to(
indoc!(
r"
f : {} -> a where a implements Eq & Hash & Decode,
b implements Eq & Hash
f
"
),
indoc!(
// TODO: ideally, this would look a bit nicer - consider
// f : {} -> a
// where a implements Eq & Hash & Decode,
// b implements Eq & Hash
r"
f : {} -> a where a implements Eq & Hash & Decode, b implements Eq & Hash
f
"
),
);
}
#[test]
fn format_list_patterns() {
expr_formats_same(indoc!(
r"
when [] is
[] -> []
"
));
expr_formats_to(
indoc!(
r"
when [] is
[ ] -> []
"
),
indoc!(
r"
when [] is
[] -> []
"
),
);
expr_formats_to(
indoc!(
r"
when [] is
[ x, .. , A 5 6, .. ] -> []
"
),
indoc!(
r"
when [] is
[x, .., A 5 6, ..] -> []
"
),
);
expr_formats_to(
indoc!(
r"
when [] is
[ x, 4, 5 ] -> []
[ .., 5 ] -> []
[ x, .. ] -> []
"
),
indoc!(
r"
when [] is
[x, 4, 5] -> []
[.., 5] -> []
[x, ..] -> []
"
),
);
}
#[test]
fn format_crash() {
expr_formats_same(indoc!(
r#"
_ = crash
_ = crash ""
crash "" ""
"#
));
expr_formats_to(
indoc!(
r#"
_ = crash
_ = crash ""
_ = crash "" ""
try
foo
(|_| crash "")
"#
),
indoc!(
r#"
_ = crash
_ = crash ""
_ = crash "" ""
try
foo
(|_| crash "")
"#
),
);
}
#[test]
fn format_try() {
expr_formats_same(indoc!(
r#"
_ = crash
_ = crash ""
crash "" ""
"#
));
expr_formats_to(
indoc!(
r#"
_ = crash
_ = crash ""
_ = crash "" ""
try
foo
(|_| crash "")
"#
),
indoc!(
r#"
_ = crash
_ = crash ""
_ = crash "" ""
try
foo
(|_| crash "")
"#
),
);
}
#[test]
fn issue_6197() {
expr_formats_to(
indoc!(
r"
when l1 is
[
..
as
rest
]
as
l2
->
f rest
"
),
indoc!(
r"
when l1 is
[.. as rest] as l2 ->
f rest
"
),
);
}
#[test]
fn issue_6215() {
expr_formats_to(
indoc!(
r"
when list is
[first as last]
| [first, last] ->
first
_->Not
"
),
indoc!(
r"
when list is
[first as last]
| [first, last] ->
first
_ -> Not
"
),
);
}
#[test]
fn preserve_annotated_body() {
expr_formats_same(indoc!(
r"
x : i32
x = 1
x
"
));
}
#[test]
fn preserve_annotated_body_comment() {
expr_formats_same(indoc!(
r"
x : i32 # comment
x = 1
x
"
));
}
#[test]
fn preserve_annotated_body_comments() {
expr_formats_same(indoc!(
r"
x : i32
# comment
# comment 2
x = 1
x
"
));
}
#[test]
fn preserve_annotated_body_comments_without_newlines() {
expr_formats_to(
indoc!(
r"
x : i32
# comment
# comment 2
x = 1
x
"
),
indoc!(
r"
x : i32
# comment
# comment 2
x = 1
x
"
),
);
}
#[test]
fn preserve_annotated_body_blank_comment() {
expr_formats_same(indoc!(
r"
x : i32
#
x = 1
x
"
));
}
#[test]
fn preserve_annotated_body_without_newlines() {
expr_formats_to(
indoc!(
r"
x : i32
x = 1
x
"
),
indoc!(
r"
x : i32
x = 1
x
"
),
);
}
#[test]
fn keep_explicit_blank_chars() {
expr_formats_same(indoc!(
r#"
x = "a\u(200a)b\u(200b)c\u(200c)d\u(feff)e"
x
"#
));
}
#[test]
fn make_blank_chars_explicit() {
expr_formats_to(
indoc!(
"
x = \"a\u{200A}b\u{200B}c\u{200C}d\u{FEFF}e\"
x
"
),
indoc!(
r#"
x = "a\u(200a)b\u(200b)c\u(200c)d\u(feff)e"
x
"#
),
);
}
#[test]
fn make_blank_chars_explicit_when_interpolating() {
expr_formats_to(
indoc!(
"
x = \"foo:\u{200B} ${bar}.\"
x
"
),
indoc!(
r#"
x = "foo:\u(200b) ${bar}."
x
"#
),
);
}
#[test]
fn make_blank_chars_explicit_in_multiline_string() {
expr_formats_to(
indoc!(
"
x =
\"\"\"
foo:\u{200B} ${bar}.
\"\"\"
x
"
),
indoc!(
r#"
x =
"""
foo:\u(200b) ${bar}.
"""
x
"#
),
);
}
#[test]
fn preserve_multiline_string_trailing_whitespace() {
expr_formats_same(indoc!(
"x =\n \"\"\"\n foo\n bar \n baz\n \"\"\"\nx"
));
}
// this is a parse error atm
// #[test]
// fn multiline_apply() {
// expr_formats_same(indoc!(
// r"
// f :
// Result a
// { x : Int *
// , y : Float
// }
// c
// -> Int *
// f =
// \_ -> 4
// "
// ));
// }
#[test]
fn pattern_tag_apply_with_whitespace_single_arg() {
pattern_formats_same(indoc!("Ok a"));
}
#[test]
fn pattern_tag_apply_with_pnc_single_arg() {
pattern_formats_same(indoc!("Ok(a)"));
}
#[test]
fn pattern_tag_apply_with_whitespace_multi_arg() {
pattern_formats_same(indoc!("Ok a b"));
}
#[test]
fn pattern_tag_apply_with_pnc_multi_arg() {
pattern_formats_same(indoc!("Ok(a, b)"));
}
}