diff --git a/compiler/fmt/src/def.rs b/compiler/fmt/src/def.rs index ca99d19110..72f95b0dab 100644 --- a/compiler/fmt/src/def.rs +++ b/compiler/fmt/src/def.rs @@ -191,12 +191,26 @@ pub fn fmt_body<'a, 'buf>( buf.push_str(" ="); if body.is_multiline() { match body { - Expr::SpaceBefore(_, _) => { - body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT); - } - Expr::Record { .. } | Expr::List { .. } => { - buf.newline(); - body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT); + Expr::SpaceBefore(sub_def, spaces) => { + let should_outdent = match sub_def { + Expr::Record { .. } | Expr::List { .. } => { + let is_only_newlines = spaces.iter().all(|s| s.is_newline()); + is_only_newlines && sub_def.is_multiline() + } + _ => false, + }; + + if should_outdent { + buf.spaces(1); + sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent); + } else { + body.format_with_options( + buf, + Parens::NotNeeded, + Newlines::Yes, + indent + INDENT, + ); + } } _ => { buf.spaces(1); diff --git a/compiler/fmt/src/expr.rs b/compiler/fmt/src/expr.rs index 70b62b0e13..300e6ac4d4 100644 --- a/compiler/fmt/src/expr.rs +++ b/compiler/fmt/src/expr.rs @@ -186,14 +186,75 @@ impl<'a> Formattable for Expr<'a> { loc_expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); let multiline_args = loc_args.iter().any(|loc_arg| loc_arg.is_multiline()); + let last_arg_index = loc_args.len() - 1; - if multiline_args { + let should_outdent_last_arg = + loc_args + .iter() + .enumerate() + .fold(false, |found_multiline_expr, val| { + let (index, loc_arg) = val; + if index == last_arg_index { + match loc_arg.value { + SpaceBefore(sub_expr, spaces) => match sub_expr { + Record { .. } | List { .. } => { + let is_only_newlines = + spaces.iter().all(|s| s.is_newline()); + is_only_newlines + && !found_multiline_expr + && sub_expr.is_multiline() + } + _ => false, + }, + Record { .. } | List { .. } => { + !found_multiline_expr && loc_arg.is_multiline() + } + _ => false, + } + } else { + loc_arg.is_multiline() + } + }); + + if multiline_args && !should_outdent_last_arg { let arg_indent = indent + INDENT; for loc_arg in loc_args.iter() { buf.newline(); loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent); } + } else if multiline_args && should_outdent_last_arg { + for (index, loc_arg) in loc_args.iter().enumerate() { + buf.spaces(1); + + if index == last_arg_index { + match loc_arg.value { + SpaceBefore(sub_expr, _) => { + sub_expr.format_with_options( + buf, + Parens::InApply, + Newlines::Yes, + indent, + ); + } + _ => { + loc_arg.format_with_options( + buf, + Parens::InApply, + Newlines::Yes, + indent, + ); + } + } + } else { + loc_arg.format_with_options( + buf, + Parens::InApply, + Newlines::Yes, + indent, + ); + } + } } else { for loc_arg in loc_args.iter() { buf.spaces(1); @@ -846,7 +907,34 @@ fn fmt_closure<'a, 'buf>( } }; - loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent); + if is_multiline { + match &loc_ret.value { + SpaceBefore(sub_expr, spaces) => { + let should_outdent = match sub_expr { + Record { .. } | List { .. } => { + let is_only_newlines = spaces.iter().all(|s| s.is_newline()); + is_only_newlines && sub_expr.is_multiline() + } + _ => false, + }; + + if should_outdent { + buf.spaces(1); + sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent); + } else { + loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent); + } + } + Record { .. } | List { .. } => { + loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent); + } + _ => { + loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent); + } + } + } else { + loc_ret.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, body_indent); + } } fn fmt_backpassing<'a, 'buf>( diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index c514ff7cf0..b91658ef12 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -626,6 +626,392 @@ mod test_fmt { )); } + #[test] + fn lambda_returns_record() { + expr_formats_same(indoc!( + r#" + toRecord = \_ -> { + x: 1, + y: 2, + z: 3, + } + + toRecord + "# + )); + + expr_formats_same(indoc!( + r#" + func = \_ -> + { x: 1, y: 2, z: 3 } + + func + "# + )); + + expr_formats_same(indoc!( + r#" + toRecord = \_ -> + val = 0 + + { + x: 1, + y: 2, + z: 3, + } + + toRecord + "# + )); + + expr_formats_to( + indoc!( + r#" + toRecord = \_ -> + { + x: 1, + y: 2, + z: 3, + } + + toRecord + "# + ), + indoc!( + r#" + toRecord = \_ -> { + x: 1, + y: 2, + z: 3, + } + + toRecord + "# + ), + ); + } + + #[test] + fn lambda_returns_list() { + expr_formats_same(indoc!( + r#" + toList = \_ -> [ + 1, + 2, + 3, + ] + + toList + "# + )); + + expr_formats_same(indoc!( + r#" + func = \_ -> + [ 1, 2, 3 ] + + func + "# + )); + + expr_formats_same(indoc!( + r#" + toList = \_ -> + val = 0 + + [ + 1, + 2, + 3, + ] + + toList + "# + )); + + expr_formats_to( + indoc!( + r#" + toList = \_ -> + [ + 1, + 2, + 3, + ] + + toList + "# + ), + indoc!( + r#" + toList = \_ -> [ + 1, + 2, + 3, + ] + + toList + "# + ), + ); + } + + #[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 + "# + ), + ); + + 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 + "# + ), + ); + + 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!( @@ -1301,6 +1687,27 @@ mod test_fmt { 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, @@ -1308,8 +1715,19 @@ mod test_fmt { ] l - "# - )); + "# + ), + indoc!( + r#" + l = [ + 1, + 2, + ] + + l + "# + ), + ); expr_formats_to( indoc!( @@ -1324,11 +1742,10 @@ mod test_fmt { ), indoc!( r#" - results = - [ - Ok 4, - Ok 5, - ] + results = [ + Ok 4, + Ok 5, + ] allOks results "# @@ -1417,18 +1834,69 @@ mod test_fmt { #[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#" + myDef = + list = [ + a, + b, + ] + { + c, + d, + } + + myDef + "# + )); + + expr_formats_to( + indoc!( + r#" + pos = + { + x: 4, + y: 11, + z: 16, + } + + pos + "# + ), + indoc!( + r#" + pos = { x: 4, y: 11, z: 16, } - pos - "# - )); + pos + "# + ), + ); expr_formats_to( indoc!( @@ -1443,11 +1911,10 @@ mod test_fmt { ), indoc!( r#" - pos = - { - x: 5, - y: 10, - } + pos = { + x: 5, + y: 10, + } pos "# diff --git a/examples/benchmarks/AStar.roc b/examples/benchmarks/AStar.roc index 4f5a8e0fa1..d6a6ec4c1e 100644 --- a/examples/benchmarks/AStar.roc +++ b/examples/benchmarks/AStar.roc @@ -12,13 +12,12 @@ Model position : } initialModel : position -> Model position -initialModel = \start -> - { - evaluated: Set.empty, - openSet: Set.single start, - costs: Dict.single start 0, - cameFrom: Dict.empty, - } +initialModel = \start -> { + evaluated: Set.empty, + openSet: Set.single start, + costs: Dict.single start 0, + cameFrom: Dict.empty, +} cheapestOpen : (position -> F64), Model position -> Result position {} cheapestOpen = \costFn, model -> diff --git a/examples/benchmarks/Base64/Decode.roc b/examples/benchmarks/Base64/Decode.roc index e78585ad5b..ad5c92ef17 100644 --- a/examples/benchmarks/Base64/Decode.roc +++ b/examples/benchmarks/Base64/Decode.roc @@ -20,11 +20,10 @@ loopHelp = \{ remaining, string } -> c = Num.intCast z combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)) c - Loop - { - remaining: remaining - 3, - string: Str.concat string (bitsToChars combined 0), - } + Loop { + remaining: remaining - 3, + string: Str.concat string (bitsToChars combined 0), + } else if remaining == 0 then Bytes.Decode.succeed (Done string) else if remaining == 2 then diff --git a/examples/benchmarks/Bytes/Encode.roc b/examples/benchmarks/Bytes/Encode.roc index 37321e8506..195ccd2407 100644 --- a/examples/benchmarks/Bytes/Encode.roc +++ b/examples/benchmarks/Bytes/Encode.roc @@ -132,11 +132,10 @@ encodeHelp = \encoder, offset, output -> List.walk bs { output, offset } - \accum, byte -> - { - offset: accum.offset + 1, - output: List.set accum.output offset byte, - } + \accum, byte -> { + offset: accum.offset + 1, + output: List.set accum.output offset byte, + } Sequence _ encoders -> List.walk diff --git a/examples/breakout/breakout.roc b/examples/breakout/breakout.roc index d06e3ab45f..1625f9b411 100644 --- a/examples/breakout/breakout.roc +++ b/examples/breakout/breakout.roc @@ -29,20 +29,19 @@ Model : { } init : Bounds -> Model -init = \{ width, height } -> - { - # Screen height and width - width, - height, - # Paddle X-coordinate - paddleX: (width * 0.5) - (paddleWidth * width * 0.5), - # Ball coordinates - ballX: width * 0.5, - ballY: height * 0.4, - # Delta - how much ball moves in each tick - dBallX: 4, - dBallY: 4, - } +init = \{ width, height } -> { + # Screen height and width + width, + height, + # Paddle X-coordinate + paddleX: (width * 0.5) - (paddleWidth * width * 0.5), + # Ball coordinates + ballX: width * 0.5, + ballY: height * 0.4, + # Delta - how much ball moves in each tick + dBallX: 4, + dBallY: 4, +} update : Model, Event -> Model update = \model, event -> @@ -125,23 +124,21 @@ render = \model -> top = Num.toF32 (row * blockHeight) border = blockBorder * blockWidth - outer = Rect - { - left, - top, - width: blockWidth, - height: blockHeight, - color: { r: color.r * 0.8, g: color.g * 0.8, b: color.b * 0.8, a: 1 }, - } + outer = Rect { + left, + top, + width: blockWidth, + height: blockHeight, + color: { r: color.r * 0.8, g: color.g * 0.8, b: color.b * 0.8, a: 1 }, + } - inner = Rect - { - left: left + border, - top: top + border, - width: blockWidth - (border * 2), - height: blockHeight - (border * 2), - color, - } + inner = Rect { + left: left + border, + top: top + border, + width: blockWidth - (border * 2), + height: blockHeight - (border * 2), + color, + } [ outer, inner ] diff --git a/examples/gui/Hello.roc b/examples/gui/Hello.roc index 653ccbd8e1..4738b97b11 100644 --- a/examples/gui/Hello.roc +++ b/examples/gui/Hello.roc @@ -8,14 +8,12 @@ render = styles = { bgColor: rgba 100 50 50 1, borderColor: rgba 10 20 30 1, borderWidth: 10, textColor: rgba 220 220 250 1 } - Col - [ - Row - [ - Button (Text "Corner ") styles, - Button (Text "Top Mid ") { styles & bgColor: rgba 100 100 50 1 }, - Button (Text "Top Right ") { styles & bgColor: rgba 50 50 150 1 }, - ], - Button (Text "Mid Left ") { styles & bgColor: rgba 150 100 100 1 }, - Button (Text "Bottom Left") { styles & bgColor: rgba 150 50 50 1 }, - ] + Col [ + Row [ + Button (Text "Corner ") styles, + Button (Text "Top Mid ") { styles & bgColor: rgba 100 100 50 1 }, + Button (Text "Top Right ") { styles & bgColor: rgba 50 50 150 1 }, + ], + Button (Text "Mid Left ") { styles & bgColor: rgba 150 100 100 1 }, + Button (Text "Bottom Left") { styles & bgColor: rgba 150 50 50 1 }, + ] diff --git a/examples/interactive/tui.roc b/examples/interactive/tui.roc index 03b05efc17..3f09cbfdf5 100644 --- a/examples/interactive/tui.roc +++ b/examples/interactive/tui.roc @@ -6,9 +6,8 @@ app "tui" Model : Str main : Program Model -main = - { - init: \{ } -> "Hello World", - update: \model, new -> Str.concat model new, - view: \model -> Str.concat model "!", - } +main = { + init: \{ } -> "Hello World", + update: \model, new -> Str.concat model new, + view: \model -> Str.concat model "!", +}