diff --git a/crates/compiler/fmt/src/annotation.rs b/crates/compiler/fmt/src/annotation.rs index 34060e99f5..eff2c01030 100644 --- a/crates/compiler/fmt/src/annotation.rs +++ b/crates/compiler/fmt/src/annotation.rs @@ -5,10 +5,9 @@ use crate::{ parens_around_node, DelimitedItem, Item, Node, NodeInfo, NodeSequenceBuilder, Nodify, Prec, Sp, }, - pattern::pattern_lift_spaces_after, - pattern::snakify_camel_ident, + pattern::{pattern_lift_spaces_after, snakify_camel_ident}, spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT}, - Buf, + Buf, MigrationFlags, }; use bumpalo::{ collections::{String, Vec}, @@ -232,7 +231,7 @@ fn fmt_ty_ann( } me.item - .to_node(buf.text.bump()) + .to_node(buf.text.bump(), buf.flags()) .add_parens(buf.text.bump(), parens) .node .format(buf, indent); @@ -243,7 +242,7 @@ fn fmt_ty_ann( } impl<'a> Nodify<'a> for Tag<'a> { - fn to_node<'b>(&'a self, arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b, { @@ -258,7 +257,10 @@ impl<'a> Nodify<'a> for Tag<'a> { let mut last_after: &[CommentOrNewline<'_>] = &[]; for arg in args.iter() { - let lifted = arg.value.to_node(arena).add_parens(arena, Parens::InApply); + let lifted = arg + .value + .to_node(arena, flags) + .add_parens(arena, Parens::InApply); let before = merge_spaces_conservative(arena, last_after, lifted.before); last_after = lifted.after; new_args.push((Sp::with_space(before), lifted.node)); @@ -278,12 +280,12 @@ impl<'a> Nodify<'a> for Tag<'a> { } } Tag::SpaceBefore(inner, sp) => { - let mut inner = inner.to_node(arena); + let mut inner = inner.to_node(arena, flags); inner.before = merge_spaces_conservative(arena, sp, inner.before); inner } Tag::SpaceAfter(inner, sp) => { - let mut inner = inner.to_node(arena); + let mut inner = inner.to_node(arena, flags); inner.after = merge_spaces_conservative(arena, inner.after, sp); inner } @@ -369,22 +371,22 @@ impl<'a> Formattable for AssignedField<'a, Expr<'a>> { } impl<'a> Nodify<'a> for AssignedField<'a, TypeAnnotation<'a>> { - fn to_node<'b>(&'a self, arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b, { match self { AssignedField::RequiredValue(name, sp, value) => { - assigned_field_value_to_node(name.value, arena, sp, &value.value, ":") + assigned_field_value_to_node(name.value, arena, sp, &value.value, ":", flags) } AssignedField::IgnoredValue(name, sp, value) => { let mut n = String::with_capacity_in(name.value.len() + 1, arena); n.push('_'); n.push_str(name.value); - assigned_field_value_to_node(n.into_bump_str(), arena, sp, &value.value, ":") + assigned_field_value_to_node(n.into_bump_str(), arena, sp, &value.value, ":", flags) } AssignedField::OptionalValue(name, sp, value) => { - assigned_field_value_to_node(name.value, arena, sp, &value.value, "?") + assigned_field_value_to_node(name.value, arena, sp, &value.value, "?", flags) } AssignedField::LabelOnly(name) => NodeInfo { before: &[], @@ -394,12 +396,12 @@ impl<'a> Nodify<'a> for AssignedField<'a, TypeAnnotation<'a>> { prec: Prec::Term, }, AssignedField::SpaceBefore(inner, sp) => { - let mut inner = inner.to_node(arena); + let mut inner = inner.to_node(arena, flags); inner.before = merge_spaces_conservative(arena, sp, inner.before); inner } AssignedField::SpaceAfter(inner, sp) => { - let mut inner = inner.to_node(arena); + let mut inner = inner.to_node(arena, flags); inner.after = merge_spaces_conservative(arena, inner.after, sp); inner } @@ -413,17 +415,27 @@ fn assigned_field_value_to_node<'a, 'b>( sp: &'a [CommentOrNewline<'a>], value: &'a TypeAnnotation<'a>, sep: &'static str, + flags: MigrationFlags, ) -> NodeInfo<'b> where 'a: 'b, { - let first = Node::Literal(name); + let field_name = if flags.snakify { + let mut buf = Buf::new_in(arena, flags); + buf.indent(0); // Take out of beginning of line + snakify_camel_ident(&mut buf, name); + let s: &str = arena.alloc_str(buf.as_str()); + s + } else { + name + }; + let first = Node::Literal(field_name); let mut b = NodeSequenceBuilder::new(arena, first, 2, false); b.push(Sp::with_space(sp), Node::Literal(sep)); - let value_lifted = value.to_node(arena); + let value_lifted = value.to_node(arena, flags); b.push(Sp::with_space(value_lifted.before), value_lifted.node); @@ -968,7 +980,7 @@ pub fn type_head_lift_spaces_after<'a, 'b: 'a>( } impl<'a> Nodify<'a> for TypeAnnotation<'a> { - fn to_node<'b>(&'a self, arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b, { @@ -983,16 +995,16 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { NodeInfo::apply( arena, NodeInfo::item(first), - args.iter().map(|arg| arg.value.to_node(arena)), + args.iter().map(|arg| arg.value.to_node(arena, flags)), ) } TypeAnnotation::SpaceBefore(expr, spaces) => { - let mut inner = expr.to_node(arena); + let mut inner = expr.to_node(arena, flags); inner.before = merge_spaces_conservative(arena, spaces, inner.before); inner } TypeAnnotation::SpaceAfter(expr, spaces) => { - let mut inner = expr.to_node(arena); + let mut inner = expr.to_node(arena, flags); inner.after = merge_spaces_conservative(arena, inner.after, spaces); inner } @@ -1000,7 +1012,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { let (first, rest) = args.split_first().expect("args must not be empty"); let first_node = first .value - .to_node(arena) + .to_node(arena, flags) .add_parens(arena, Parens::InFunctionType); let mut last_after: &'_ [CommentOrNewline<'_>] = &[]; let mut rest_nodes = Vec::with_capacity_in(rest.len() + 2, arena); @@ -1009,7 +1021,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { for item in rest { let node = item .value - .to_node(arena) + .to_node(arena, flags) .add_parens(arena, Parens::InFunctionType); let before = merge_spaces_conservative(arena, last_after, node.before); multiline |= node.node.is_multiline() || !before.is_empty(); @@ -1025,7 +1037,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { let res_node = res .value - .to_node(arena) + .to_node(arena, flags) .add_parens(arena, Parens::InFunctionType); multiline |= res_node.node.is_multiline() || !last_after.is_empty() @@ -1073,9 +1085,11 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { TypeAnnotation::As(left, sp, right) => { let left = left .value - .to_node(arena) + .to_node(arena, flags) + .add_parens(arena, Parens::InAsPattern); + let right = right + .to_node(arena, flags) .add_parens(arena, Parens::InAsPattern); - let right = right.to_node(arena).add_parens(arena, Parens::InAsPattern); let before_as = merge_spaces(arena, left.after, sp); let mut b = NodeSequenceBuilder::new(arena, left.node, 2, true); b.push(Sp::with_space(before_as), Node::Literal("as")); @@ -1090,7 +1104,16 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { } } TypeAnnotation::BoundVariable(text) => { - let item = NodeInfo::item(Node::Literal(text)); + let var_name = if flags.snakify { + let mut buf = Buf::new_in(arena, flags); + buf.indent(0); // Take out of beginning of line + snakify_camel_ident(&mut buf, text); + let s: &str = arena.alloc_str(buf.as_str()); + s + } else { + text + }; + let item = NodeInfo::item(Node::Literal(var_name)); if *text == "implements" { parens_around_node(arena, item, false) @@ -1105,7 +1128,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { let coll = collection_to_node(arena, Braces::Curly, true, fields, |_is_first, f| { f.value - .to_node(arena) + .to_node(arena, flags) .add_parens(arena, Parens::InCollection) }); maybe_add_ext( @@ -1113,13 +1136,14 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { coll, ext, fields.is_empty() && fields.final_comments().is_empty(), + flags, ) } TypeAnnotation::TagUnion { ext, tags } => { let coll = collection_to_node(arena, Braces::Square, false, tags, |_is_first, t| { t.value - .to_node(arena) + .to_node(arena, flags) .add_parens(arena, Parens::InCollection) }); maybe_add_ext( @@ -1127,11 +1151,12 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { coll, ext, tags.is_empty() && tags.final_comments().is_empty(), + flags, ) } TypeAnnotation::Tuple { elems, ext } => { let coll = collection_to_node(arena, Braces::Round, false, elems, |is_first, e| { - let v = e.value.to_node(arena); + let v = e.value.to_node(arena, flags); if is_first { v } else { @@ -1143,6 +1168,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { coll, ext, elems.is_empty() && elems.final_comments().is_empty(), + flags, ) } TypeAnnotation::Where(annot, implements_clauses) => { @@ -1150,7 +1176,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { let annot = annot .value - .to_node(arena) + .to_node(arena, flags) .add_parens(arena, Parens::NotNeeded); let mut needs_indent = annot.needs_indent || !annot.after.is_empty(); @@ -1166,7 +1192,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { let mut last_after: &[CommentOrNewline<'_>] = &[]; for (i, clause) in implements_clauses.iter().enumerate() { - let node = clause.value.to_node(arena); + let node = clause.value.to_node(arena, flags); let before = merge_spaces_conservative(arena, last_after, node.before); last_after = node.after; items.push(Item { @@ -1199,28 +1225,36 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> { } impl<'a> Nodify<'a> for &'a str { - fn to_node<'b>(&'a self, _arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b, { - NodeInfo::item(Node::Literal(self)) + if flags.snakify { + let mut buf = Buf::new_in(arena, flags); + buf.indent(0); // Take out of beginning of line + snakify_camel_ident(&mut buf, self); + let s: &str = arena.alloc_str(buf.as_str()); + NodeInfo::item(Node::Literal(s)) + } else { + NodeInfo::item(Node::Literal(self)) + } } } impl<'a, T: Nodify<'a>> Nodify<'a> for Spaced<'a, T> { - fn to_node<'b>(&'a self, arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b, { match self { - Spaced::Item(item) => item.to_node(arena), + Spaced::Item(item) => item.to_node(arena, flags), Spaced::SpaceBefore(inner, sp) => { - let mut inner = inner.to_node(arena); + let mut inner = inner.to_node(arena, flags); inner.before = merge_spaces_conservative(arena, sp, inner.before); inner } Spaced::SpaceAfter(inner, sp) => { - let mut inner = inner.to_node(arena); + let mut inner = inner.to_node(arena, flags); inner.after = merge_spaces_conservative(arena, inner.after, sp); inner } @@ -1229,7 +1263,7 @@ impl<'a, T: Nodify<'a>> Nodify<'a> for Spaced<'a, T> { } impl<'a> Nodify<'a> for ImplementsClause<'a> { - fn to_node<'b>(&'a self, arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b, { @@ -1238,7 +1272,7 @@ impl<'a> Nodify<'a> for ImplementsClause<'a> { let var = self .var .value - .to_node(arena) + .to_node(arena, flags) .add_parens(arena, Parens::InAsPattern); items.push(Item { @@ -1252,7 +1286,7 @@ impl<'a> Nodify<'a> for ImplementsClause<'a> { let mut last_after: &[CommentOrNewline<'_>] = &[]; for (i, clause) in self.abilities.iter().enumerate() { - let node = clause.value.to_node(arena); + let node = clause.value.to_node(arena, flags); let before = merge_spaces_conservative(arena, last_after, node.before); last_after = node.after; @@ -1373,9 +1407,10 @@ fn maybe_add_ext<'a>( delim: Node<'a>, ext: &Option<&'a Loc>>, needs_indent: bool, + flags: MigrationFlags, ) -> NodeInfo<'a> { if let Some(ext) = ext { - let ext = ext.value.to_node(arena).add_ty_ext_parens(arena); + let ext = ext.value.to_node(arena, flags).add_ty_ext_parens(arena); debug_assert_eq!(ext.before, &[]); let item = Node::Sequence { first: arena.alloc(delim), diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 5296b468d4..00886a5c25 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -431,6 +431,8 @@ impl<'a> Formattable for TypeDef<'a> { fn format_with_options(&self, buf: &mut Buf, _parens: Parens, newlines: Newlines, indent: u16) { use roc_parse::ast::TypeDef::*; + println!("WTF???"); + match self { Alias { header, ann } => { header.format(buf, indent); @@ -596,7 +598,7 @@ fn type_head_lift_spaces<'a, 'b: 'a>( } impl<'a> Nodify<'a> for TypeHeader<'a> { - fn to_node<'b>(&'a self, arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, _flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b, { @@ -936,7 +938,7 @@ fn fmt_general_def( buf.push_str(sep); buf.spaces(1); - let rhs = rhs.to_node(buf.text.bump()); + let rhs = rhs.to_node(buf.text.bump(), buf.flags()); if rhs.node.is_multiline() || !rhs.before.is_empty() || !rhs.after.is_empty() { if rhs.node.is_multiline() && !rhs.needs_indent && rhs.before.iter().all(|s| s.is_newline()) diff --git a/crates/compiler/fmt/src/node.rs b/crates/compiler/fmt/src/node.rs index b462e22e0e..0d6ff3cd07 100644 --- a/crates/compiler/fmt/src/node.rs +++ b/crates/compiler/fmt/src/node.rs @@ -6,7 +6,7 @@ use crate::{ collection::Braces, expr::merge_spaces_conservative, spaces::{fmt_comments_only, fmt_spaces, fmt_spaces_no_blank_lines, NewlineAt, INDENT}, - Buf, + Buf, MigrationFlags, }; #[derive(Copy, Clone, Debug)] @@ -305,7 +305,7 @@ impl<'b> NodeInfo<'b> { } pub trait Nodify<'a> { - fn to_node<'b>(&'a self, arena: &'b Bump) -> NodeInfo<'b> + fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b> where 'a: 'b; } diff --git a/crates/compiler/test_syntax/src/minimize.rs b/crates/compiler/test_syntax/src/minimize.rs index d889f946bc..6bbe2fd602 100644 --- a/crates/compiler/test_syntax/src/minimize.rs +++ b/crates/compiler/test_syntax/src/minimize.rs @@ -7,6 +7,7 @@ use crate::test_helpers::{Input, InputKind}; use bumpalo::Bump; +use roc_fmt::MigrationFlags; use roc_parse::{ast::Malformed, normalize::Normalize}; #[derive(Copy, Clone, Debug)] @@ -122,7 +123,12 @@ fn round_trip_once(input: Input<'_>, options: Options) -> Option { } } - let output = actual.format(); + let flags = MigrationFlags { + snakify: false, + parens_and_commas: false, + }; + + let output = actual.format(flags); let reparsed_ast = match output.as_ref().parse_in(&arena) { Ok(r) => r, @@ -142,7 +148,7 @@ fn round_trip_once(input: Input<'_>, options: Options) -> Option { return Some("Different ast".to_string()); } - let reformatted = reparsed_ast.format(); + let reformatted = reparsed_ast.format(flags); if output != reformatted { return Some("Formatting not stable".to_string()); diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index ffa9148c32..4aeb5eeb70 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -108,12 +108,8 @@ pub enum Output<'a> { } impl<'a> Output<'a> { - pub fn format(&self) -> InputOwned { + pub fn format(&self, flags: MigrationFlags) -> InputOwned { let arena = Bump::new(); - let flags = MigrationFlags { - snakify: false, - parens_and_commas: false, - }; let mut buf = Buf::new_in(&arena, flags); match self { Output::Header(header) => { @@ -334,16 +330,33 @@ impl<'a> Input<'a> { } } + pub fn check_invariants( + &self, + handle_formatted_output: impl Fn(Input), + check_idempotency: bool, + canonicalize_mode: Option, + ) { + self.check_invariants_with_flags( + handle_formatted_output, + check_idempotency, + canonicalize_mode, + MigrationFlags { + snakify: false, + parens_and_commas: false, + }, + ); + } /// Parse and re-format the given input, and pass the output to `check_formatting` /// for verification. The expectation is that `check_formatting` assert the result matches /// expectations (or, overwrite the expectation based on a command-line flag) /// Optionally, based on the value of `check_idempotency`, also verify that the formatting /// is idempotent - that if we reformat the output, we get the same result. - pub fn check_invariants( + pub fn check_invariants_with_flags( &self, handle_formatted_output: impl Fn(Input), check_idempotency: bool, canonicalize_mode: Option, + flags: MigrationFlags, ) { let arena = Bump::new(); @@ -351,7 +364,7 @@ impl<'a> Input<'a> { panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:#?}\n\n", self.as_str(), err); }); - let output = actual.format(); + let output = actual.format(flags); handle_formatted_output(output.as_ref()); @@ -369,31 +382,33 @@ impl<'a> Input<'a> { ); }); - 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{}\n\n\ - * * * Source code after formatting:\n{}\n\n\ - * * * AST before formatting:\n{:#?}\n\n\ - * * * AST after formatting:\n{:#?}\n\n", - self.as_str(), - output.as_ref().as_str(), - actual, - reparsed_ast - ); + if !flags.at_least_one_active() { + 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{}\n\n\ + * * * Source code after formatting:\n{}\n\n\ + * * * AST before formatting:\n{:#?}\n\n\ + * * * AST after formatting:\n{:#?}\n\n", + self.as_str(), + output.as_ref().as_str(), + actual, + reparsed_ast + ); + } } // Now verify that the resultant formatting is _idempotent_ - i.e. that it doesn't change again if re-formatted if check_idempotency { - let reformatted = reparsed_ast.format(); + let reformatted = reparsed_ast.format(flags); if output != reformatted { eprintln!("Formatting bug; formatting is not stable.\nOriginal code:\n{}\n\nFormatted code:\n{}\n\nAST:\n{:#?}\n\nReparsed AST:\n{:#?}\n\n", diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index d2e2d67202..073e3fe91a 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -29,6 +29,15 @@ mod test_fmt { ) } + 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()), @@ -45,6 +54,15 @@ mod test_fmt { ); } + 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, @@ -81,7 +99,7 @@ mod test_fmt { Ok((actual, state)) => { use roc_parse::normalize::Normalize; - let flags = MigrationFlags{ snakify: false, parens_and_commas: false }; + 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); @@ -352,6 +370,65 @@ mod test_fmt { " )); + 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 : { @@ -583,6 +660,65 @@ mod test_fmt { "# )); + 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 = { @@ -1284,6 +1420,29 @@ mod test_fmt { \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, + }, + ); } #[test] @@ -1947,6 +2106,27 @@ mod test_fmt { { 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!( @@ -1961,7 +2141,7 @@ mod test_fmt { ), ); - expr_formats_to( + expr_formats_to_with_flags( indoc!( r" { shoes & rightShoe : nothing } @@ -1969,9 +2149,13 @@ mod test_fmt { ), indoc!( r" - { shoes & rightShoe: nothing } + { shoes & right_shoe: nothing } " ), + MigrationFlags { + snakify: true, + parens_and_commas: false, + }, ); expr_formats_same(indoc!( @@ -2526,6 +2710,60 @@ mod test_fmt { 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] @@ -3503,6 +3741,37 @@ mod test_fmt { 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] @@ -5316,6 +5585,40 @@ mod test_fmt { 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] @@ -5351,6 +5654,27 @@ mod test_fmt { 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] @@ -5487,6 +5811,27 @@ mod test_fmt { ), ); + 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"