diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 1a785f9671..a5c353fecb 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -1,7 +1,7 @@ use crate::ast::CommentOrNewline; use crate::ast::Spaceable; use crate::parser::{ - self, and, BadInputError, Col, Parser, + self, and, backtrackable, BadInputError, Col, Parser, Progress::{self, *}, Row, State, }; @@ -31,38 +31,75 @@ where space0_e(min_indent, space_problem, indent_after_problem), ), ), - move |arena: &'a Bump, - tuples: ( - &'a [CommentOrNewline<'a>], - (Located, &'a [CommentOrNewline<'a>]), - )| { - let (spaces_before, (loc_val, spaces_after)) = tuples; - - if spaces_before.is_empty() { - if spaces_after.is_empty() { - loc_val - } else { - arena - .alloc(loc_val.value) - .with_spaces_after(spaces_after, loc_val.region) - } - } else if spaces_after.is_empty() { - arena - .alloc(loc_val.value) - .with_spaces_before(spaces_before, loc_val.region) - } else { - let wrapped_expr = arena - .alloc(loc_val.value) - .with_spaces_after(spaces_after, loc_val.region); - - arena - .alloc(wrapped_expr.value) - .with_spaces_before(spaces_before, wrapped_expr.region) - } - }, + spaces_around_help, ) } +pub fn space0_before_optional_after<'a, P, S, E>( + parser: P, + min_indent: u16, + space_problem: fn(BadInputError, Row, Col) -> E, + indent_before_problem: fn(Row, Col) -> E, + indent_after_problem: fn(Row, Col) -> E, +) -> impl Parser<'a, Located, E> +where + S: Spaceable<'a>, + S: 'a, + P: Parser<'a, Located, E>, + P: 'a, + E: 'a, +{ + parser::map_with_arena( + and( + space0_e(min_indent, space_problem, indent_before_problem), + and( + parser, + one_of![ + backtrackable(space0_e(min_indent, space_problem, indent_after_problem)), + succeed!(&[] as &[_]), + ], + ), + ), + spaces_around_help, + ) +} + +fn spaces_around_help<'a, S>( + arena: &'a Bump, + tuples: ( + &'a [CommentOrNewline<'a>], + (Located, &'a [CommentOrNewline<'a>]), + ), +) -> Located +where + S: Spaceable<'a>, + S: 'a, +{ + let (spaces_before, (loc_val, spaces_after)) = tuples; + + if spaces_before.is_empty() { + if spaces_after.is_empty() { + loc_val + } else { + arena + .alloc(loc_val.value) + .with_spaces_after(spaces_after, loc_val.region) + } + } else if spaces_after.is_empty() { + arena + .alloc(loc_val.value) + .with_spaces_before(spaces_before, loc_val.region) + } else { + let wrapped_expr = arena + .alloc(loc_val.value) + .with_spaces_after(spaces_after, loc_val.region); + + arena + .alloc(wrapped_expr.value) + .with_spaces_before(spaces_before, wrapped_expr.region) + } +} + pub fn space0_before_e<'a, P, S, E>( parser: P, min_indent: u16, diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 6afb7049af..d769b95b33 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1273,7 +1273,7 @@ macro_rules! collection_trailing_sep_e { and!( $crate::parser::trailing_sep_by0( $delimiter, - $crate::blankspace::space0_around_ee( + $crate::blankspace::space0_before_optional_after( $elem, $min_indent, $space_problem, @@ -1299,6 +1299,15 @@ macro_rules! collection_trailing_sep_e { }; } +#[macro_export] +macro_rules! succeed { + ($value:expr) => { + move |_arena: &'a bumpalo::Bump, state: $crate::parser::State<'a>| { + Ok((NoProgress, $value, state)) + } + }; +} + #[macro_export] macro_rules! and { ($p1:expr, $p2:expr) => { diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index ec45197666..edaab151aa 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -147,7 +147,7 @@ fn loc_type_in_parens<'a>( TInParens::IndentOpen, TInParens::IndentEnd, ), - word1(b')', TInParens::End) + word1(b')', TInParens::IndentEnd) ) } diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 44a9b74dbe..681e9bd323 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -1750,7 +1750,7 @@ fn to_precord_report<'a>( } PRecord::IndentEnd(row, col) => { - match next_line_starts_with_close_curly(alloc.src_lines, row.saturating_sub(1)) { + match next_line_starts_with_close_curly(alloc.src_lines, row) { Some((curly_row, curly_col)) => { let surroundings = Region::from_rows_cols(start_row, start_col, curly_row, curly_col); @@ -1895,7 +1895,7 @@ fn to_pattern_in_parens_report<'a>( } PInParens::IndentEnd(row, col) => { - match next_line_starts_with_close_parenthesis(alloc.src_lines, row.saturating_sub(1)) { + match next_line_starts_with_close_parenthesis(alloc.src_lines, row) { Some((curly_row, curly_col)) => { let surroundings = Region::from_rows_cols(start_row, start_col, curly_row, curly_col); @@ -2257,7 +2257,7 @@ fn to_trecord_report<'a>( } TRecord::IndentEnd(row, col) => { - match next_line_starts_with_close_curly(alloc.src_lines, row.saturating_sub(1)) { + match next_line_starts_with_close_curly(alloc.src_lines, row) { Some((curly_row, curly_col)) => { let surroundings = Region::from_rows_cols(start_row, start_col, curly_row, curly_col); @@ -2478,7 +2478,7 @@ fn to_ttag_union_report<'a>( } TTagUnion::IndentEnd(row, col) => { - match next_line_starts_with_close_square_bracket(alloc.src_lines, row - 1) { + match next_line_starts_with_close_square_bracket(alloc.src_lines, row) { Some((curly_row, curly_col)) => { let surroundings = Region::from_rows_cols(start_row, start_col, curly_row, curly_col); @@ -2682,7 +2682,7 @@ fn to_tinparens_report<'a>( } TInParens::IndentEnd(row, col) => { - match next_line_starts_with_close_square_bracket(alloc.src_lines, row - 1) { + match next_line_starts_with_close_parenthesis(alloc.src_lines, row) { Some((curly_row, curly_col)) => { let surroundings = Region::from_rows_cols(start_row, start_col, curly_row, curly_col); @@ -2694,7 +2694,7 @@ fn to_tinparens_report<'a>( ), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ - alloc.reflow("I need this square bracket to be indented more. Try adding more spaces before it!"), + alloc.reflow("I need this parenthesis to be indented more. Try adding more spaces before it!"), ]), ]); @@ -2714,9 +2714,9 @@ fn to_tinparens_report<'a>( ), alloc.region_with_subregion(surroundings, region), alloc.concat(vec![ - alloc.reflow("I was expecting to see a closing square "), - alloc.reflow("bracket before this, so try adding a "), - alloc.parser_suggestion("]"), + alloc.reflow("I was expecting to see a parenthesis "), + alloc.reflow("before this, so try adding a "), + alloc.parser_suggestion(")"), alloc.reflow(" and see if that helps?"), ]), note_for_tag_union_type_indent(alloc), diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index ebb6654cb2..492bf1a6ef 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -4526,15 +4526,17 @@ mod test_reporting { indoc!( r#" ── UNFINISHED PARENTHESES ────────────────────────────────────────────────────── - + I am partway through parsing a type in parentheses, but I got stuck here: - + 1│ f : ( I64 ^ - - I was expecting to see a closing parenthesis before this, so try - adding a ) and see if that helps? + + I was expecting to see a parenthesis before this, so try adding a ) + and see if that helps? + + Note: I may be confused by indentation "# ), ) @@ -4979,12 +4981,12 @@ mod test_reporting { indoc!( r#" ── UNFINISHED PATTERN ────────────────────────────────────────────────────────── - + I just started parsing a pattern, but I got stuck here: - + 2│ Just 4 | -> ^ - + Note: I may be confused by indentation "# ), @@ -5125,13 +5127,13 @@ mod test_reporting { indoc!( r#" ── SYNTAX PROBLEM ────────────────────────────────────────────────────────────── - + I got stuck here: - + 1│ when 4 is 2│ 5 -> 2 ^ - + Whatever I am running into is confusing me a lot! Normally I can give fairly specific hints, but something is really tripping me up this time. @@ -5176,13 +5178,13 @@ mod test_reporting { indoc!( r#" ── SYNTAX PROBLEM ────────────────────────────────────────────────────────────── - + I got stuck here: - + 1│ when 4 is 2│ 5 -> 2 ^ - + Whatever I am running into is confusing me a lot! Normally I can give fairly specific hints, but something is really tripping me up this time. @@ -5204,26 +5206,26 @@ mod test_reporting { indoc!( r#" ── UNEXPECTED ARROW ──────────────────────────────────────────────────────────── - + I am parsing a `when` expression right now, but this arrow is confusing me: - + 3│ 2 -> 2 ^^ - + It makes sense to see arrows around here, so I suspect it is something earlier.Maybe this pattern is indented a bit farther from the previous patterns? - + Note: Here is an example of a valid `when` expression for reference. - + when List.first plants is Ok n -> n - + Err _ -> 200 - + Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! "# @@ -5876,14 +5878,14 @@ mod test_reporting { indoc!( r#" ── UNKNOWN OPERATOR ──────────────────────────────────────────────────────────── - + This looks like an operator, but it's not one I recognize! - + 1│ main = 5 -> 3 ^^ - + The arrow -> is only used to define cases in a `when`. - + when color is Red -> "stop!" Green -> "go!" @@ -6208,4 +6210,97 @@ mod test_reporting { ), ) } + + #[test] + fn outdented_alias() { + report_problem_as( + indoc!( + r#" + Box item : [ + Box item, + Items item item + ] + + 4 + "# + ), + indoc!( + r#" + ── NEED MORE INDENTATION ─────────────────────────────────────────────────────── + + I am partway through parsing a tag union type, but I got stuck here: + + 1│ Box item : [ + 2│ Box item, + 3│ Items item item + 4│ ] + ^ + + I need this square bracket to be indented more. Try adding more spaces + before it! + "# + ), + ) + } + + #[test] + fn outdented_in_parens() { + report_problem_as( + indoc!( + r#" + Box : ( + Str + ) + + 4 + "# + ), + indoc!( + r#" + ── NEED MORE INDENTATION ─────────────────────────────────────────────────────── + + I am partway through parsing a type in parentheses, but I got stuck + here: + + 1│ Box : ( + 2│ Str + 3│ ) + ^ + + I need this parenthesis to be indented more. Try adding more spaces + before it! + "# + ), + ) + } + + #[test] + fn outdented_record() { + report_problem_as( + indoc!( + r#" + Box : { + id: Str + } + + 4 + "# + ), + indoc!( + r#" + ── NEED MORE INDENTATION ─────────────────────────────────────────────────────── + + I am partway through parsing a record type, but I got stuck here: + + 1│ Box : { + 2│ id: Str + 3│ } + ^ + + I need this curly brace to be indented more. Try adding more spaces + before it! + "# + ), + ) + } }