Further relax indentation of implements ability chains

This commit is contained in:
Joshua Warner 2025-01-10 21:10:16 -08:00
parent 7a1b3b8257
commit 0471993428
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
7 changed files with 165 additions and 22 deletions

View file

@ -2024,6 +2024,26 @@ pub trait Spaceable<'a> {
}
}
fn maybe_around_loc(
mut me: Loc<Self>,
arena: &'a Bump,
before: &'a [CommentOrNewline<'a>],
after: &'a [CommentOrNewline<'a>],
) -> Loc<Self>
where
Self: Sized + 'a,
{
if !after.is_empty() {
me.value = arena.alloc(me.value).after(after);
}
if !before.is_empty() {
me.value = arena.alloc(me.value).before(before);
}
me
}
fn with_spaces_before(&'a self, spaces: &'a [CommentOrNewline<'a>], region: Region) -> Loc<Self>
where
Self: Sized,

View file

@ -80,6 +80,24 @@ where
)
}
pub fn space0_before<'a, P, S, E>(
parser: P,
indent_before_problem: fn(Position) -> E,
) -> impl Parser<'a, SpacesBefore<'a, S>, E>
where
S: 'a,
P: 'a + Parser<'a, S, E>,
E: 'a + SpaceProblem,
{
parser::map(
and(space0_e(indent_before_problem), parser),
|(space_list, loc_expr): (&'a [CommentOrNewline<'a>], S)| SpacesBefore {
before: space_list,
item: loc_expr,
},
)
}
pub fn spaces_before_optional_after<'a, P, S, E>(parser: P) -> impl Parser<'a, Loc<S>, E>
where
S: 'a + Spaceable<'a>,

View file

@ -1,25 +1,25 @@
use crate::ast::{
AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, FunctionArrow,
ImplementsAbilities, ImplementsAbility, ImplementsClause, Pattern, Spaceable, Spaced, Tag,
TypeAnnotation, TypeHeader,
ImplementsAbilities, ImplementsAbility, ImplementsClause, Pattern, Spaceable, Spaced,
SpacesBefore, Tag, TypeAnnotation, TypeHeader,
};
use crate::blankspace::{
self, plain_spaces_before, space0_around_ee, space0_before_e, space0_before_optional_after,
space0_e, spaces_before_optional_after,
self, plain_spaces_before, space0_around_ee, space0_before, space0_before_e, space0_e,
spaces_before_optional_after,
};
use crate::expr::record_field;
use crate::ident::{lowercase_ident, lowercase_ident_keyword_e};
use crate::keyword;
use crate::parser::{
absolute_column_min_indent, and, collection_trailing_sep_e, either, error_on_byte,
increment_min_indent, indented_seq, loc, map, map_with_arena, reset_min_indent, skip_first,
skip_second, succeed, then, zero_or_more, ERecord, ETypeAbilityImpl, ParseResult,
};
use crate::parser::{
allocated, backtrackable, byte, fail, optional, specialize_err, specialize_err_ref, two_bytes,
EType, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, Parser,
Progress::*,
};
use crate::parser::{
and, collection_trailing_sep_e, either, error_on_byte, increment_min_indent, indented_seq, loc,
map, map_with_arena, reset_min_indent, skip_first, skip_second, succeed, then, zero_or_more,
ERecord, ETypeAbilityImpl, ParseResult,
};
use crate::state::State;
use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
@ -688,30 +688,53 @@ fn loc_applied_args_e<'a>(
// Hash & Eq & ...
fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
map(
map_with_arena(
and(
space0_before_optional_after(
space0_before(
specialize_err(EType::TApply, loc(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
),
zero_or_more(skip_first(
byte(b'&', EType::TImplementsClause),
space0_before_optional_after(
zero_or_more(and(
skip_second(
backtrackable(space0_e(EType::TIndentStart)),
byte(b'&', EType::TImplementsClause),
),
space0_before(
specialize_err(EType::TApply, loc(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
),
)),
),
|(first_ability, mut other_abilities): (
Loc<TypeAnnotation<'a>>,
Vec<'a, Loc<TypeAnnotation<'a>>>,
|arena: &'a Bump,
(first_ability, other_abilities): (
SpacesBefore<'a, Loc<TypeAnnotation<'a>>>,
Vec<
'a,
(
&'a [CommentOrNewline<'a>],
SpacesBefore<'a, Loc<TypeAnnotation<'a>>>,
),
>,
)| {
other_abilities.insert(0, first_ability);
other_abilities
let mut res = Vec::with_capacity_in(other_abilities.len() + 1, arena);
let mut pending = first_ability;
for (after, ability) in other_abilities {
res.push(Spaceable::maybe_around_loc(
pending.item,
arena,
pending.before,
after,
));
pending = ability;
}
res.push(Loc::at(
pending.item.region,
pending.item.value.maybe_before(arena, pending.before),
));
res
},
)
.trace("ability_chain")
}
fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'a>> {
@ -731,7 +754,7 @@ fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'
// Parse "implements"; we don't care about this keyword
crate::parser::keyword(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
// Parse "Hash & ..."; this may be qualified from another module like "Hash.Hash"
absolute_column_min_indent(ability_chain()),
ability_chain(),
),
)),
|(var, abilities): (Loc<Spaced<'a, &'a str>>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {