mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
Format has-derived clauses
This commit is contained in:
parent
fcf464e9da
commit
e78b345ee0
@ -4,7 +4,8 @@ use crate::{
|
|||||||
Buf,
|
Buf,
|
||||||
};
|
};
|
||||||
use roc_parse::ast::{
|
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_parse::ident::UppercaseIdent;
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::Loc;
|
||||||
@ -43,6 +44,16 @@ pub enum Newlines {
|
|||||||
Yes,
|
Yes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Newlines {
|
||||||
|
pub fn from_bool(yes: bool) -> Self {
|
||||||
|
if yes {
|
||||||
|
Self::Yes
|
||||||
|
} else {
|
||||||
|
Self::No
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Formattable {
|
pub trait Formattable {
|
||||||
fn is_multiline(&self) -> bool;
|
fn is_multiline(&self) -> bool;
|
||||||
|
|
||||||
@ -546,3 +557,42 @@ impl<'a> Formattable for HasClause<'a> {
|
|||||||
.format_with_options(buf, parens, newlines, indent);
|
.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::pattern::fmt_pattern;
|
||||||
use crate::spaces::{fmt_spaces, INDENT};
|
use crate::spaces::{fmt_spaces, INDENT};
|
||||||
use crate::Buf;
|
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;
|
use roc_region::all::Loc;
|
||||||
|
|
||||||
/// A Located formattable value is also formattable
|
/// A Located formattable value is also formattable
|
||||||
@ -51,10 +53,6 @@ impl<'a> Formattable for Def<'a> {
|
|||||||
Alias {
|
Alias {
|
||||||
header: TypeHeader { name, vars },
|
header: TypeHeader { name, vars },
|
||||||
ann,
|
ann,
|
||||||
}
|
|
||||||
| Opaque {
|
|
||||||
header: TypeHeader { name, vars },
|
|
||||||
typ: ann,
|
|
||||||
} => {
|
} => {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(name.value);
|
buf.push_str(name.value);
|
||||||
@ -64,15 +62,64 @@ impl<'a> Formattable for Def<'a> {
|
|||||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.push_str(match def {
|
buf.push_str(" :");
|
||||||
Alias { .. } => " :",
|
|
||||||
Opaque { .. } => " :=",
|
|
||||||
_ => unreachable!(),
|
|
||||||
});
|
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
|
||||||
ann.format(buf, indent + INDENT)
|
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 {
|
Ability {
|
||||||
header: TypeHeader { name, vars },
|
header: TypeHeader { name, vars },
|
||||||
loc_has: _,
|
loc_has: _,
|
||||||
@ -134,7 +181,6 @@ impl<'a> Formattable for Def<'a> {
|
|||||||
body_pattern,
|
body_pattern,
|
||||||
body_expr,
|
body_expr,
|
||||||
} => {
|
} => {
|
||||||
use roc_parse::ast::TypeAnnotation;
|
|
||||||
let is_type_multiline = ann_type.is_multiline();
|
let is_type_multiline = ann_type.is_multiline();
|
||||||
let is_type_function = matches!(
|
let is_type_function = matches!(
|
||||||
ann_type.value,
|
ann_type.value,
|
||||||
|
@ -3,9 +3,9 @@ use bumpalo::Bump;
|
|||||||
use roc_module::called_via::{BinOp, UnaryOp};
|
use roc_module::called_via::{BinOp, UnaryOp};
|
||||||
use roc_parse::{
|
use roc_parse::{
|
||||||
ast::{
|
ast::{
|
||||||
AbilityMember, AssignedField, Collection, CommentOrNewline, Def, Expr, Has, HasClause,
|
AbilityMember, AssignedField, Collection, CommentOrNewline, Def, Derived, Expr, Has,
|
||||||
Module, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader,
|
HasClause, Module, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef,
|
||||||
ValueDef, WhenBranch,
|
TypeHeader, ValueDef, WhenBranch,
|
||||||
},
|
},
|
||||||
header::{
|
header::{
|
||||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName,
|
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName,
|
||||||
@ -438,12 +438,14 @@ impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
|||||||
Opaque {
|
Opaque {
|
||||||
header: TypeHeader { name, vars },
|
header: TypeHeader { name, vars },
|
||||||
typ,
|
typ,
|
||||||
|
derived,
|
||||||
} => Opaque {
|
} => Opaque {
|
||||||
header: TypeHeader {
|
header: TypeHeader {
|
||||||
name: name.remove_spaces(arena),
|
name: name.remove_spaces(arena),
|
||||||
vars: vars.remove_spaces(arena),
|
vars: vars.remove_spaces(arena),
|
||||||
},
|
},
|
||||||
typ: typ.remove_spaces(arena),
|
typ: typ.remove_spaces(arena),
|
||||||
|
derived: derived.remove_spaces(arena),
|
||||||
},
|
},
|
||||||
Ability {
|
Ability {
|
||||||
header: TypeHeader { name, vars },
|
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]
|
||||||
/// Test that everything under examples/ is formatted correctly
|
/// 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.
|
/// 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>]),
|
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)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum TypeAnnotation<'a> {
|
pub enum TypeAnnotation<'a> {
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// 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!(Pattern);
|
||||||
impl_extract_spaces!(Tag);
|
impl_extract_spaces!(Tag);
|
||||||
impl_extract_spaces!(AssignedField<T>);
|
impl_extract_spaces!(AssignedField<T>);
|
||||||
|
impl_extract_spaces!(TypeAnnotation);
|
||||||
|
|
||||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
Loading…
Reference in New Issue
Block a user