mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
Format has-derived clauses
This commit is contained in:
parent
fcf464e9da
commit
e78b345ee0
@ -4,7 +4,8 @@ use crate::{
|
||||
Buf,
|
||||
};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Collection, Expr, ExtractSpaces, HasClause, Tag, TypeAnnotation, TypeHeader,
|
||||
AssignedField, Collection, Derived, Expr, ExtractSpaces, HasClause, Tag, TypeAnnotation,
|
||||
TypeHeader,
|
||||
};
|
||||
use roc_parse::ident::UppercaseIdent;
|
||||
use roc_region::all::Loc;
|
||||
@ -43,6 +44,16 @@ pub enum Newlines {
|
||||
Yes,
|
||||
}
|
||||
|
||||
impl Newlines {
|
||||
pub fn from_bool(yes: bool) -> Self {
|
||||
if yes {
|
||||
Self::Yes
|
||||
} else {
|
||||
Self::No
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Formattable {
|
||||
fn is_multiline(&self) -> bool;
|
||||
|
||||
@ -546,3 +557,42 @@ impl<'a> Formattable for HasClause<'a> {
|
||||
.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for Derived<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
match self {
|
||||
Derived::SpaceAfter(..) | Derived::SpaceBefore(..) => true,
|
||||
Derived::Has(derived) => derived.is_multiline(),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_with_options<'buf>(
|
||||
&self,
|
||||
buf: &mut Buf<'buf>,
|
||||
parens: Parens,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
match self {
|
||||
Derived::Has(derived) => {
|
||||
if newlines == Newlines::Yes {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
}
|
||||
buf.push_str("has");
|
||||
buf.spaces(1);
|
||||
fmt_collection(buf, indent, '[', ']', *derived, newlines);
|
||||
}
|
||||
Derived::SpaceBefore(derived, spaces) => {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
derived.format_with_options(buf, parens, Newlines::No, indent)
|
||||
}
|
||||
Derived::SpaceAfter(derived, spaces) => {
|
||||
derived.format_with_options(buf, parens, newlines, indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ use crate::annotation::{Formattable, Newlines, Parens};
|
||||
use crate::pattern::fmt_pattern;
|
||||
use crate::spaces::{fmt_spaces, INDENT};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{AbilityMember, Def, Expr, ExtractSpaces, Pattern, TypeHeader};
|
||||
use roc_parse::ast::{
|
||||
AbilityMember, Def, Expr, ExtractSpaces, Pattern, TypeAnnotation, TypeHeader,
|
||||
};
|
||||
use roc_region::all::Loc;
|
||||
|
||||
/// A Located formattable value is also formattable
|
||||
@ -51,10 +53,6 @@ impl<'a> Formattable for Def<'a> {
|
||||
Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
}
|
||||
| Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
typ: ann,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(name.value);
|
||||
@ -64,15 +62,64 @@ impl<'a> Formattable for Def<'a> {
|
||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||
}
|
||||
|
||||
buf.push_str(match def {
|
||||
Alias { .. } => " :",
|
||||
Opaque { .. } => " :=",
|
||||
_ => unreachable!(),
|
||||
});
|
||||
buf.push_str(" :");
|
||||
buf.spaces(1);
|
||||
|
||||
ann.format(buf, indent + INDENT)
|
||||
}
|
||||
Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
typ: ann,
|
||||
derived,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(name.value);
|
||||
|
||||
for var in *vars {
|
||||
buf.spaces(1);
|
||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||
}
|
||||
|
||||
buf.push_str(" :=");
|
||||
buf.spaces(1);
|
||||
|
||||
let ann_is_where_clause =
|
||||
matches!(ann.extract_spaces().item, TypeAnnotation::Where(..));
|
||||
|
||||
let ann_has_spaces_before =
|
||||
matches!(&ann.value, TypeAnnotation::SpaceBefore(..));
|
||||
|
||||
// Always put the has-derived clause on a newline if it is itself multiline, or
|
||||
// the annotation has a where-has clause.
|
||||
let derived_multiline = if let Some(derived) = derived {
|
||||
!derived.value.is_empty() && (derived.is_multiline() || ann_is_where_clause)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let make_multiline = ann.is_multiline() || derived_multiline;
|
||||
|
||||
// If the annotation has spaces before, a newline will already be printed.
|
||||
if make_multiline && !ann_has_spaces_before {
|
||||
buf.newline();
|
||||
buf.indent(indent + INDENT);
|
||||
}
|
||||
|
||||
ann.format(buf, indent + INDENT);
|
||||
|
||||
if let Some(derived) = derived {
|
||||
if !make_multiline {
|
||||
buf.spaces(1);
|
||||
}
|
||||
|
||||
derived.format_with_options(
|
||||
buf,
|
||||
Parens::NotNeeded,
|
||||
Newlines::from_bool(make_multiline),
|
||||
indent + INDENT,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_has: _,
|
||||
@ -134,7 +181,6 @@ impl<'a> Formattable for Def<'a> {
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => {
|
||||
use roc_parse::ast::TypeAnnotation;
|
||||
let is_type_multiline = ann_type.is_multiline();
|
||||
let is_type_function = matches!(
|
||||
ann_type.value,
|
||||
|
@ -3,9 +3,9 @@ use bumpalo::Bump;
|
||||
use roc_module::called_via::{BinOp, UnaryOp};
|
||||
use roc_parse::{
|
||||
ast::{
|
||||
AbilityMember, AssignedField, Collection, CommentOrNewline, Def, Expr, Has, HasClause,
|
||||
Module, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader,
|
||||
ValueDef, WhenBranch,
|
||||
AbilityMember, AssignedField, Collection, CommentOrNewline, Def, Derived, Expr, Has,
|
||||
HasClause, Module, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef, WhenBranch,
|
||||
},
|
||||
header::{
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName,
|
||||
@ -438,12 +438,14 @@ impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
||||
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 },
|
||||
@ -741,3 +743,14 @@ impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Derived<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Derived::Has(derived) => Derived::Has(derived.remove_spaces(arena)),
|
||||
Derived::SpaceBefore(derived, _) | Derived::SpaceAfter(derived, _) => {
|
||||
derived.remove_spaces(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4658,6 +4658,46 @@ mod test_fmt {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opaque_has_clause() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
A := U8 has [ Eq, Hash ]
|
||||
|
||||
0
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
A :=
|
||||
U8
|
||||
has [ Eq, Hash ]
|
||||
|
||||
0
|
||||
"#
|
||||
));
|
||||
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
A := a | a has Hash has [ Eq, Hash ]
|
||||
|
||||
0
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
A :=
|
||||
a | a has Hash
|
||||
has [ Eq, Hash ]
|
||||
|
||||
0
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test that everything under examples/ is formatted correctly
|
||||
/// If this test fails on your diff, it probably means you need to re-format the examples.
|
||||
|
@ -400,6 +400,20 @@ pub enum Derived<'a> {
|
||||
SpaceAfter(&'a Derived<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl Derived<'_> {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let mut it = self;
|
||||
loop {
|
||||
match it {
|
||||
Self::SpaceBefore(inner, _) | Self::SpaceAfter(inner, _) => {
|
||||
it = inner;
|
||||
}
|
||||
Self::Has(collection) => return collection.is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum TypeAnnotation<'a> {
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
@ -1005,6 +1019,7 @@ impl_extract_spaces!(Expr);
|
||||
impl_extract_spaces!(Pattern);
|
||||
impl_extract_spaces!(Tag);
|
||||
impl_extract_spaces!(AssignedField<T>);
|
||||
impl_extract_spaces!(TypeAnnotation);
|
||||
|
||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||
type Item = T;
|
||||
|
Loading…
Reference in New Issue
Block a user