diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 01ffd53c61..266137aef1 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -233,16 +233,9 @@ fn format_expr_only( Expr::If { if_thens: branches, final_else, - indented_else, + indented_else: _, } => { - fmt_if( - buf, - branches, - final_else, - item.is_multiline(), - *indented_else, - indent, - ); + fmt_if(buf, branches, final_else, item.is_multiline(), indent); } Expr::When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent), Expr::Tuple(items) => fmt_expr_collection(buf, indent, Braces::Round, *items, Newlines::No), @@ -1927,8 +1920,6 @@ fn fmt_if<'a>( branches: &'a [(Loc>, Loc>)], final_else: &'a Loc>, is_multiline: bool, - indented_else: bool, - indent: u16, ) { // let is_multiline_then = loc_then.is_multiline(); @@ -1972,29 +1963,20 @@ fn fmt_if<'a>( } buf.ensure_ends_with_whitespace(); - if indented_else { - buf.indent(indent + INDENT); - buf.push_str("else"); - buf.newline(); - buf.newline(); - } else if is_multiline { - buf.indent(indent); - buf.push_str("else"); + buf.indent(indent); + buf.push_str("else"); + if is_multiline { buf.newline(); } else { - buf.indent(indent); - buf.push_str("else"); buf.spaces(1); } - let indent = if indented_else { indent } else { return_indent }; - final_else.format(buf, indent); + final_else.format(buf, return_indent); } fn fmt_closure<'a>( buf: &mut Buf, loc_patterns: &'a [Loc>], loc_ret: &'a Loc>, - indent: u16, ) { use self::Expr::*; diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index f044671351..f85a077cd8 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -3390,7 +3390,7 @@ fn starts_with_spaces_conservative(value: &Pattern<'_>) -> bool { } fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>) -> bool { - match pat { + match pat.without_spaces() { Pattern::Apply(func, args) => { if !matches!(func.value, Pattern::Tag(tag) if header.name.value == tag) { return false; @@ -3399,7 +3399,7 @@ fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>) return false; } for (arg, var) in (*args).iter().zip(header.vars) { - match (arg.value, var.value) { + match (arg.value.without_spaces(), var.value.without_spaces()) { (Pattern::Identifier { ident: left }, TypeVar::Identifier(right)) => { if left != right { return false; @@ -3410,7 +3410,7 @@ fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>) } true } - Pattern::Tag(tag) => header.vars.is_empty() && header.name.value == *tag, + Pattern::Tag(tag) => header.vars.is_empty() && header.name.value == tag, _ => false, } } @@ -3991,9 +3991,10 @@ enum OperatorOrDef { } fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> { - (move |_, state: State<'a>, min_indent| { + (move |arena: &'a Bump, state: State<'a>, min_indent| { let start = state.pos(); - let (_, op, state) = operator_help(EExpr::Start, EExpr::BadOperator, state, min_indent)?; + let (_, op, state) = + operator_help(arena, EExpr::Start, EExpr::BadOperator, state, min_indent)?; let err_progress = if check_for_defs { MadeProgress } else { @@ -4014,12 +4015,15 @@ fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> { } fn operator<'a>() -> impl Parser<'a, OperatorOrDef, EExpr<'a>> { - (move |_, state, min_indent| operator_help(EExpr::Start, EExpr::BadOperator, state, min_indent)) - .trace("operator") + (move |arena: &'a Bump, state, min_indent| { + operator_help(arena, EExpr::Start, EExpr::BadOperator, state, min_indent) + }) + .trace("operator") } #[inline(always)] fn operator_help<'a, F, G, E>( + arena: &'a Bump, to_expectation: F, to_error: G, mut state: State<'a>, @@ -4030,20 +4034,17 @@ where G: Fn(&'a str, Position) -> E, E: 'a, { - match *state.bytes() { - [b'o', b'r', ..] => { - return Ok(( - MadeProgress, - OperatorOrDef::BinOp(BinOp::Or), - state.advance(2), - )) + let and_or = either( + parser::keyword(keyword::AND, EExpr::End), + parser::keyword(keyword::OR, EExpr::End), + ); + + match and_or.parse(arena, state.clone(), min_indent) { + Ok((MadeProgress, Either::First(_), state)) => { + return Ok((MadeProgress, OperatorOrDef::BinOp(BinOp::And), state)) } - [b'a', b'n', b'd', ..] => { - return Ok(( - MadeProgress, - OperatorOrDef::BinOp(BinOp::And), - state.advance(3), - )) + Ok((MadeProgress, Either::Second(_), state)) => { + return Ok((MadeProgress, OperatorOrDef::BinOp(BinOp::Or), state)) } _ => {} } diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index e437508c97..2f49efaa57 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -778,11 +778,11 @@ impl<'a> Normalize<'a> for Expr<'a> { Expr::If { if_thens, final_else, - indented_else, + indented_else: _, } => Expr::If { if_thens: if_thens.normalize(arena), final_else: arena.alloc(final_else.normalize(arena)), - indented_else, + indented_else: false, }, Expr::When(a, b) => Expr::When(arena.alloc(a.normalize(arena)), b.normalize(arena)), Expr::ParensAround(a) => { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.formatted.roc new file mode 100644 index 0000000000..c59a346494 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.formatted.roc @@ -0,0 +1,5 @@ +if !a! then + t +else + l +5 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.result-ast new file mode 100644 index 0000000000..4aaf9fb419 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.result-ast @@ -0,0 +1,72 @@ +@1-24 SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @1-22, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + Stmt( + @1-22 If { + if_thens: [ + ( + @3-6 UnaryOp( + @4-6 Var { + module_name: "", + ident: "a!", + }, + @3-4 Not, + ), + @11-12 SpaceBefore( + SpaceAfter( + Var { + module_name: "", + ident: "t", + }, + [ + Newline, + ], + ), + [ + Newline, + ], + ), + ), + ], + final_else: @21-22 SpaceBefore( + Var { + module_name: "", + ident: "l", + }, + [ + Newline, + ], + ), + indented_else: true, + }, + ), + ], + }, + @23-24 SpaceBefore( + Num( + "5", + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.roc new file mode 100644 index 0000000000..0b1a434a44 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/if_bang_then_bang_indented_else.expr.roc @@ -0,0 +1,5 @@ + if!a!then +t + else + l +5 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/if_then_weird_indent.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/if_then_weird_indent.expr.formatted.roc index 9753b07793..7c7f36a80f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/if_then_weird_indent.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/if_then_weird_indent.expr.formatted.roc @@ -2,7 +2,6 @@ if k then A - else - -e -r \ No newline at end of file +else + e + r \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.formatted.roc new file mode 100644 index 0000000000..f690f026d6 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.formatted.roc @@ -0,0 +1,3 @@ +E : i +E = h +0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.result-ast new file mode 100644 index 0000000000..f498b4d4b9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.result-ast @@ -0,0 +1,60 @@ +@0-11 SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + ], + regions: [ + @0-9, + ], + space_before: [ + Slice { start: 0, length: 0 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: Apply( + @0-1 Tag( + "E", + ), + [], + ), + ann_type: @2-3 BoundVariable( + "i", + ), + lines_between: [ + Newline, + Newline, + ], + body_pattern: @5-6 SpaceAfter( + Tag( + "E", + ), + [ + Newline, + ], + ), + body_expr: @8-9 Var { + module_name: "", + ident: "h", + }, + }, + ], + }, + @10-11 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.roc new file mode 100644 index 0000000000..6c497816d9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/sep_annotation.expr.roc @@ -0,0 +1,5 @@ +E:i + +E +=h +0 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.formatted.roc new file mode 100644 index 0000000000..20d5bd2e8b --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.formatted.roc @@ -0,0 +1,3 @@ +a +ands +d \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.result-ast new file mode 100644 index 0000000000..c38110d34e --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.result-ast @@ -0,0 +1,55 @@ +@0-11 SpaceAfter( + Defs( + Defs { + tags: [ + EitherIndex(2147483648), + EitherIndex(2147483649), + ], + regions: [ + @0-1, + @2-9, + ], + space_before: [ + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + ], + space_after: [ + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Stmt( + @0-1 Var { + module_name: "", + ident: "a", + }, + ), + Body( + @2-4 RecordDestructure( + [], + ), + @5-9 Var { + module_name: "", + ident: "ands", + }, + ), + ], + }, + @10-11 SpaceBefore( + Var { + module_name: "", + ident: "d", + }, + [ + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.roc new file mode 100644 index 0000000000..ce9da1f0dc --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/sneaky_and_expr.expr.roc @@ -0,0 +1,3 @@ +a +{}=ands +d diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index cb1532d994..4b1d3cff97 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -3061,7 +3061,7 @@ mod test_fmt { x, y }| a - + identity 43 " ), @@ -3069,7 +3069,7 @@ mod test_fmt { r" identity = |{ x, y }| a - + identity 43 " ), @@ -3083,7 +3083,7 @@ mod test_fmt { x, y } -> a - + identity 43 " ), @@ -3091,7 +3091,7 @@ mod test_fmt { r" identity = |{ x, y }| a - + identity 43 " ), @@ -3996,15 +3996,25 @@ mod test_fmt { #[test] fn early_return_else() { - expr_formats_same(indoc!( - r" - if foo then - bar - else + expr_formats_to( + indoc!( + r" + if foo then + bar + else - baz - " - )); + baz + " + ), + indoc!( + r" + if foo then + bar + else + baz + " + ), + ); expr_formats_to( indoc!( @@ -4019,9 +4029,8 @@ mod test_fmt { r" if thing then whatever - else - - too close + else + too close " ), ); @@ -4031,8 +4040,7 @@ mod test_fmt { r" if isGrowing plant then LetBe - else - + else Water " ), @@ -4040,9 +4048,8 @@ mod test_fmt { r" if isGrowing plant then LetBe - else - - Water + else + Water " ), ); diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 258c18e198..da86f6992c 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -472,6 +472,7 @@ mod test_snapshots { pass/highest_float.expr, pass/highest_int.expr, pass/i_over_not_g.expr, + pass/if_bang_then_bang_indented_else.expr, pass/if_bang_then_else_one_line.expr, pass/if_def.expr, pass/if_newline_then_negate_else_recordupdater.expr, @@ -697,12 +698,14 @@ mod test_snapshots { pass/return_record_update_comment_empty_fields.expr, pass/return_then_nested_parens.expr, pass/return_with_after.expr, + pass/sep_annotation.expr, pass/separate_defs.moduledefs, pass/single_arg_closure.expr, pass/single_arg_with_underscore_closure.expr, pass/single_question_binop_closure.expr, pass/single_question_binop_tag.expr, pass/single_underscore_closure.expr, + pass/sneaky_and_expr.expr, pass/sneaky_implements_in_opaque_fn_type.expr, pass/space_after_opt_field_pat.expr, pass/space_before_colon.full,