diff --git a/compiler/fmt/src/annotation.rs b/compiler/fmt/src/annotation.rs index beb0aef9ba..c51c729860 100644 --- a/compiler/fmt/src/annotation.rs +++ b/compiler/fmt/src/annotation.rs @@ -1,6 +1,6 @@ use crate::spaces::{fmt_comments_only, fmt_condition_spaces, newline, INDENT}; use bumpalo::collections::String; -use roc_parse::ast::{AssignedField, Tag, TypeAnnotation}; +use roc_parse::ast::{AssignedField, Expr, Tag, TypeAnnotation}; use roc_region::all::Located; #[derive(PartialEq, Eq)] @@ -10,19 +10,31 @@ pub enum Parens { InApply, } +#[derive(PartialEq, Eq)] +pub enum Newlines { + Yes, + No, +} + pub fn fmt_annotation<'a>(buf: &mut String<'a>, annotation: &'a TypeAnnotation<'a>, indent: u16) { - annotation.format_with_parens(buf, Parens::NotNeeded, indent); + annotation.format(buf, indent); } pub trait Formattable<'a> { fn is_multiline(&self) -> bool; - fn format_with_parens(&self, buf: &mut String<'a>, _parens: Parens, indent: u16) { + fn format_with_options( + &self, + buf: &mut String<'a>, + _parens: Parens, + _newlines: Newlines, + indent: u16, + ) { self.format(buf, indent); } fn format(&self, buf: &mut String<'a>, indent: u16) { - self.format_with_parens(buf, Parens::NotNeeded, indent); + self.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent); } } @@ -35,8 +47,15 @@ where self.value.is_multiline() } - fn format_with_parens(&self, buf: &mut String<'a>, parens: Parens, indent: u16) { - self.value.format_with_parens(buf, parens, indent) + fn format_with_options( + &self, + buf: &mut String<'a>, + parens: Parens, + newlines: Newlines, + indent: u16, + ) { + self.value + .format_with_options(buf, parens, newlines, indent) } fn format(&self, buf: &mut String<'a>, indent: u16) { @@ -85,7 +104,13 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { } } - fn format_with_parens(&self, buf: &mut String<'a>, parens: Parens, indent: u16) { + fn format_with_options( + &self, + buf: &mut String<'a>, + parens: Parens, + newlines: Newlines, + indent: u16, + ) { use roc_parse::ast::TypeAnnotation::*; match self { @@ -99,7 +124,12 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { let mut it = arguments.iter().peekable(); while let Some(argument) = it.next() { - (&argument.value).format_with_parens(buf, Parens::InFunctionType, indent); + (&argument.value).format_with_options( + buf, + Parens::InFunctionType, + Newlines::No, + indent, + ); if it.peek().is_some() { buf.push_str(", "); @@ -108,7 +138,12 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { buf.push_str(" -> "); - (&result.value).format_with_parens(buf, Parens::InFunctionType, indent); + (&result.value).format_with_options( + buf, + Parens::InFunctionType, + Newlines::No, + indent, + ); if write_parens { buf.push(')') @@ -126,7 +161,12 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { for argument in *arguments { buf.push(' '); - (&argument.value).format_with_parens(buf, Parens::InApply, indent); + (&argument.value).format_with_options( + buf, + Parens::InApply, + Newlines::No, + indent, + ); } if write_parens { @@ -137,7 +177,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { Wildcard => buf.push('*'), TagUnion { tags, ext } => { - tags.format_with_parens(buf, Parens::NotNeeded, indent); + tags.format(buf, indent); if let Some(loc_ext_ann) = *ext { loc_ext_ann.value.format(buf, indent); @@ -145,7 +185,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { } Record { fields, ext } => { - fields.format_with_parens(buf, Parens::NotNeeded, indent); + fields.format(buf, indent); if let Some(loc_ext_ann) = *ext { loc_ext_ann.value.format(buf, indent); @@ -160,7 +200,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { } SpaceBefore(ann, _spaces) | SpaceAfter(ann, _spaces) => { - ann.format_with_parens(buf, parens, indent) + ann.format_with_options(buf, parens, newlines, indent) } Malformed(raw) => buf.push_str(raw), @@ -168,7 +208,7 @@ impl<'a> Formattable<'a> for TypeAnnotation<'a> { } } -impl<'a, T: Formattable<'a>> Formattable<'a> for AssignedField<'a, T> { +impl<'a> Formattable<'a> for AssignedField<'a, TypeAnnotation<'a>> { fn is_multiline(&self) -> bool { use self::AssignedField::*; @@ -180,25 +220,66 @@ impl<'a, T: Formattable<'a>> Formattable<'a> for AssignedField<'a, T> { } } - fn format_with_parens(&self, buf: &mut String<'a>, _parens: Parens, indent: u16) { - // TODO multiline? + fn format_with_options( + &self, + buf: &mut String<'a>, + parens: Parens, + _newlines: Newlines, + indent: u16, + ) { + format_assigned_field_help(self, buf, parens, indent, " : "); + } +} +impl<'a> Formattable<'a> for AssignedField<'a, Expr<'a>> { + fn is_multiline(&self) -> bool { use self::AssignedField::*; match self { - LabeledValue(name, _spaces, ann) => { - // TODO use spaces? - buf.push_str(name.value); - buf.push_str(" : "); - ann.value.format(buf, indent); - } - LabelOnly(name) => { - buf.push_str(name.value); - } - AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => unreachable!(), - Malformed(raw) => { - buf.push_str(raw); - } + LabeledValue(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(), + LabelOnly(_) => false, + AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => true, + Malformed(text) => text.chars().any(|c| c == '\n'), + } + } + + fn format_with_options( + &self, + buf: &mut String<'a>, + parens: Parens, + _newlines: Newlines, + indent: u16, + ) { + format_assigned_field_help(self, buf, parens, indent, ": "); + } +} + +fn format_assigned_field_help<'a, T>( + zelf: &AssignedField<'a, T>, + buf: &mut String<'a>, + _parens: Parens, + indent: u16, + separator: &str, +) where + T: Formattable<'a>, +{ + // TODO multiline? + + use self::AssignedField::*; + + match zelf { + LabeledValue(name, _spaces, ann) => { + // TODO use spaces? + buf.push_str(name.value); + buf.push_str(separator); + ann.value.format(buf, indent); + } + LabelOnly(name) => { + buf.push_str(name.value); + } + AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => unreachable!(), + Malformed(raw) => { + buf.push_str(raw); } } } @@ -216,7 +297,13 @@ impl<'a> Formattable<'a> for Tag<'a> { } } - fn format_with_parens(&self, buf: &mut String<'a>, _parens: Parens, indent: u16) { + fn format_with_options( + &self, + buf: &mut String<'a>, + _parens: Parens, + _newlines: Newlines, + indent: u16, + ) { let is_multiline = self.is_multiline(); match self { @@ -227,12 +314,12 @@ impl<'a> Formattable<'a> for Tag<'a> { for arg in *args { newline(buf, arg_indent); - (&arg.value).format_with_parens(buf, Parens::InApply, arg_indent); + arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent); } } else { for arg in *args { buf.push(' '); - (&arg.value).format_with_parens(buf, Parens::InApply, indent); + arg.format_with_options(buf, Parens::InApply, Newlines::No, indent); } } } @@ -244,12 +331,12 @@ impl<'a> Formattable<'a> for Tag<'a> { for arg in *args { newline(buf, arg_indent); - (&arg.value).format_with_parens(buf, Parens::InApply, arg_indent); + arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent); } } else { for arg in *args { buf.push(' '); - (&arg.value).format_with_parens(buf, Parens::InApply, indent); + arg.format_with_options(buf, Parens::InApply, Newlines::No, indent); } } } @@ -261,7 +348,13 @@ impl<'a> Formattable<'a> for Tag<'a> { macro_rules! implement_format_sequence { ($start:expr, $end:expr, $t:ident) => { - fn format_with_parens(&self, buf: &mut String<'a>, _parens: Parens, indent: u16) { + fn format_with_options( + &self, + buf: &mut String<'a>, + _parens: Parens, + _newlines: Newlines, + indent: u16, + ) { buf.push($start); let mut iter = self.iter().peekable(); @@ -283,11 +376,7 @@ macro_rules! implement_format_sequence { match &expr_below { $t::SpaceAfter(expr_above, spaces_below_expr) => { - expr_above.format_with_parens( - buf, - Parens::NotNeeded, - item_indent, - ); + expr_above.format(buf, item_indent); if iter.peek().is_some() { buf.push(','); @@ -300,11 +389,7 @@ macro_rules! implement_format_sequence { ); } _ => { - expr_below.format_with_parens( - buf, - Parens::NotNeeded, - item_indent, - ); + expr_below.format(buf, item_indent); if iter.peek().is_some() { buf.push(','); } @@ -315,7 +400,7 @@ macro_rules! implement_format_sequence { $t::SpaceAfter(sub_expr, spaces) => { newline(buf, item_indent); - sub_expr.format_with_parens(buf, Parens::NotNeeded, item_indent); + sub_expr.format(buf, item_indent); if iter.peek().is_some() { buf.push(','); } @@ -325,7 +410,7 @@ macro_rules! implement_format_sequence { _ => { newline(buf, item_indent); - (&item.value).format_with_parens(buf, Parens::NotNeeded, item_indent); + item.format(buf, item_indent); if iter.peek().is_some() { buf.push(','); } @@ -333,7 +418,7 @@ macro_rules! implement_format_sequence { } } else { buf.push(' '); - (&item.value).format_with_parens(buf, Parens::NotNeeded, item_indent); + item.format(buf, item_indent); if iter.peek().is_some() { buf.push(','); } @@ -360,7 +445,7 @@ impl<'a> Formattable<'a> for &'a [Located>] { implement_format_sequence!('[', ']', Tag); } -impl<'a, T: Formattable<'a>> Formattable<'a> for &'a [Located>] { +impl<'a> Formattable<'a> for &'a [Located>>] { fn is_multiline(&self) -> bool { self.iter().any(|f| f.value.is_multiline()) } diff --git a/compiler/fmt/src/def.rs b/compiler/fmt/src/def.rs index b404ef12ef..a83b22e80a 100644 --- a/compiler/fmt/src/def.rs +++ b/compiler/fmt/src/def.rs @@ -1,50 +1,85 @@ -use crate::annotation::{fmt_annotation, Parens}; -use crate::expr::{fmt_expr, is_multiline_expr}; +use crate::annotation::{fmt_annotation, Formattable, Newlines, Parens}; +use crate::expr::fmt_expr; use crate::pattern::fmt_pattern; -use crate::spaces::{fmt_spaces, newline, INDENT}; +use crate::spaces::{fmt_spaces, is_comment, newline, INDENT}; use bumpalo::collections::String; -use roc_parse::ast::{Def, Expr, Pattern, TypeAnnotation}; +use roc_parse::ast::{Def, Expr, Pattern}; -pub fn fmt_def<'a>(buf: &mut String<'a>, def: &'a Def<'a>, indent: u16) { - use roc_parse::ast::Def::*; +/// A Located formattable value is also formattable +impl<'a> Formattable<'a> for Def<'a> { + fn is_multiline(&self) -> bool { + use roc_parse::ast::Def::*; - match def { - Annotation(loc_pattern, loc_annotation) => { - fmt_type_annotation(buf, &loc_pattern.value, &loc_annotation.value, indent); - } - Alias { name, vars, ann } => { - buf.push_str(name.value); + match self { + Alias { ann, .. } => ann.is_multiline(), + Annotation(loc_pattern, loc_annotation) => { + loc_pattern.is_multiline() || loc_annotation.is_multiline() + } + Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(), - if vars.is_empty() { - buf.push(' '); - } else { - for var in *vars { - buf.push(' '); - fmt_pattern(buf, &var.value, indent, Parens::NotNeeded); - } + TypedBody(_loc_pattern, _loc_annotation, _loc_expr) => { + unreachable!("annotations and bodies have not yet been merged into TypedBody"); } - buf.push_str(" : "); - - fmt_annotation(buf, &ann.value, indent); + SpaceBefore(sub_def, spaces) | SpaceAfter(sub_def, spaces) => { + spaces.iter().any(|s| is_comment(s)) || sub_def.is_multiline() + } + Nested(def) => def.is_multiline(), } - Body(loc_pattern, loc_expr) => { - fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent); - } - TypedBody(_loc_pattern, _loc_annotation, _loc_expr) => { - unreachable!("annotations and bodies have not yet been merged into TypedBody"); - } - SpaceBefore(sub_def, spaces) => { - fmt_spaces(buf, spaces.iter(), indent); - fmt_def(buf, sub_def, indent); - } - SpaceAfter(sub_def, spaces) => { - fmt_def(buf, sub_def, indent); - - fmt_spaces(buf, spaces.iter(), indent); - } - Nested(def) => fmt_def(buf, def, indent), } + + fn format_with_options( + &self, + buf: &mut String<'a>, + _parens: Parens, + _newlines: Newlines, + indent: u16, + ) { + use roc_parse::ast::Def::*; + + match self { + Annotation(loc_pattern, loc_annotation) => { + loc_pattern.format(buf, indent); + buf.push_str(" : "); + loc_annotation.format(buf, indent); + } + Alias { name, vars, ann } => { + buf.push_str(name.value); + + if vars.is_empty() { + buf.push(' '); + } else { + for var in *vars { + buf.push(' '); + fmt_pattern(buf, &var.value, indent, Parens::NotNeeded); + } + } + + buf.push_str(" : "); + + ann.format(buf, indent) + } + Body(loc_pattern, loc_expr) => { + fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent); + } + TypedBody(_loc_pattern, _loc_annotation, _loc_expr) => { + unreachable!("annotations and bodies have not yet been merged into TypedBody"); + } + SpaceBefore(sub_def, spaces) => { + fmt_spaces(buf, spaces.iter(), indent); + sub_def.format(buf, indent); + } + SpaceAfter(sub_def, spaces) => { + sub_def.format(buf, indent); + fmt_spaces(buf, spaces.iter(), indent); + } + Nested(def) => def.format(buf, indent), + } + } +} + +pub fn fmt_def<'a>(buf: &mut String<'a>, def: &Def<'a>, indent: u16) { + def.format(buf, indent); } pub fn fmt_body<'a>( @@ -55,7 +90,7 @@ pub fn fmt_body<'a>( ) { fmt_pattern(buf, pattern, indent, Parens::InApply); buf.push_str(" ="); - if is_multiline_expr(body) { + if body.is_multiline() { match body { Expr::Record { .. } | Expr::List(_) => { newline(buf, indent + INDENT); @@ -71,14 +106,3 @@ pub fn fmt_body<'a>( fmt_expr(buf, body, indent, false, true); } } - -pub fn fmt_type_annotation<'a>( - buf: &mut String<'a>, - pattern: &'a Pattern<'a>, - annotation: &'a TypeAnnotation<'a>, - indent: u16, -) { - fmt_pattern(buf, pattern, indent, Parens::NotNeeded); - buf.push_str(" : "); - fmt_annotation(buf, annotation, indent); -} diff --git a/compiler/fmt/src/pattern.rs b/compiler/fmt/src/pattern.rs index 7a02f00e6c..ac35bcd585 100644 --- a/compiler/fmt/src/pattern.rs +++ b/compiler/fmt/src/pattern.rs @@ -1,5 +1,4 @@ -use crate::annotation::{Formattable, Parens}; -use crate::expr::is_multiline_pattern; +use crate::annotation::{Formattable, Newlines, Parens}; use crate::spaces::{fmt_comments_only, fmt_spaces, is_comment}; use bumpalo::collections::String; use roc_parse::ast::{Base, Pattern}; @@ -10,7 +9,7 @@ pub fn fmt_pattern<'a>( indent: u16, parens: Parens, ) { - pattern.format_with_parens(buf, parens, indent); + pattern.format_with_options(buf, parens, Newlines::No, indent); } impl<'a> Formattable<'a> for Pattern<'a> { @@ -23,7 +22,7 @@ impl<'a> Formattable<'a> for Pattern<'a> { spaces.iter().any(|s| is_comment(s)) } - Pattern::Nested(nested_pat) => is_multiline_pattern(nested_pat), + Pattern::Nested(nested_pat) => nested_pat.is_multiline(), Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()), Pattern::RecordField(_, subpattern) => subpattern.is_multiline(), @@ -43,7 +42,13 @@ impl<'a> Formattable<'a> for Pattern<'a> { } } - fn format_with_parens(&self, buf: &mut String<'a>, parens: Parens, indent: u16) { + fn format_with_options( + &self, + buf: &mut String<'a>, + parens: Parens, + newlines: Newlines, + indent: u16, + ) { use self::Pattern::*; match self { @@ -60,11 +65,11 @@ impl<'a> Formattable<'a> for Pattern<'a> { buf.push('('); } - loc_pattern.format_with_parens(buf, Parens::InApply, indent); + loc_pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent); for loc_arg in loc_arg_patterns.iter() { buf.push(' '); - loc_arg.format_with_parens(buf, Parens::InApply, indent); + loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent); } if parens { @@ -77,7 +82,7 @@ impl<'a> Formattable<'a> for Pattern<'a> { let mut it = loc_patterns.iter().peekable(); while let Some(loc_pattern) = it.next() { - loc_pattern.format_with_parens(buf, Parens::NotNeeded, indent); + loc_pattern.format(buf, indent); if it.peek().is_some() { buf.push_str(", "); @@ -90,7 +95,7 @@ impl<'a> Formattable<'a> for Pattern<'a> { RecordField(name, loc_pattern) => { buf.push_str(name); buf.push_str(": "); - loc_pattern.format_with_parens(buf, Parens::NotNeeded, indent); + loc_pattern.format(buf, indent); } NumLiteral(string) => buf.push_str(string), @@ -128,10 +133,10 @@ impl<'a> Formattable<'a> for Pattern<'a> { } else { fmt_spaces(buf, spaces.iter(), indent); } - sub_pattern.format_with_parens(buf, parens, indent); + sub_pattern.format_with_options(buf, parens, newlines, indent); } SpaceAfter(sub_pattern, spaces) => { - sub_pattern.format_with_parens(buf, parens, indent); + sub_pattern.format_with_options(buf, parens, newlines, indent); // if only_comments { if !sub_pattern.is_multiline() { fmt_comments_only(buf, spaces.iter(), indent) @@ -141,7 +146,7 @@ impl<'a> Formattable<'a> for Pattern<'a> { } Nested(sub_pattern) => { - sub_pattern.format_with_parens(buf, parens, indent); + sub_pattern.format_with_options(buf, parens, newlines, indent); } // Malformed