Refactoring Node

This commit is contained in:
Joshua Warner 2024-12-13 21:11:45 -08:00
parent c54b01016e
commit 7ea074dfa1
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
3 changed files with 224 additions and 205 deletions

View file

@ -1,6 +1,7 @@
use crate::{
collection::{fmt_collection, Braces},
expr::merge_spaces_conservative,
node::{Node, Nodify, Sp},
pattern::pattern_lift_spaces_after,
spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT},
Buf,
@ -415,7 +416,7 @@ fn fmt_tag_collection<'a>(
let mut last_after: &[CommentOrNewline<'_>] = &[];
for item in tags.items.iter() {
let lifted = tag_lift_to_node(arena, item.value);
let lifted = item.value.to_node(arena, Parens::NotNeeded);
let before = merge_spaces_conservative(arena, last_after, lifted.before);
last_after = lifted.after;
new_items.push(NodeSpaces {
@ -433,44 +434,49 @@ fn fmt_tag_collection<'a>(
fmt_collection(buf, indent, Braces::Square, new_items, newlines);
}
fn tag_lift_to_node<'a, 'b: 'a>(arena: &'a Bump, value: Tag<'b>) -> Spaces<'a, Node<'a>> {
match value {
Tag::Apply { name, args } => {
if args.is_empty() {
Spaces {
before: &[],
item: Node::Literal(name.value),
after: &[],
}
} else {
let first = Node::Literal(name.value);
let mut new_args: Vec<'a, (Sp<'a>, Node<'a>)> =
Vec::with_capacity_in(args.len(), arena);
let mut last_after: &[CommentOrNewline<'_>] = &[];
impl<'a> Nodify<'a> for Tag<'a> {
fn to_node<'b>(&'a self, arena: &'b Bump, parens: Parens) -> Spaces<'b, Node<'b>>
where
'a: 'b,
{
match self {
Tag::Apply { name, args } => {
if args.is_empty() {
Spaces {
before: &[],
item: Node::Literal(name.value),
after: &[],
}
} else {
let first = Node::Literal(name.value);
let mut new_args: Vec<'b, (Sp<'b>, Node<'b>)> =
Vec::with_capacity_in(args.len(), arena);
let mut last_after: &[CommentOrNewline<'_>] = &[];
for arg in args.iter() {
let lifted = ann_lift_to_node(Parens::InApply, arena, &arg.value);
let before = merge_spaces_conservative(arena, last_after, lifted.before);
last_after = lifted.after;
new_args.push((before, lifted.item));
}
for arg in args.iter() {
let lifted = arg.value.to_node(arena, Parens::InApply);
let before = merge_spaces_conservative(arena, last_after, lifted.before);
last_after = lifted.after;
new_args.push((before, lifted.item));
}
Spaces {
before: &[],
item: Node::Sequence(arena.alloc(first), new_args.into_bump_slice()),
after: last_after,
Spaces {
before: &[],
item: Node::Sequence(arena.alloc(first), new_args.into_bump_slice()),
after: last_after,
}
}
}
}
Tag::SpaceBefore(inner, sp) => {
let mut inner = tag_lift_to_node(arena, *inner);
inner.before = merge_spaces_conservative(arena, sp, inner.before);
inner
}
Tag::SpaceAfter(inner, sp) => {
let mut inner = tag_lift_to_node(arena, *inner);
inner.after = merge_spaces_conservative(arena, inner.after, sp);
inner
Tag::SpaceBefore(inner, sp) => {
let mut inner = inner.to_node(arena, parens);
inner.before = merge_spaces_conservative(arena, sp, inner.before);
inner
}
Tag::SpaceAfter(inner, sp) => {
let mut inner = inner.to_node(arena, parens);
inner.after = merge_spaces_conservative(arena, inner.after, sp);
inner
}
}
}
}
@ -511,16 +517,12 @@ fn fmt_ty_collection(
let mut last_after: &[CommentOrNewline<'_>] = &[];
for (i, item) in items.items.iter().enumerate() {
let func_ty_risky = i > 0;
let lifted = ann_lift_to_node(
if func_ty_risky {
Parens::InCollection
} else {
Parens::NotNeeded
},
arena,
&item.value,
);
let parens = if i > 0 {
Parens::InCollection
} else {
Parens::NotNeeded
};
let lifted = item.value.to_node(arena, parens);
let before = merge_spaces_conservative(arena, last_after, lifted.before);
last_after = lifted.after;
new_items.push(NodeSpaces {
@ -1138,184 +1140,110 @@ pub fn type_head_lift_spaces_after<'a, 'b: 'a>(
}
}
type Sp<'a> = &'a [CommentOrNewline<'a>];
#[derive(Copy, Clone, Debug)]
enum Node<'a> {
Literal(&'a str),
Sequence(&'a Node<'a>, &'a [(Sp<'a>, Node<'a>)]),
DelimitedSequence(Braces, &'a [(Sp<'a>, Node<'a>)], Sp<'a>),
TypeAnnotation(TypeAnnotation<'a>),
}
impl<'a> Formattable for Node<'a> {
fn is_multiline(&self) -> bool {
impl<'a> Nodify<'a> for TypeAnnotation<'a> {
fn to_node<'b>(&'a self, arena: &'b Bump, parens: Parens) -> Spaces<'b, Node<'b>>
where
'a: 'b,
{
match self {
Node::DelimitedSequence(_braces, lefts, right) => {
right.is_empty()
&& lefts
.iter()
.any(|(sp, l)| l.is_multiline() || !sp.is_empty())
}
Node::Sequence(first, rest) => {
first.is_multiline()
|| rest
.iter()
.any(|(sp, l)| l.is_multiline() || !sp.is_empty())
}
Node::TypeAnnotation(type_annotation) => type_annotation.is_multiline(),
Node::Literal(_) => false,
}
}
TypeAnnotation::Apply(module, func, args) => {
if args.is_empty() {
return Spaces {
item: Node::TypeAnnotation(*self),
before: &[],
after: &[],
};
}
let mut new_args = Vec::with_capacity_in(args.len(), arena);
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
match self {
Node::DelimitedSequence(braces, lefts, right) => {
buf.indent(indent);
buf.push(braces.start());
for (sp, l) in *lefts {
if !sp.is_empty() {
fmt_spaces(buf, sp.iter(), indent);
if !args.is_empty() {
for arg in args.iter().take(args.len() - 1) {
let lifted = ann_lift_spaces(arena, &arg.value);
new_args.push(Loc::at(arg.region, lower(arena, lifted)));
}
l.format_with_options(buf, parens, newlines, indent);
}
if !right.is_empty() {
fmt_spaces(buf, right.iter(), indent);
}
buf.indent(indent);
buf.push(braces.end());
}
Node::Sequence(first, rest) => {
first.format_with_options(buf, parens, newlines, indent);
for (sp, l) in *rest {
if !sp.is_empty() {
fmt_spaces(buf, sp.iter(), indent);
let after = if let Some(last) = args.last() {
let lifted = ann_lift_spaces(arena, &last.value);
if lifted.before.is_empty() {
new_args.push(Loc::at(last.region, lifted.item));
} else {
buf.spaces(1);
new_args.push(Loc::at(
last.region,
TypeAnnotation::SpaceBefore(arena.alloc(lifted.item), lifted.before),
));
}
l.format_with_options(buf, parens, newlines, indent);
}
}
Node::TypeAnnotation(type_annotation) => {
type_annotation.format_with_options(buf, parens, newlines, indent);
}
Node::Literal(text) => {
buf.indent(indent);
buf.push_str(text);
}
}
}
}
fn ann_lift_to_node<'a, 'b: 'a>(
parens: Parens,
arena: &'a Bump,
ann: &TypeAnnotation<'b>,
) -> Spaces<'a, Node<'a>> {
match ann {
TypeAnnotation::Apply(module, func, args) => {
if args.is_empty() {
return Spaces {
item: Node::TypeAnnotation(*ann),
before: &[],
after: &[],
};
}
let mut new_args = Vec::with_capacity_in(args.len(), arena);
if !args.is_empty() {
for arg in args.iter().take(args.len() - 1) {
let lifted = ann_lift_spaces(arena, &arg.value);
new_args.push(Loc::at(arg.region, lower(arena, lifted)));
}
}
let after = if let Some(last) = args.last() {
let lifted = ann_lift_spaces(arena, &last.value);
if lifted.before.is_empty() {
new_args.push(Loc::at(last.region, lifted.item));
lifted.after
} else {
new_args.push(Loc::at(
last.region,
TypeAnnotation::SpaceBefore(arena.alloc(lifted.item), lifted.before),
));
}
lifted.after
} else {
&[]
};
&[]
};
let item = Node::TypeAnnotation(TypeAnnotation::Apply(
module,
func,
new_args.into_bump_slice(),
));
let item = Node::TypeAnnotation(TypeAnnotation::Apply(
module,
func,
new_args.into_bump_slice(),
));
if parens == Parens::InApply {
parens_around_node(
arena,
if parens == Parens::InApply {
parens_around_node(
arena,
Spaces {
before: &[],
item,
after,
},
)
} else {
Spaces {
before: &[],
item,
after,
},
)
} else {
Spaces {
before: &[],
item,
after,
}
}
}
}
TypeAnnotation::SpaceBefore(expr, spaces) => {
let mut inner = ann_lift_to_node(parens, arena, expr);
inner.before = merge_spaces_conservative(arena, spaces, inner.before);
inner
}
TypeAnnotation::SpaceAfter(expr, spaces) => {
let mut inner = ann_lift_to_node(parens, arena, expr);
inner.after = merge_spaces_conservative(arena, inner.after, spaces);
inner
}
TypeAnnotation::Function(args, purity, res) => {
let new_args = arena.alloc_slice_copy(args);
let before = if let Some(first) = new_args.first_mut() {
let lifted = ann_lift_spaces_before(arena, &first.value);
first.value = lifted.item;
lifted.before
} else {
&[]
};
let new_res = ann_lift_spaces_after(arena, &res.value);
let new_ann = TypeAnnotation::Function(
new_args,
*purity,
arena.alloc(Loc::at_zero(new_res.item)),
);
let inner = Spaces {
before,
item: Node::TypeAnnotation(new_ann),
after: new_res.after,
};
if parens == Parens::InCollection || parens == Parens::InApply {
parens_around_node(arena, inner)
} else {
TypeAnnotation::SpaceBefore(expr, spaces) => {
let mut inner = expr.to_node(arena, parens);
inner.before = merge_spaces_conservative(arena, spaces, inner.before);
inner
}
}
_ => {
let lifted = ann_lift_spaces(arena, ann);
Spaces {
before: lifted.before,
item: Node::TypeAnnotation(lifted.item),
after: lifted.after,
TypeAnnotation::SpaceAfter(expr, spaces) => {
let mut inner = expr.to_node(arena, parens);
inner.after = merge_spaces_conservative(arena, inner.after, spaces);
inner
}
TypeAnnotation::Function(args, purity, res) => {
let new_args = arena.alloc_slice_copy(args);
let before = if let Some(first) = new_args.first_mut() {
let lifted = ann_lift_spaces_before(arena, &first.value);
first.value = lifted.item;
lifted.before
} else {
&[]
};
let new_res = ann_lift_spaces_after(arena, &res.value);
let new_ann = TypeAnnotation::Function(
new_args,
*purity,
arena.alloc(Loc::at_zero(new_res.item)),
);
let inner = Spaces {
before,
item: Node::TypeAnnotation(new_ann),
after: new_res.after,
};
if parens == Parens::InCollection || parens == Parens::InApply {
parens_around_node(arena, inner)
} else {
inner
}
}
_ => {
let lifted = ann_lift_spaces(arena, self);
Spaces {
before: lifted.before,
item: Node::TypeAnnotation(lifted.item),
after: lifted.after,
}
}
}
}

View file

@ -7,6 +7,7 @@ pub mod collection;
pub mod def;
pub mod expr;
pub mod header;
pub mod node;
pub mod pattern;
pub mod spaces;

View file

@ -0,0 +1,90 @@
use bumpalo::Bump;
use roc_parse::ast::{CommentOrNewline, Spaces, TypeAnnotation};
use crate::{
annotation::{Formattable, Newlines, Parens},
collection::Braces,
spaces::fmt_spaces,
Buf,
};
pub type Sp<'a> = &'a [CommentOrNewline<'a>];
#[derive(Copy, Clone, Debug)]
pub enum Node<'a> {
Literal(&'a str),
Sequence(&'a Node<'a>, &'a [(Sp<'a>, Node<'a>)]),
DelimitedSequence(Braces, &'a [(Sp<'a>, Node<'a>)], Sp<'a>),
TypeAnnotation(TypeAnnotation<'a>),
}
pub trait Nodify<'a> {
fn to_node<'b>(&'a self, arena: &'b Bump, parens: Parens) -> Spaces<'b, Node<'b>>
where
'a: 'b;
}
impl<'a> Formattable for Node<'a> {
fn is_multiline(&self) -> bool {
match self {
Node::DelimitedSequence(_braces, lefts, right) => {
right.is_empty()
&& lefts
.iter()
.any(|(sp, l)| l.is_multiline() || !sp.is_empty())
}
Node::Sequence(first, rest) => {
first.is_multiline()
|| rest
.iter()
.any(|(sp, l)| l.is_multiline() || !sp.is_empty())
}
Node::TypeAnnotation(type_annotation) => type_annotation.is_multiline(),
Node::Literal(_) => false,
}
}
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
match self {
Node::DelimitedSequence(braces, lefts, right) => {
buf.indent(indent);
buf.push(braces.start());
for (sp, l) in *lefts {
if !sp.is_empty() {
fmt_spaces(buf, sp.iter(), indent);
}
l.format_with_options(buf, parens, newlines, indent);
}
if !right.is_empty() {
fmt_spaces(buf, right.iter(), indent);
}
buf.indent(indent);
buf.push(braces.end());
}
Node::Sequence(first, rest) => {
first.format_with_options(buf, parens, newlines, indent);
for (sp, l) in *rest {
if !sp.is_empty() {
fmt_spaces(buf, sp.iter(), indent);
} else {
buf.spaces(1);
}
l.format_with_options(buf, parens, newlines, indent);
}
}
Node::TypeAnnotation(type_annotation) => {
type_annotation.format_with_options(buf, parens, newlines, indent);
}
Node::Literal(text) => {
buf.indent(indent);
buf.push_str(text);
}
}
}
}