Move PNC apply to separate Expr/Pattern variant

This commit is contained in:
Anthony Bullard 2025-01-07 17:22:19 -06:00
parent 96fc573b6b
commit 898b3f55e5
No known key found for this signature in database
70 changed files with 873 additions and 555 deletions

View file

@ -19,8 +19,8 @@ use roc_error_macros::internal_error;
use roc_parse::ast::{
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, Pattern, PatternApplyStyle, Spaces, SpacesBefore, StrLiteral,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
ModuleImportParams, Pattern, Spaces, SpacesBefore, StrLiteral, TypeAnnotation, TypeDef,
TypeHeader, ValueDef,
};
use roc_parse::expr::merge_spaces;
use roc_parse::header::Keyword;
@ -556,7 +556,8 @@ impl<'a> Formattable for TypeHeader<'a> {
Parens::NotNeeded,
indent,
self.vars.iter().any(|v| v.is_multiline()),
PatternApplyStyle::Whitespace,
false,
None,
);
buf.flags = old_flags;
}
@ -569,7 +570,6 @@ fn type_head_lift_spaces<'a, 'b: 'a>(
let pat = Pattern::Apply(
arena.alloc(Loc::at(head.name.region, Pattern::Tag(head.name.value))),
head.vars,
PatternApplyStyle::Whitespace,
);
pattern_lift_spaces(arena, &pat)
@ -891,9 +891,7 @@ impl<'a> Formattable for ValueDef<'a> {
fn ann_pattern_needs_parens(value: &Pattern<'_>) -> bool {
match value.extract_spaces().item {
Pattern::Tag(_) => true,
Pattern::Apply(func, _args, _style)
if matches!(func.extract_spaces().item, Pattern::Tag(..)) =>
{
Pattern::Apply(func, _args) if matches!(func.extract_spaces().item, Pattern::Tag(..)) => {
true
}
_ => false,
@ -1085,6 +1083,13 @@ pub fn fmt_body<'a>(
..
},
..,
)
| Expr::PncApply(
Loc {
value: Expr::Str(StrLiteral::Block(..)),
..
},
..,
) => {
buf.spaces(1);
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
@ -1148,6 +1153,7 @@ fn starts_with_expect_ident(expr: &Expr<'_>) -> bool {
// If we removed the `{}=` in this case, that would change the meaning
match expr {
Expr::Apply(inner, _, _) => starts_with_expect_ident(&inner.value),
Expr::PncApply(inner, _) => starts_with_expect_ident(&inner.value),
Expr::Var { module_name, ident } => {
module_name.is_empty() && (*ident == "expect" || *ident == "expect!")
}
@ -1162,6 +1168,7 @@ pub fn starts_with_block_string_literal(expr: &Expr<'_>) -> bool {
starts_with_block_string_literal(inner)
}
Expr::Apply(inner, _, _) => starts_with_block_string_literal(&inner.value),
Expr::PncApply(inner, _) => starts_with_block_string_literal(&inner.value),
Expr::TrySuffix { target: _, expr } => starts_with_block_string_literal(expr),
_ => false,
}

View file

@ -11,7 +11,7 @@ use crate::spaces::{
use crate::Buf;
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_module::called_via::{self, BinOp, CalledVia, UnaryOp};
use roc_module::called_via::{self, BinOp, UnaryOp};
use roc_parse::ast::{
AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern, Spaceable,
Spaces, SpacesAfter, SpacesBefore, TryTarget, WhenBranch,
@ -91,13 +91,24 @@ fn format_expr_only(
buf.indent(indent);
buf.push_str("try");
}
Expr::Apply(loc_expr, loc_args, called_via::CalledVia::ParensAndCommas) => {
fmt_apply(loc_expr, loc_args, indent, buf, true);
Expr::PncApply(
loc_expr @ Loc {
value: Expr::Dbg, ..
},
loc_args,
) => {
fmt_apply(&loc_expr, loc_args.items, indent, buf);
}
Expr::PncApply(loc_expr, loc_args) => {
fmt_pnc_apply(loc_expr, loc_args, indent, buf);
}
Expr::Apply(loc_expr, loc_args, _) => {
let apply_needs_parens = parens == Parens::InApply || parens == Parens::InApplyLastArg;
if buf.flags().parens_and_commas || !apply_needs_parens || loc_args.is_empty() {
fmt_apply(loc_expr, loc_args, indent, buf, false);
if buf.flags().parens_and_commas {
fmt_pnc_apply(loc_expr, &Collection::with_items(loc_args), indent, buf);
} else if !apply_needs_parens || loc_args.is_empty() {
println!("No need parens");
fmt_apply(loc_expr, loc_args, indent, buf);
} else {
fmt_parens(item, buf, indent);
}
@ -519,6 +530,12 @@ pub fn expr_is_multiline(me: &Expr<'_>, comments_only: bool) -> bool {
.iter()
.any(|loc_arg| expr_is_multiline(&loc_arg.value, comments_only))
}
Expr::PncApply(loc_expr, args) => {
expr_is_multiline(&loc_expr.value, comments_only)
|| args
.iter()
.any(|loc_arg| expr_is_multiline(&loc_arg.value, comments_only))
}
Expr::DbgStmt { .. } => true,
Expr::LowLevelDbg(_, _, _) => {
@ -639,13 +656,28 @@ fn requires_space_after_unary(item: &Expr<'_>) -> bool {
}
}
fn fmt_pnc_apply(
loc_expr: &Loc<Expr<'_>>,
loc_args: &Collection<'_, &Loc<Expr<'_>>>,
indent: u16,
buf: &mut Buf<'_>,
) {
let expr = expr_lift_spaces(Parens::InApply, buf.text.bump(), &loc_expr.value);
if !expr.before.is_empty() {
format_spaces(buf, expr.before, Newlines::Yes, indent);
}
expr.item
.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
fmt_expr_collection(buf, indent, Braces::Round, *loc_args, Newlines::No);
}
fn fmt_apply(
loc_expr: &Loc<Expr<'_>>,
loc_args: &[&Loc<Expr<'_>>],
indent: u16,
buf: &mut Buf<'_>,
expr_used_commas_and_parens: bool,
) {
// should_reflow_outdentable, aka should we transform this:
//
@ -665,7 +697,6 @@ fn fmt_apply(
// 2,
// ]
// ```
let use_commas_and_parens = expr_used_commas_and_parens || buf.flags().parens_and_commas;
let should_reflow_outdentable = loc_expr.extract_spaces().after.is_empty()
&& except_last(loc_args).all(|a| !a.is_multiline())
&& loc_args
@ -701,23 +732,17 @@ fn fmt_apply(
if !expr.before.is_empty() {
format_spaces(buf, expr.before, Newlines::Yes, indent);
}
expr.item
.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
if use_commas_and_parens {
buf.push('(');
}
let mut last_after = expr.after;
for (i, loc_arg) in loc_args.iter().enumerate() {
let is_last_arg = i == loc_args.len() - 1;
let is_first_arg = i == 0;
let arg = expr_lift_spaces(
if use_commas_and_parens {
Parens::NotNeeded
} else if is_last_arg {
if is_last_arg {
Parens::InApplyLastArg
} else {
Parens::InApply
@ -738,7 +763,7 @@ fn fmt_apply(
last_after = arg.after;
if needs_indent {
buf.ensure_ends_with_newline();
} else if !(is_first_arg && use_commas_and_parens) {
} else {
buf.spaces(1);
}
@ -746,34 +771,13 @@ fn fmt_apply(
{
fmt_parens(&arg.item, buf, arg_indent);
} else {
format_expr_only(
&arg.item,
buf,
if use_commas_and_parens {
Parens::NotNeeded
} else {
Parens::InApply
},
Newlines::Yes,
arg_indent,
);
}
if use_commas_and_parens && (!is_last_arg || needs_indent) {
buf.push(',');
format_expr_only(&arg.item, buf, Parens::InApply, Newlines::Yes, arg_indent);
}
}
if !last_after.is_empty() {
format_spaces(buf, last_after, Newlines::Yes, arg_indent);
}
if use_commas_and_parens {
if needs_indent {
buf.ensure_ends_with_newline();
buf.indent(indent);
}
buf.push(')');
}
}
fn is_outdentable_collection(expr: &Expr<'_>) -> bool {
@ -1038,16 +1042,12 @@ pub fn expr_lift_spaces<'a, 'b: 'a>(
expr: &Expr<'b>,
) -> Spaces<'a, Expr<'a>> {
match expr {
Expr::Apply(func, args, CalledVia::ParensAndCommas) => {
let lifted = expr_lift_spaces_before(Parens::NotNeeded, arena, &func.value);
Expr::PncApply(func, args) => {
let lifted = expr_lift_spaces_before(Parens::InApply, arena, &func.value);
Spaces {
before: lifted.before,
item: Expr::Apply(
arena.alloc(Loc::at(func.region, lifted.item)),
args,
CalledVia::ParensAndCommas,
),
item: Expr::PncApply(arena.alloc(Loc::at(func.region, lifted.item)), *args),
after: arena.alloc([]),
}
}

View file

@ -7,8 +7,7 @@ use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
use crate::Buf;
use bumpalo::Bump;
use roc_parse::ast::{
Base, CommentOrNewline, Pattern, PatternApplyStyle, PatternAs, Spaceable, Spaces, SpacesAfter,
SpacesBefore,
Base, CommentOrNewline, Pattern, PatternAs, Spaceable, Spaces, SpacesAfter, SpacesBefore,
};
use roc_parse::expr::merge_spaces;
use roc_region::all::Loc;
@ -73,9 +72,14 @@ impl<'a> Formattable for Pattern<'a> {
}
},
Pattern::StrLiteral(literal) => is_str_multiline(literal),
Pattern::Apply(pat, args, _) => {
Pattern::Apply(pat, args) => {
pat.is_multiline() || args.iter().any(|a| a.is_multiline())
}
Pattern::PncApply(pat, args) => {
pat.is_multiline()
|| args.iter().any(|a| a.is_multiline())
|| !args.final_comments().is_empty()
}
Pattern::Identifier { .. }
| Pattern::Tag(_)
@ -163,22 +167,19 @@ fn fmt_pattern_only(
buf.indent(indent);
buf.push_str(name);
}
Pattern::Apply(
loc_pattern,
loc_arg_patterns,
style @ PatternApplyStyle::ParensAndCommas,
) => {
Pattern::PncApply(loc_pattern, loc_arg_patterns) => {
pattern_fmt_apply(
buf,
loc_pattern.value,
loc_arg_patterns,
loc_arg_patterns.items,
Parens::NotNeeded,
indent,
is_multiline,
*style,
true,
Some(loc_arg_patterns.final_comments()),
);
}
Pattern::Apply(loc_pattern, loc_arg_patterns, style) => {
Pattern::Apply(loc_pattern, loc_arg_patterns) => {
pattern_fmt_apply(
buf,
loc_pattern.value,
@ -186,7 +187,8 @@ fn fmt_pattern_only(
parens,
indent,
is_multiline,
*style,
false,
None,
);
}
Pattern::RecordDestructure(loc_patterns) => {
@ -462,10 +464,10 @@ pub fn pattern_fmt_apply(
parens: Parens,
indent: u16,
is_multiline: bool,
style: PatternApplyStyle,
is_pnc: bool,
final_comments: Option<&[CommentOrNewline]>,
) {
let use_commas_and_parens =
matches!(style, PatternApplyStyle::ParensAndCommas) || buf.flags().parens_and_commas;
let use_commas_and_parens = is_pnc || buf.flags().parens_and_commas;
buf.indent(indent);
// Sometimes, an Apply pattern needs parens around it.
// In particular when an Apply's argument is itself an Apply (> 0) arguments
@ -560,7 +562,7 @@ pub fn pattern_fmt_apply(
buf.push_str("(implements)");
} else {
fmt_pattern_only(&arg.item, buf, parens, indent_more, arg.item.is_multiline());
if use_commas_and_parens && (!is_last_arg || add_newlines) {
if use_commas_and_parens && (!is_last_arg || is_multiline) {
buf.push(',');
}
}
@ -570,6 +572,13 @@ pub fn pattern_fmt_apply(
add_newlines |= was_multiline;
}
if let Some(comments) = final_comments {
if !is_multiline {
fmt_comments_only(buf, comments.iter(), NewlineAt::Bottom, indent_more);
} else {
fmt_spaces(buf, comments.iter(), indent_more);
}
}
if !last_after.is_empty() {
if !is_multiline {
fmt_comments_only(buf, last_after.iter(), NewlineAt::Bottom, indent_more)
@ -578,7 +587,21 @@ pub fn pattern_fmt_apply(
}
}
if parens || use_commas_and_parens {
if use_commas_and_parens {
if is_multiline {
buf.ensure_ends_with_newline();
buf.indent(indent);
}
if buf.ends_with_newline() {
buf.indent(indent);
}
if buf.ends_with_newline() {
buf.indent(indent);
}
buf.push(')');
}
if parens {
buf.push(')');
}
}
@ -632,8 +655,9 @@ fn pattern_prec(pat: Pattern<'_>) -> Prec {
| Pattern::SingleQuote(_)
| Pattern::Tuple(..)
| Pattern::List(..)
| Pattern::ListRest(_) => Prec::Term,
Pattern::Apply(_, _, _) | Pattern::As(_, _) => Prec::Apply,
| Pattern::ListRest(_)
| Pattern::PncApply(_, _) => Prec::Term,
Pattern::Apply(_, _) | Pattern::As(_, _) => Prec::Apply,
Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => pattern_prec(*inner),
Pattern::Malformed(_) | Pattern::MalformedIdent(..) => Prec::Term,
}
@ -653,7 +677,7 @@ pub fn pattern_lift_spaces<'a, 'b: 'a>(
pat: &Pattern<'b>,
) -> Spaces<'a, Pattern<'a>> {
match pat {
Pattern::Apply(func, args, style) => {
Pattern::Apply(func, args) => {
let func_lifted = pattern_lift_spaces(arena, &func.value);
let args = arena.alloc_slice_copy(args);
@ -688,10 +712,19 @@ pub fn pattern_lift_spaces<'a, 'b: 'a>(
};
Spaces {
before,
item: Pattern::Apply(arena.alloc(func), args, *style),
item: Pattern::Apply(arena.alloc(func), args),
after,
}
}
Pattern::PncApply(func, args) => {
let func_lifted = pattern_lift_spaces_before(arena, &func.value);
Spaces {
before: func_lifted.before,
item: Pattern::PncApply(arena.alloc(func), *args),
after: &[],
}
}
Pattern::OptionalField(name, expr) => {
let lifted = expr_lift_spaces_after(Parens::NotNeeded, arena, &expr.value);
Spaces {
@ -748,7 +781,9 @@ fn handle_multiline_str_spaces<'a>(pat: &Pattern<'_>, before: &mut &'a [CommentO
fn starts_with_block_str(item: &Pattern<'_>) -> bool {
match item {
Pattern::As(inner, _) | Pattern::Apply(inner, _, _) => starts_with_block_str(&inner.value),
Pattern::As(inner, _) | Pattern::Apply(inner, _) | Pattern::PncApply(inner, _) => {
starts_with_block_str(&inner.value)
}
Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => {
starts_with_block_str(inner)
}