roc/crates/compiler/fmt/src/spaces.rs
2023-06-06 19:46:15 -04:00

930 lines
32 KiB
Rust

use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
use roc_module::called_via::{BinOp, UnaryOp};
use roc_parse::{
ast::{
AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr, Has, HasAbilities,
HasAbility, HasClause, HasImpls, Header, Module, Pattern, RecordBuilderField, Spaced,
Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
WhenBranch,
},
header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
ModuleName, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires,
ProvidesTo, To, TypedIdent,
},
ident::{BadIdent, UppercaseIdent},
};
use roc_region::all::{Loc, Position, Region};
use crate::{Ast, Buf};
/// The number of spaces to indent.
pub const INDENT: u16 = 4;
pub fn fmt_default_spaces(buf: &mut Buf, spaces: &[CommentOrNewline], indent: u16) {
if spaces.is_empty() {
buf.spaces(1);
} else {
fmt_spaces(buf, spaces.iter(), indent);
}
}
pub fn fmt_default_newline(buf: &mut Buf, spaces: &[CommentOrNewline], indent: u16) {
if spaces.is_empty() {
buf.newline();
} else {
fmt_spaces(buf, spaces.iter(), indent);
}
}
/// Like fmt_spaces, but disallows two consecutive newlines.
pub fn fmt_spaces_no_blank_lines<'a, 'buf, I>(buf: &mut Buf<'buf>, spaces: I, indent: u16)
where
I: Iterator<Item = &'a CommentOrNewline<'a>>,
{
fmt_spaces_max_consecutive_newlines(buf, spaces, 1, indent)
}
pub fn fmt_spaces<'a, 'buf, I>(buf: &mut Buf<'buf>, spaces: I, indent: u16)
where
I: Iterator<Item = &'a CommentOrNewline<'a>>,
{
fmt_spaces_max_consecutive_newlines(buf, spaces, 2, indent)
}
fn fmt_spaces_max_consecutive_newlines<'a, 'buf, I>(
buf: &mut Buf<'buf>,
spaces: I,
max_consecutive_newlines: usize,
indent: u16,
) where
I: Iterator<Item = &'a CommentOrNewline<'a>>,
{
use self::CommentOrNewline::*;
// Only ever print two newlines back to back.
// (Two newlines renders as one blank line.)
let mut consecutive_newlines = 0;
for space in spaces {
match space {
Newline => {
if consecutive_newlines < max_consecutive_newlines {
buf.newline();
// Don't bother incrementing it if we're already over the limit.
// There's no upside, and it might eventually overflow.
consecutive_newlines += 1;
}
}
LineComment(comment) => {
buf.indent(indent);
fmt_comment(buf, comment);
buf.newline();
consecutive_newlines = 1;
}
DocComment(docs) => {
buf.indent(indent);
fmt_docs(buf, docs);
buf.newline();
consecutive_newlines = 1;
}
}
}
}
#[derive(Eq, PartialEq, Debug)]
pub enum NewlineAt {
Top,
Bottom,
Both,
None,
}
/// Like format_spaces, but remove newlines and keep only comments.
/// The `new_line_at` argument describes how new lines should be inserted
/// at the beginning or at the end of the block
/// in the case of there is some comment in the `spaces` argument.
pub fn fmt_comments_only<'a, 'buf, I>(
buf: &mut Buf<'buf>,
spaces: I,
new_line_at: NewlineAt,
indent: u16,
) where
I: Iterator<Item = &'a CommentOrNewline<'a>>,
{
use self::CommentOrNewline::*;
use NewlineAt::*;
let mut comment_seen = false;
for space in spaces {
match space {
Newline => {}
LineComment(comment) => {
if comment_seen || new_line_at == Top || new_line_at == Both {
buf.newline();
}
buf.indent(indent);
fmt_comment(buf, comment);
comment_seen = true;
}
DocComment(docs) => {
if comment_seen || new_line_at == Top || new_line_at == Both {
buf.newline();
}
buf.indent(indent);
fmt_docs(buf, docs);
comment_seen = true;
}
}
}
if comment_seen && (new_line_at == Bottom || new_line_at == Both) {
buf.newline();
}
}
fn fmt_comment(buf: &mut Buf, comment: &str) {
// The '#' in a comment should always be preceded by a newline or a space,
// unless it's the very beginning of the buffer.
if !buf.is_empty() && !buf.ends_with_space() && !buf.ends_with_newline() {
buf.spaces(1);
}
buf.push('#');
if !comment.starts_with(' ') {
buf.spaces(1);
}
buf.push_str(comment.trim_end());
}
pub fn count_leading_newlines<'a, I>(data: I) -> u16
where
I: Iterator<Item = &'a CommentOrNewline<'a>>,
{
let mut count = 0;
let mut allow_counting = false;
for (index, val) in data.enumerate() {
let is_first = index == 0;
let is_newline = matches!(val, CommentOrNewline::Newline);
if is_first && is_newline {
allow_counting = true
}
if is_newline && allow_counting {
count += 1;
} else {
break;
}
}
count
}
fn fmt_docs(buf: &mut Buf, docs: &str) {
// The "##" in a doc comment should always be preceded by a newline or a space,
// unless it's the very beginning of the buffer.
if !buf.is_empty() && !buf.ends_with_space() && !buf.ends_with_newline() {
buf.spaces(1);
}
buf.push_str("##");
if !docs.is_empty() {
buf.spaces(1);
}
buf.push_str(docs.trim_end());
}
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.
///
/// Currently this consists of:
/// * Removing newlines
/// * Removing comments
/// * Removing parens in Exprs
///
/// Long term, we actuall want this transform to preserve comments (so we can assert they're maintained by formatting)
/// - but there are currently several bugs where they're _not_ preserved.
/// TODO: ensure formatting retains comments
pub trait RemoveSpaces<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self;
}
impl<'a> RemoveSpaces<'a> for Ast<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
Ast {
module: self.module.remove_spaces(arena),
defs: self.defs.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for Defs<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
let mut defs = self.clone();
defs.spaces.clear();
defs.space_before.clear();
defs.space_after.clear();
for type_def in defs.type_defs.iter_mut() {
*type_def = type_def.remove_spaces(arena);
}
for value_def in defs.value_defs.iter_mut() {
*value_def = value_def.remove_spaces(arena);
}
for region_def in defs.regions.iter_mut() {
*region_def = region_def.remove_spaces(arena);
}
defs
}
}
impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
Spaces {
before: &[],
item: self.item.remove_spaces(arena),
after: &[],
}
}
}
impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
KeywordItem {
keyword: self.keyword.remove_spaces(arena),
item: self.item.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
ProvidesTo {
provides_keyword: self.provides_keyword.remove_spaces(arena),
entries: self.entries.remove_spaces(arena),
types: self.types.remove_spaces(arena),
to_keyword: self.to_keyword.remove_spaces(arena),
to: self.to.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for Module<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
let header = match &self.header {
Header::Interface(header) => Header::Interface(InterfaceHeader {
before_name: &[],
name: header.name.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena),
imports: header.imports.remove_spaces(arena),
}),
Header::App(header) => Header::App(AppHeader {
before_name: &[],
name: header.name.remove_spaces(arena),
packages: header.packages.remove_spaces(arena),
imports: header.imports.remove_spaces(arena),
provides: header.provides.remove_spaces(arena),
}),
Header::Package(header) => Header::Package(PackageHeader {
before_name: &[],
name: header.name.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena),
packages: header.packages.remove_spaces(arena),
}),
Header::Platform(header) => Header::Platform(PlatformHeader {
before_name: &[],
name: header.name.remove_spaces(arena),
requires: header.requires.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena),
packages: header.packages.remove_spaces(arena),
imports: header.imports.remove_spaces(arena),
provides: header.provides.remove_spaces(arena),
}),
Header::Hosted(header) => Header::Hosted(HostedHeader {
before_name: &[],
name: header.name.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena),
imports: header.imports.remove_spaces(arena),
generates: header.generates.remove_spaces(arena),
generates_with: header.generates_with.remove_spaces(arena),
}),
};
Module {
comments: &[],
header,
}
}
}
impl<'a> RemoveSpaces<'a> for Region {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
Region::zero()
}
}
impl<'a> RemoveSpaces<'a> for &'a str {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
self
}
}
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
Spaced::SpaceBefore(a, _) => a.remove_spaces(arena),
Spaced::SpaceAfter(a, _) => a.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for ExposedName<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a> RemoveSpaces<'a> for PackageName<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a> RemoveSpaces<'a> for To<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
To::ExistingPackage(a) => To::ExistingPackage(a),
To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)),
}
}
}
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
TypedIdent {
ident: self.ident.remove_spaces(arena),
spaces_before_colon: &[],
ann: self.ann.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
PlatformRequires {
rigids: self.rigids.remove_spaces(arena),
signature: self.signature.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
PackageEntry {
shorthand: self.shorthand,
spaces_after_shorthand: &[],
package_name: self.package_name.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
ImportsEntry::IngestedFile(a, b) => {
ImportsEntry::IngestedFile(a, b.remove_spaces(arena))
}
}
}
}
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option<T> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
self.as_ref().map(|a| a.remove_spaces(arena))
}
}
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc<T> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
let res = self.value.remove_spaces(arena);
Loc::at(Region::zero(), res)
}
}
impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
(self.0.remove_spaces(arena), self.1.remove_spaces(arena))
}
}
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
let mut items = Vec::with_capacity_in(self.items.len(), arena);
for item in self.items {
items.push(item.remove_spaces(arena));
}
Collection::with_items(items.into_bump_slice())
}
}
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
let mut items = Vec::with_capacity_in(self.len(), arena);
for item in *self {
let res = item.remove_spaces(arena);
items.push(res);
}
items.into_bump_slice()
}
}
impl<'a> RemoveSpaces<'a> for UnaryOp {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a> RemoveSpaces<'a> for BinOp {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
arena.alloc((*self).remove_spaces(arena))
}
}
impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
use TypeDef::*;
match *self {
Alias {
header: TypeHeader { name, vars },
ann,
} => Alias {
header: TypeHeader {
name: name.remove_spaces(arena),
vars: vars.remove_spaces(arena),
},
ann: ann.remove_spaces(arena),
},
Opaque {
header: TypeHeader { name, vars },
typ,
derived,
} => Opaque {
header: TypeHeader {
name: name.remove_spaces(arena),
vars: vars.remove_spaces(arena),
},
typ: typ.remove_spaces(arena),
derived: derived.remove_spaces(arena),
},
Ability {
header: TypeHeader { name, vars },
loc_has,
members,
} => Ability {
header: TypeHeader {
name: name.remove_spaces(arena),
vars: vars.remove_spaces(arena),
},
loc_has: loc_has.remove_spaces(arena),
members: members.remove_spaces(arena),
},
}
}
}
impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
use ValueDef::*;
match *self {
Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)),
Body(a, b) => Body(
arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)),
),
AnnotatedBody {
ann_pattern,
ann_type,
comment: _,
body_pattern,
body_expr,
} => AnnotatedBody {
ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)),
ann_type: arena.alloc(ann_type.remove_spaces(arena)),
comment: None,
body_pattern: arena.alloc(body_pattern.remove_spaces(arena)),
body_expr: arena.alloc(body_expr.remove_spaces(arena)),
},
Dbg {
condition,
preceding_comment: _,
} => Dbg {
condition: arena.alloc(condition.remove_spaces(arena)),
preceding_comment: Region::zero(),
},
Expect {
condition,
preceding_comment: _,
} => Expect {
condition: arena.alloc(condition.remove_spaces(arena)),
preceding_comment: Region::zero(),
},
ExpectFx {
condition,
preceding_comment: _,
} => ExpectFx {
condition: arena.alloc(condition.remove_spaces(arena)),
preceding_comment: Region::zero(),
},
}
}
}
impl<'a> RemoveSpaces<'a> for Has<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
Has::Has
}
}
impl<'a> RemoveSpaces<'a> for AbilityMember<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
AbilityMember {
name: self.name.remove_spaces(arena),
typ: self.typ.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for WhenBranch<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
WhenBranch {
patterns: self.patterns.remove_spaces(arena),
value: self.value.remove_spaces(arena),
guard: self.guard.remove_spaces(arena),
}
}
}
impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue(
a.remove_spaces(arena),
arena.alloc([]),
arena.alloc(c.remove_spaces(arena)),
),
AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue(
a.remove_spaces(arena),
arena.alloc([]),
arena.alloc(c.remove_spaces(arena)),
),
AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)),
AssignedField::Malformed(a) => AssignedField::Malformed(a),
AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena),
AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for RecordBuilderField<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
RecordBuilderField::Value(a, _, c) => RecordBuilderField::Value(
a.remove_spaces(arena),
&[],
arena.alloc(c.remove_spaces(arena)),
),
RecordBuilderField::ApplyValue(a, _, _, c) => RecordBuilderField::ApplyValue(
a.remove_spaces(arena),
&[],
&[],
arena.alloc(c.remove_spaces(arena)),
),
RecordBuilderField::LabelOnly(a) => {
RecordBuilderField::LabelOnly(a.remove_spaces(arena))
}
RecordBuilderField::Malformed(a) => RecordBuilderField::Malformed(a),
RecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena),
RecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for StrLiteral<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t),
StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)),
StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)),
}
}
}
impl<'a> RemoveSpaces<'a> for StrSegment<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
StrSegment::Plaintext(t) => StrSegment::Plaintext(t),
StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)),
StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c),
StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)),
}
}
}
impl<'a> RemoveSpaces<'a> for Expr<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
Expr::Float(a) => Expr::Float(a),
Expr::Num(a) => Expr::Num(a),
Expr::NonBase10Int {
string,
base,
is_negative,
} => Expr::NonBase10Int {
string,
base,
is_negative,
},
Expr::Str(a) => Expr::Str(a.remove_spaces(arena)),
Expr::IngestedFile(a, b) => Expr::IngestedFile(a, b),
Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b),
Expr::AccessorFunction(a) => Expr::AccessorFunction(a),
Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b),
Expr::List(a) => Expr::List(a.remove_spaces(arena)),
Expr::RecordUpdate { update, fields } => Expr::RecordUpdate {
update: arena.alloc(update.remove_spaces(arena)),
fields: fields.remove_spaces(arena),
},
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
Expr::RecordBuilder(a) => Expr::RecordBuilder(a.remove_spaces(arena)),
Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)),
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
Expr::Underscore(a) => Expr::Underscore(a),
Expr::Tag(a) => Expr::Tag(a),
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
Expr::Closure(a, b) => Expr::Closure(
arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)),
),
Expr::Crash => Expr::Crash,
Expr::Defs(a, b) => {
let mut defs = a.clone();
defs.space_before = vec![Default::default(); defs.len()];
defs.space_after = vec![Default::default(); defs.len()];
defs.regions = vec![Region::zero(); defs.len()];
defs.spaces.clear();
for type_def in defs.type_defs.iter_mut() {
*type_def = type_def.remove_spaces(arena);
}
for value_def in defs.value_defs.iter_mut() {
*value_def = value_def.remove_spaces(arena);
}
Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena)))
}
Expr::Backpassing(a, b, c) => Expr::Backpassing(
arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)),
arena.alloc(c.remove_spaces(arena)),
),
Expr::Expect(a, b) => Expr::Expect(
arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)),
),
Expr::Dbg(a, b) => Expr::Dbg(
arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)),
),
Expr::Apply(a, b, c) => Expr::Apply(
arena.alloc(a.remove_spaces(arena)),
b.remove_spaces(arena),
c,
),
Expr::BinOps(a, b) => {
Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena)))
}
Expr::UnaryOp(a, b) => {
Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
}
Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))),
Expr::When(a, b) => {
Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
}
Expr::ParensAround(a) => {
// The formatter can remove redundant parentheses, so also remove these when normalizing for comparison.
a.remove_spaces(arena)
}
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
Expr::MalformedClosure => Expr::MalformedClosure,
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
Expr::MultipleRecordBuilders(a) => Expr::MultipleRecordBuilders(a),
Expr::UnappliedRecordBuilder(a) => Expr::UnappliedRecordBuilder(a),
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
Expr::SingleQuote(a) => Expr::Num(a),
}
}
}
fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
match ident {
BadIdent::Start(_) => BadIdent::Start(Position::zero()),
BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()),
BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()),
BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()),
BadIdent::UnderscoreAtStart {
position: _,
declaration_region,
} => BadIdent::UnderscoreAtStart {
position: Position::zero(),
declaration_region,
},
BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()),
BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()),
BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()),
BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()),
BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()),
BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()),
BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()),
}
}
impl<'a> RemoveSpaces<'a> for Pattern<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
Pattern::Identifier(a) => Pattern::Identifier(a),
Pattern::Tag(a) => Pattern::Tag(a),
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
Pattern::Apply(a, b) => Pattern::Apply(
arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)),
),
Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)),
Pattern::RequiredField(a, b) => {
Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena)))
}
Pattern::OptionalField(a, b) => {
Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena)))
}
Pattern::As(pattern, pattern_as) => {
Pattern::As(arena.alloc(pattern.remove_spaces(arena)), pattern_as)
}
Pattern::NumLiteral(a) => Pattern::NumLiteral(a),
Pattern::NonBase10Literal {
string,
base,
is_negative,
} => Pattern::NonBase10Literal {
string,
base,
is_negative,
},
Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a),
Pattern::StrLiteral(a) => Pattern::StrLiteral(a),
Pattern::Underscore(a) => Pattern::Underscore(a),
Pattern::Malformed(a) => Pattern::Malformed(a),
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
Pattern::QualifiedIdentifier { module_name, ident } => {
Pattern::QualifiedIdentifier { module_name, ident }
}
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
Pattern::SingleQuote(a) => Pattern::SingleQuote(a),
Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)),
Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)),
Pattern::ListRest(opt_pattern_as) => Pattern::ListRest(opt_pattern_as),
}
}
}
impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
TypeAnnotation::Function(a, b) => TypeAnnotation::Function(
arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)),
),
TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)),
TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a),
TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As(
arena.alloc(a.remove_spaces(arena)),
&[],
TypeHeader {
name: name.remove_spaces(arena),
vars: vars.remove_spaces(arena),
},
),
TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple {
elems: fields.remove_spaces(arena),
ext: ext.remove_spaces(arena),
},
TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record {
fields: fields.remove_spaces(arena),
ext: ext.remove_spaces(arena),
},
TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion {
ext: ext.remove_spaces(arena),
tags: tags.remove_spaces(arena),
},
TypeAnnotation::Inferred => TypeAnnotation::Inferred,
TypeAnnotation::Wildcard => TypeAnnotation::Wildcard,
TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where(
arena.alloc(annot.remove_spaces(arena)),
arena.alloc(has_clauses.remove_spaces(arena)),
),
TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena),
TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena),
TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a),
}
}
}
impl<'a> RemoveSpaces<'a> for HasClause<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
HasClause {
var: self.var.remove_spaces(arena),
abilities: self.abilities.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for Tag<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
Tag::Apply { name, args } => Tag::Apply {
name: name.remove_spaces(arena),
args: args.remove_spaces(arena),
},
Tag::Malformed(a) => Tag::Malformed(a),
Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for HasImpls<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
HasImpls::HasImpls(impls) => HasImpls::HasImpls(impls.remove_spaces(arena)),
HasImpls::SpaceBefore(has, _) | HasImpls::SpaceAfter(has, _) => {
has.remove_spaces(arena)
}
}
}
}
impl<'a> RemoveSpaces<'a> for HasAbility<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
HasAbility::HasAbility { ability, impls } => HasAbility::HasAbility {
ability: ability.remove_spaces(arena),
impls: impls.remove_spaces(arena),
},
HasAbility::SpaceBefore(has, _) | HasAbility::SpaceAfter(has, _) => {
has.remove_spaces(arena)
}
}
}
}
impl<'a> RemoveSpaces<'a> for HasAbilities<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self {
HasAbilities::Has(derived) => HasAbilities::Has(derived.remove_spaces(arena)),
HasAbilities::SpaceBefore(derived, _) | HasAbilities::SpaceAfter(derived, _) => {
derived.remove_spaces(arena)
}
}
}
}