keep indent state in fmt Buf

This commit is contained in:
Joshua Warner 2021-12-02 16:28:57 -08:00
parent e872d00b13
commit e3f2f8d9a4
11 changed files with 313 additions and 182 deletions

View file

@ -1,8 +1,8 @@
use crate::annotation::{Formattable, Newlines, Parens};
use crate::def::fmt_def;
use crate::pattern::fmt_pattern;
use crate::spaces::{add_spaces, fmt_comments_only, fmt_spaces, newline, NewlineAt, INDENT};
use bumpalo::collections::String;
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
use crate::Buf;
use roc_module::called_via::{self, BinOp};
use roc_parse::ast::{
AssignedField, Base, Collection, CommentOrNewline, Expr, Pattern, WhenBranch,
@ -107,7 +107,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
fn format_with_options(
&self,
buf: &mut String<'a>,
buf: &mut Buf<'a>,
parens: Parens,
newlines: Newlines,
indent: u16,
@ -138,6 +138,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
if parens == Parens::NotNeeded && !sub_expr_requests_parens(sub_expr) {
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
} else {
buf.indent(indent);
buf.push('(');
sub_expr.format_with_options(
buf,
@ -145,6 +146,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
Newlines::Yes,
indent + INDENT,
);
buf.indent(indent);
buf.push(')');
}
}
@ -152,6 +154,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
fmt_str_literal(buf, *literal, indent);
}
Var { module_name, ident } => {
buf.indent(indent);
if !module_name.is_empty() {
buf.push_str(module_name);
buf.push('.');
@ -160,10 +163,12 @@ impl<'a> Formattable<'a> for Expr<'a> {
buf.push_str(ident);
}
Underscore(name) => {
buf.indent(indent);
buf.push('_');
buf.push_str(name);
}
Apply(loc_expr, loc_args, _) => {
buf.indent(indent);
if apply_needs_parens && !loc_args.is_empty() {
buf.push('(');
}
@ -176,7 +181,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
let arg_indent = indent + INDENT;
for loc_arg in loc_args.iter() {
newline(buf, arg_indent);
buf.newline();
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
}
} else {
@ -191,6 +196,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
}
}
Num(string) | Float(string) | GlobalTag(string) | PrivateTag(string) => {
buf.indent(indent);
buf.push_str(string)
}
NonBase10Int {
@ -198,6 +204,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
string,
is_negative,
} => {
buf.indent(indent);
if *is_negative {
buf.push('-');
}
@ -235,7 +242,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
let empty_line_before_return = empty_line_before_expr(&ret.value);
if !empty_line_before_return {
buf.push('\n');
buf.newline();
}
// Even if there were no defs, which theoretically should never happen,
@ -254,6 +261,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
}
BinOps(lefts, right) => fmt_bin_ops(buf, lefts, right, false, parens, indent),
UnaryOp(sub_expr, unary_op) => {
buf.indent(indent);
match &unary_op.value {
called_via::UnaryOp::Negate => {
buf.push('-');
@ -266,6 +274,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
sub_expr.format_with_options(buf, Parens::InApply, newlines, indent);
}
AccessorFunction(key) => {
buf.indent(indent);
buf.push('.');
buf.push_str(key);
}
@ -281,7 +290,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
}
}
fn format_str_segment<'a>(seg: &StrSegment<'a>, buf: &mut String<'a>, indent: u16) {
fn format_str_segment<'a>(seg: &StrSegment<'a>, buf: &mut Buf<'a>, indent: u16) {
use StrSegment::*;
match seg {
@ -311,7 +320,7 @@ fn format_str_segment<'a>(seg: &StrSegment<'a>, buf: &mut String<'a>, indent: u1
}
}
fn push_op(buf: &mut String, op: BinOp) {
fn push_op(buf: &mut Buf, op: BinOp) {
match op {
called_via::BinOp::Caret => buf.push('^'),
called_via::BinOp::Star => buf.push('*'),
@ -336,9 +345,10 @@ fn push_op(buf: &mut String, op: BinOp) {
}
}
pub fn fmt_str_literal<'a>(buf: &mut String<'a>, literal: StrLiteral<'a>, indent: u16) {
pub fn fmt_str_literal<'a>(buf: &mut Buf<'a>, literal: StrLiteral<'a>, indent: u16) {
use roc_parse::ast::StrLiteral::*;
buf.indent(indent);
buf.push('"');
match literal {
PlainLine(string) => {
@ -355,14 +365,14 @@ pub fn fmt_str_literal<'a>(buf: &mut String<'a>, literal: StrLiteral<'a>, indent
if lines.len() > 1 {
// Since we have multiple lines, format this with
// the `"""` symbols on their own lines, and the
newline(buf, indent);
buf.newline();
for segments in lines.iter() {
for seg in segments.iter() {
format_str_segment(seg, buf, indent);
}
newline(buf, indent);
buf.newline();
}
} else {
// This is a single-line block string, for example:
@ -387,7 +397,7 @@ pub fn fmt_str_literal<'a>(buf: &mut String<'a>, literal: StrLiteral<'a>, indent
}
fn fmt_bin_ops<'a>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
lefts: &'a [(Located<Expr<'a>>, Located<BinOp>)],
loc_right_side: &'a Located<Expr<'a>>,
part_of_multi_line_bin_ops: bool,
@ -404,7 +414,8 @@ fn fmt_bin_ops<'a>(
loc_left_side.format_with_options(buf, apply_needs_parens, Newlines::No, indent);
if is_multiline {
newline(buf, indent + INDENT)
buf.newline();
buf.indent(indent + INDENT);
} else {
buf.push(' ');
}
@ -417,9 +428,10 @@ fn fmt_bin_ops<'a>(
loc_right_side.format_with_options(buf, apply_needs_parens, Newlines::Yes, indent);
}
fn fmt_list<'a>(buf: &mut String<'a>, items: Collection<'a, &'a Located<Expr<'a>>>, indent: u16) {
fn fmt_list<'a>(buf: &mut Buf<'a>, items: Collection<'a, &'a Located<Expr<'a>>>, indent: u16) {
let loc_items = items.items;
let final_comments = items.final_comments();
buf.indent(indent);
if loc_items.is_empty() && final_comments.iter().all(|c| c.is_newline()) {
buf.push_str("[]");
} else {
@ -432,7 +444,7 @@ fn fmt_list<'a>(buf: &mut String<'a>, items: Collection<'a, &'a Located<Expr<'a>
// TODO?? These SpaceAfter/SpaceBefore litany seems overcomplicated
// Can we simplify this? Or at least move this in a separate function.
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
newline(buf, item_indent);
buf.newline();
fmt_comments_only(
buf,
spaces_above_expr.iter(),
@ -460,7 +472,7 @@ fn fmt_list<'a>(buf: &mut String<'a>, items: Collection<'a, &'a Located<Expr<'a>
}
Expr::SpaceAfter(sub_expr, spaces) => {
newline(buf, item_indent);
buf.newline();
sub_expr.format(buf, item_indent);
buf.push(',');
@ -469,7 +481,7 @@ fn fmt_list<'a>(buf: &mut String<'a>, items: Collection<'a, &'a Located<Expr<'a>
}
_ => {
newline(buf, item_indent);
buf.newline();
item.format_with_options(
buf,
Parens::NotNeeded,
@ -481,7 +493,8 @@ fn fmt_list<'a>(buf: &mut String<'a>, items: Collection<'a, &'a Located<Expr<'a>
}
}
fmt_comments_only(buf, final_comments.iter(), NewlineAt::Top, item_indent);
newline(buf, indent);
buf.newline();
buf.indent(indent);
buf.push(']');
} else {
// is_multiline == false
@ -526,12 +539,13 @@ fn empty_line_before_expr<'a>(expr: &'a Expr<'a>) -> bool {
}
fn fmt_when<'a>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
loc_condition: &'a Located<Expr<'a>>,
branches: &[&'a WhenBranch<'a>],
indent: u16,
) {
let is_multiline_condition = loc_condition.is_multiline();
buf.indent(indent);
buf.push_str(
"\
when",
@ -547,7 +561,7 @@ fn fmt_when<'a>(
NewlineAt::Top,
condition_indent,
);
newline(buf, condition_indent);
buf.newline();
match &expr_below {
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
expr_above.format(buf, condition_indent);
@ -557,7 +571,7 @@ fn fmt_when<'a>(
NewlineAt::Top,
condition_indent,
);
newline(buf, indent);
buf.newline();
}
_ => {
expr_below.format(buf, condition_indent);
@ -565,26 +579,25 @@ fn fmt_when<'a>(
}
}
_ => {
newline(buf, condition_indent);
buf.newline();
loc_condition.format(buf, condition_indent);
newline(buf, indent);
buf.newline();
}
}
buf.indent(indent);
} else {
buf.push(' ');
loc_condition.format(buf, indent);
buf.push(' ');
}
buf.push_str("is\n");
buf.push_str("is");
buf.newline();
let mut it = branches.iter().peekable();
while let Some(branch) = it.next() {
let patterns = &branch.patterns;
let expr = &branch.value;
let (first_pattern, rest) = patterns.split_first().unwrap();
if !has_newline_before(&first_pattern.value) {
add_spaces(buf, indent + INDENT);
}
let is_multiline = match rest.last() {
None => false,
Some(last_pattern) => first_pattern.region.start_line != last_pattern.region.end_line,
@ -598,7 +611,8 @@ fn fmt_when<'a>(
);
for when_pattern in rest {
if is_multiline {
newline(buf, indent + INDENT);
buf.newline();
buf.indent(indent + INDENT);
}
buf.push_str(" | ");
fmt_pattern(buf, &when_pattern.value, indent + INDENT, Parens::NotNeeded);
@ -610,7 +624,7 @@ fn fmt_when<'a>(
}
buf.push_str(" ->");
newline(buf, indent + INDENT * 2);
buf.newline();
match expr.value {
Expr::SpaceBefore(nested, spaces) => {
@ -633,24 +647,14 @@ fn fmt_when<'a>(
}
if it.peek().is_some() {
buf.push('\n');
buf.push('\n');
buf.newline();
buf.newline();
}
}
}
fn has_newline_before(value: &Pattern) -> bool {
match value {
Pattern::SpaceAfter(v, _) => has_newline_before(v),
Pattern::SpaceBefore(v, spaces) => {
v.is_multiline() && spaces.last().map(|s| s.is_newline()).unwrap_or(false)
}
_ => false,
}
}
fn fmt_expect<'a>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
condition: &'a Located<Expr<'a>>,
continuation: &'a Located<Expr<'a>>,
is_multiline: bool,
@ -669,7 +673,7 @@ fn fmt_expect<'a>(
}
fn fmt_if<'a>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
branches: &'a [(Located<Expr<'a>>, Located<Expr<'a>>)],
final_else: &'a Located<Expr<'a>>,
is_multiline: bool,
@ -689,6 +693,8 @@ fn fmt_if<'a>(
for (i, (loc_condition, loc_then)) in branches.iter().enumerate() {
let is_multiline_condition = loc_condition.is_multiline();
buf.indent(indent);
if i > 0 {
buf.push_str("else ");
}
@ -699,7 +705,7 @@ fn fmt_if<'a>(
match &loc_condition.value {
Expr::SpaceBefore(expr_below, spaces_above_expr) => {
fmt_comments_only(buf, spaces_above_expr.iter(), NewlineAt::Top, return_indent);
newline(buf, return_indent);
buf.newline();
match &expr_below {
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
@ -710,7 +716,7 @@ fn fmt_if<'a>(
NewlineAt::Top,
return_indent,
);
newline(buf, indent);
buf.newline();
}
_ => {
@ -720,18 +726,19 @@ fn fmt_if<'a>(
}
Expr::SpaceAfter(expr_above, spaces_below_expr) => {
newline(buf, return_indent);
buf.newline();
expr_above.format(buf, return_indent);
fmt_comments_only(buf, spaces_below_expr.iter(), NewlineAt::Top, return_indent);
newline(buf, indent);
buf.newline();
}
_ => {
newline(buf, return_indent);
buf.newline();
loc_condition.format(buf, return_indent);
newline(buf, indent);
buf.newline();
}
}
buf.indent(indent);
} else {
buf.push(' ');
loc_condition.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
@ -744,7 +751,7 @@ fn fmt_if<'a>(
match &loc_then.value {
Expr::SpaceBefore(expr_below, spaces_below) => {
// we want exactly one newline, user-inserted extra newlines are ignored.
newline(buf, return_indent);
buf.newline();
fmt_comments_only(buf, spaces_below.iter(), NewlineAt::Bottom, return_indent);
match &expr_below {
@ -757,7 +764,7 @@ fn fmt_if<'a>(
NewlineAt::Top,
return_indent,
);
newline(buf, indent);
buf.newline();
}
_ => {
@ -775,9 +782,10 @@ fn fmt_if<'a>(
}
}
buf.indent(indent);
if is_multiline {
buf.push_str("else");
newline(buf, return_indent);
buf.newline();
} else {
buf.push_str(" else ");
}
@ -786,13 +794,14 @@ fn fmt_if<'a>(
}
fn fmt_closure<'a>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
loc_patterns: &'a [Located<Pattern<'a>>],
loc_ret: &'a Located<Expr<'a>>,
indent: u16,
) {
use self::Expr::*;
buf.indent(indent);
buf.push('\\');
let arguments_are_multiline = loc_patterns
@ -812,9 +821,10 @@ fn fmt_closure<'a>(
loc_pattern.format(buf, indent);
if it.peek().is_some() {
buf.indent(indent);
if arguments_are_multiline {
buf.push(',');
newline(buf, indent);
buf.newline();
} else {
buf.push_str(", ");
}
@ -822,7 +832,8 @@ fn fmt_closure<'a>(
}
if arguments_are_multiline {
newline(buf, indent);
buf.newline();
buf.indent(indent);
} else {
buf.push(' ');
}
@ -856,7 +867,7 @@ fn fmt_closure<'a>(
}
fn fmt_backpassing<'a>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
loc_patterns: &'a [Located<Pattern<'a>>],
loc_body: &'a Located<Expr<'a>>,
loc_ret: &'a Located<Expr<'a>>,
@ -880,6 +891,7 @@ fn fmt_backpassing<'a>(
.any(|p| pattern_needs_parens_when_backpassing(&p.value));
if pattern_needs_parens {
buf.indent(indent);
buf.push('(');
}
@ -891,7 +903,7 @@ fn fmt_backpassing<'a>(
if it.peek().is_some() {
if arguments_are_multiline {
buf.push(',');
newline(buf, indent);
buf.newline();
} else {
buf.push_str(", ");
}
@ -903,7 +915,8 @@ fn fmt_backpassing<'a>(
}
if arguments_are_multiline {
newline(buf, indent);
buf.newline();
buf.indent(indent);
} else {
buf.push(' ');
}
@ -948,13 +961,14 @@ fn pattern_needs_parens_when_backpassing(pat: &Pattern) -> bool {
}
fn fmt_record<'a>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
update: Option<&'a Located<Expr<'a>>>,
fields: Collection<'a, Located<AssignedField<'a, Expr<'a>>>>,
indent: u16,
) {
let loc_fields = fields.items;
let final_comments = fields.final_comments();
buf.indent(indent);
if loc_fields.is_empty() && final_comments.iter().all(|c| c.is_newline()) {
buf.push_str("{}");
} else {
@ -990,7 +1004,7 @@ fn fmt_record<'a>(
fmt_comments_only(buf, final_comments.iter(), NewlineAt::Top, field_indent);
newline(buf, indent);
buf.newline();
} else {
// is_multiline == false
buf.push(' ');
@ -1010,12 +1024,13 @@ fn fmt_record<'a>(
};
// closes the initial bracket
buf.indent(indent);
buf.push('}');
}
}
fn format_field_multiline<'a, T>(
buf: &mut String<'a>,
buf: &mut Buf<'a>,
field: &AssignedField<'a, T>,
indent: u16,
separator_prefix: &str,
@ -1025,7 +1040,8 @@ fn format_field_multiline<'a, T>(
use self::AssignedField::*;
match field {
RequiredValue(name, spaces, ann) => {
newline(buf, indent);
buf.newline();
buf.indent(indent);
buf.push_str(name.value);
if !spaces.is_empty() {
@ -1038,7 +1054,8 @@ fn format_field_multiline<'a, T>(
buf.push(',');
}
OptionalValue(name, spaces, ann) => {
newline(buf, indent);
buf.newline();
buf.indent(indent);
buf.push_str(name.value);
if !spaces.is_empty() {
@ -1051,7 +1068,8 @@ fn format_field_multiline<'a, T>(
buf.push(',');
}
LabelOnly(name) => {
newline(buf, indent);
buf.newline();
buf.indent(indent);
buf.push_str(name.value);
buf.push(',');
}