Ensure formatted multiline patterns can be parsed

This commit is contained in:
Joshua Warner 2024-12-02 20:22:41 -08:00
parent 78cda703d9
commit dfcb7a0c3b
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
6 changed files with 168 additions and 39 deletions

View file

@ -94,7 +94,7 @@ impl<'a> Formattable for Pattern<'a> {
}
fn format_with_options(&self, buf: &mut Buf, parens: Parens, _newlines: Newlines, indent: u16) {
fmt_pattern_inner(self, buf, parens, indent, self.is_multiline());
fmt_pattern_inner(self, buf, parens, indent, self.is_multiline(), false);
}
}
@ -105,31 +105,64 @@ fn fmt_pattern_inner(
indent: u16,
outer_is_multiline: bool,
) {
use self::Pattern::*;
force_newline_at_start: bool,
) -> bool {
let me = pattern_lift_spaces(buf.text.bump(), pat);
let mut was_multiline = me.item.is_multiline();
if !me.before.is_empty() {
if !outer_is_multiline {
was_multiline |= me.before.iter().any(|s| s.is_comment());
fmt_comments_only(buf, me.before.iter(), NewlineAt::Bottom, indent)
} else {
was_multiline |= true;
fmt_spaces(buf, me.before.iter(), indent);
}
}
if force_newline_at_start {
buf.ensure_ends_with_newline();
}
let is_multiline = me.item.is_multiline();
fmt_pattern_only(me, buf, indent, parens, is_multiline);
if !me.after.is_empty() {
if starts_with_inline_comment(me.after.iter()) {
buf.spaces(1);
}
if !outer_is_multiline {
was_multiline |= me.before.iter().any(|s| s.is_comment());
fmt_comments_only(buf, me.after.iter(), NewlineAt::Bottom, indent)
} else {
was_multiline |= true;
fmt_spaces(buf, me.after.iter(), indent);
}
}
was_multiline
}
fn fmt_pattern_only(
me: Spaces<'_, Pattern<'_>>,
buf: &mut Buf<'_>,
indent: u16,
parens: Parens,
is_multiline: bool,
) {
match me.item {
Identifier { ident: string } => {
Pattern::Identifier { ident: string } => {
buf.indent(indent);
snakify_camel_ident(buf, string);
}
Tag(name) | OpaqueRef(name) => {
Pattern::Tag(name) | Pattern::OpaqueRef(name) => {
buf.indent(indent);
buf.push_str(name);
}
Apply(loc_pattern, loc_arg_patterns) => {
Pattern::Apply(loc_pattern, loc_arg_patterns) => {
buf.indent(indent);
// Sometimes, an Apply pattern needs parens around it.
// In particular when an Apply's argument is itself an Apply (> 0) arguments
@ -155,7 +188,7 @@ fn fmt_pattern_inner(
}
}
fmt_pattern_inner(&pat.item, buf, Parens::InApply, indent, is_multiline);
fmt_pattern_inner(&pat.item, buf, Parens::InApply, indent, is_multiline, false);
if !pat.after.is_empty() {
if !is_multiline {
@ -165,22 +198,26 @@ fn fmt_pattern_inner(
}
}
let mut add_newlines = false;
for loc_arg in loc_arg_patterns.iter() {
buf.spaces(1);
fmt_pattern_inner(
let was_multiline = fmt_pattern_inner(
&loc_arg.value,
buf,
Parens::InApply,
indent_more,
is_multiline,
add_newlines,
);
add_newlines |= was_multiline;
}
if parens {
buf.push(')');
}
}
RecordDestructure(loc_patterns) => {
Pattern::RecordDestructure(loc_patterns) => {
buf.indent(indent);
buf.push_str("{");
@ -198,7 +235,14 @@ fn fmt_pattern_inner(
}
}
fmt_pattern_inner(&item.item, buf, Parens::NotNeeded, indent, is_multiline);
fmt_pattern_inner(
&item.item,
buf,
Parens::NotNeeded,
indent,
is_multiline,
false,
);
let is_multiline = item.item.is_multiline();
@ -228,7 +272,7 @@ fn fmt_pattern_inner(
buf.push_str("}");
}
RequiredField(name, loc_pattern) => {
Pattern::RequiredField(name, loc_pattern) => {
buf.indent(indent);
snakify_camel_ident(buf, name);
buf.push_str(":");
@ -239,10 +283,11 @@ fn fmt_pattern_inner(
Parens::NotNeeded,
indent,
is_multiline,
false,
);
}
OptionalField(name, loc_pattern) => {
Pattern::OptionalField(name, loc_pattern) => {
buf.indent(indent);
snakify_camel_ident(buf, name);
buf.push_str(" ?");
@ -250,11 +295,11 @@ fn fmt_pattern_inner(
loc_pattern.format(buf, indent);
}
NumLiteral(string) => {
Pattern::NumLiteral(string) => {
buf.indent(indent);
buf.push_str(string);
}
NonBase10Literal {
Pattern::NonBase10Literal {
base,
string,
is_negative,
@ -273,21 +318,21 @@ fn fmt_pattern_inner(
buf.push_str(string);
}
FloatLiteral(string) => {
Pattern::FloatLiteral(string) => {
buf.indent(indent);
buf.push_str(string);
}
StrLiteral(literal) => fmt_str_literal(buf, literal, indent),
SingleQuote(string) => {
Pattern::StrLiteral(literal) => fmt_str_literal(buf, literal, indent),
Pattern::SingleQuote(string) => {
buf.indent(indent);
format_sq_literal(buf, string);
}
Underscore(name) => {
Pattern::Underscore(name) => {
buf.indent(indent);
buf.push('_');
buf.push_str(name);
}
Tuple(loc_patterns) => {
Pattern::Tuple(loc_patterns) => {
buf.indent(indent);
buf.push_str("(");
@ -299,6 +344,7 @@ fn fmt_pattern_inner(
Parens::NotNeeded,
indent,
is_multiline,
false,
);
if it.peek().is_some() {
@ -311,7 +357,7 @@ fn fmt_pattern_inner(
buf.indent(indent);
buf.push_str(")");
}
List(loc_patterns) => {
Pattern::List(loc_patterns) => {
buf.indent(indent);
buf.push_str("[");
@ -323,6 +369,7 @@ fn fmt_pattern_inner(
Parens::NotNeeded,
indent,
is_multiline,
false,
);
if it.peek().is_some() {
@ -335,7 +382,7 @@ fn fmt_pattern_inner(
buf.indent(indent);
buf.push_str("]");
}
ListRest(opt_pattern_as) => {
Pattern::ListRest(opt_pattern_as) => {
buf.indent(indent);
buf.push_str("..");
@ -347,7 +394,7 @@ fn fmt_pattern_inner(
}
}
As(pattern, pattern_as) => {
Pattern::As(pattern, pattern_as) => {
let needs_parens = parens == Parens::InAsPattern;
if needs_parens {
@ -365,14 +412,16 @@ fn fmt_pattern_inner(
}
}
SpaceBefore(..) | SpaceAfter(..) => unreachable!("handled by lift_spaces"),
Pattern::SpaceBefore(..) | Pattern::SpaceAfter(..) => {
unreachable!("handled by lift_spaces")
}
// Malformed
Malformed(string) | MalformedIdent(string, _) => {
Pattern::Malformed(string) | Pattern::MalformedIdent(string, _) => {
buf.indent(indent);
buf.push_str(string);
}
QualifiedIdentifier { module_name, ident } => {
Pattern::QualifiedIdentifier { module_name, ident } => {
buf.indent(indent);
if !module_name.is_empty() {
buf.push_str(module_name);
@ -382,18 +431,6 @@ fn fmt_pattern_inner(
snakify_camel_ident(buf, ident);
}
}
if !me.after.is_empty() {
if starts_with_inline_comment(me.after.iter()) {
buf.spaces(1);
}
if !outer_is_multiline {
fmt_comments_only(buf, me.after.iter(), NewlineAt::Bottom, indent)
} else {
fmt_spaces(buf, me.after.iter(), indent);
}
}
}
pub fn starts_with_inline_comment<'a, I: IntoIterator<Item = &'a CommentOrNewline<'a>>>(

View file

@ -0,0 +1,5 @@
1 (0 #
f)
(0 #
f) : f
e

View file

@ -0,0 +1,80 @@
SpaceAfter(
Defs(
Defs {
tags: [
EitherIndex(2147483648),
],
regions: [
@0-19,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [],
value_defs: [
Annotation(
@0-1 Apply(
@0-1 NumLiteral(
"1",
),
[
@2-8 Apply(
@3-4 SpaceAfter(
NumLiteral(
"0",
),
[
LineComment(
"",
),
],
),
[
@7-8 Identifier {
ident: "f",
},
],
),
@10-16 Apply(
@11-12 SpaceAfter(
NumLiteral(
"0",
),
[
LineComment(
"",
),
],
),
[
@15-16 Identifier {
ident: "f",
},
],
),
],
),
@18-19 BoundVariable(
"f",
),
),
],
},
@20-21 SpaceBefore(
Var {
module_name: "",
ident: "e",
},
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -0,0 +1,4 @@
1((0#
)f)((0#
)f):f
e

View file

@ -1,4 +1,6 @@
1
"""
""" "^" 2 : A
"""
"^"
2 : A
""

View file

@ -442,6 +442,7 @@ mod test_snapshots {
pass/list_patterns.expr,
pass/lowest_float.expr,
pass/lowest_int.expr,
pass/mega_parens_pat.expr,
pass/min_parens_number.expr,
pass/minimal_app_header.header,
pass/minus_minus_block_string.expr,