mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Force type headers to format just like Pattern::Apply
This commit is contained in:
parent
df16e1b633
commit
20aee5c37b
14 changed files with 269 additions and 168 deletions
|
@ -7,7 +7,7 @@ use crate::expr::{
|
|||
expr_lift_and_lower, expr_lift_spaces, expr_lift_spaces_after, expr_lift_spaces_before,
|
||||
fmt_str_literal, is_str_multiline, sub_expr_requests_parens,
|
||||
};
|
||||
use crate::pattern::{fmt_pattern, pattern_lift_spaces};
|
||||
use crate::pattern::{fmt_pattern, pattern_fmt_apply, pattern_lift_spaces};
|
||||
use crate::pattern::{pattern_lift_spaces_before, starts_with_inline_comment};
|
||||
use crate::spaces::{
|
||||
fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT,
|
||||
|
@ -527,80 +527,16 @@ impl<'a> Formattable for TypeHeader<'a> {
|
|||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
|
||||
indent: u16,
|
||||
) {
|
||||
buf.indent(indent);
|
||||
buf.push_str(self.name.value);
|
||||
|
||||
let vars_indent = if self.vars.iter().any(|v| v.is_multiline()) {
|
||||
indent + INDENT
|
||||
} else {
|
||||
indent
|
||||
};
|
||||
|
||||
let mut last_after: &[CommentOrNewline<'_>] = &[];
|
||||
let mut last_multiline = false;
|
||||
|
||||
for var in self.vars.iter() {
|
||||
let var = pattern_lift_spaces(buf.text.bump(), &var.value);
|
||||
|
||||
let before = if !last_after.is_empty() {
|
||||
merge_spaces(buf.text.bump(), last_after, var.before)
|
||||
} else {
|
||||
var.before
|
||||
};
|
||||
|
||||
if !before.is_empty() {
|
||||
if !var.item.is_multiline() {
|
||||
fmt_comments_only(buf, before.iter(), NewlineAt::Bottom, vars_indent)
|
||||
} else {
|
||||
fmt_spaces(buf, before.iter(), vars_indent);
|
||||
}
|
||||
}
|
||||
|
||||
if last_multiline {
|
||||
buf.ensure_ends_with_newline();
|
||||
} else {
|
||||
buf.ensure_ends_with_whitespace();
|
||||
}
|
||||
|
||||
last_after = var.after;
|
||||
last_multiline = var.item.is_multiline();
|
||||
|
||||
let need_parens = matches!(
|
||||
var.item,
|
||||
Pattern::Apply(..)
|
||||
| Pattern::Identifier {
|
||||
ident: "implements"
|
||||
}
|
||||
);
|
||||
|
||||
if need_parens {
|
||||
buf.indent(vars_indent);
|
||||
buf.push_str("(");
|
||||
}
|
||||
|
||||
fmt_pattern(buf, &var.item, vars_indent, Parens::NotNeeded);
|
||||
|
||||
buf.indent(vars_indent);
|
||||
|
||||
if need_parens {
|
||||
buf.push_str(")");
|
||||
}
|
||||
}
|
||||
|
||||
if !last_after.is_empty() {
|
||||
if starts_with_inline_comment(last_after.iter()) {
|
||||
buf.spaces(1);
|
||||
}
|
||||
|
||||
if !last_multiline {
|
||||
fmt_comments_only(buf, last_after.iter(), NewlineAt::Bottom, indent)
|
||||
} else {
|
||||
fmt_spaces(buf, last_after.iter(), indent);
|
||||
}
|
||||
}
|
||||
pattern_fmt_apply(
|
||||
buf,
|
||||
Pattern::Tag(self.name.value),
|
||||
self.vars,
|
||||
Parens::NotNeeded,
|
||||
indent,
|
||||
self.vars.iter().any(|v| v.is_multiline()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -153,103 +153,27 @@ fn fmt_pattern_only(
|
|||
is_multiline: bool,
|
||||
) {
|
||||
match me {
|
||||
Pattern::Identifier { ident: string } => {
|
||||
Pattern::Identifier { ident } => {
|
||||
buf.indent(indent);
|
||||
snakify_camel_ident(buf, string);
|
||||
if *ident == "implements" {
|
||||
buf.push_str("(implements)");
|
||||
} else {
|
||||
snakify_camel_ident(buf, ident);
|
||||
}
|
||||
}
|
||||
Pattern::Tag(name) | Pattern::OpaqueRef(name) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(name);
|
||||
}
|
||||
Pattern::Apply(loc_pattern, loc_arg_patterns) => {
|
||||
buf.indent(indent);
|
||||
// Sometimes, an Apply pattern needs parens around it.
|
||||
// In particular when an Apply's argument is itself an Apply (> 0) arguments
|
||||
let parens = !loc_arg_patterns.is_empty() && (parens == Parens::InApply);
|
||||
|
||||
let indent_more = if is_multiline {
|
||||
indent + INDENT
|
||||
} else {
|
||||
indent
|
||||
};
|
||||
|
||||
if parens {
|
||||
buf.push('(');
|
||||
}
|
||||
|
||||
let pat = pattern_lift_spaces(buf.text.bump(), &loc_pattern.value);
|
||||
|
||||
if !pat.before.is_empty() {
|
||||
if !is_multiline {
|
||||
fmt_comments_only(buf, pat.before.iter(), NewlineAt::Bottom, indent)
|
||||
} else {
|
||||
fmt_spaces(buf, pat.before.iter(), indent);
|
||||
}
|
||||
}
|
||||
|
||||
fmt_pattern_only(&pat.item, buf, Parens::InApply, indent, is_multiline);
|
||||
|
||||
let mut last_after = pat.after;
|
||||
|
||||
let mut add_newlines = is_multiline;
|
||||
|
||||
for loc_arg in loc_arg_patterns.iter() {
|
||||
buf.spaces(1);
|
||||
|
||||
let parens = Parens::InApply;
|
||||
let arg = pattern_lift_spaces(buf.text.bump(), &loc_arg.value);
|
||||
|
||||
let mut was_multiline = arg.item.is_multiline();
|
||||
|
||||
let mut before = merge_spaces(buf.text.bump(), last_after, arg.before);
|
||||
|
||||
if !before.is_empty() {
|
||||
if starts_with_block_str(&arg.item) {
|
||||
// Ick!
|
||||
// The block string will keep "generating" newlines when formatted (it wants to start on its own line),
|
||||
// so we strip one out here.
|
||||
//
|
||||
// Note that this doesn't affect Expr's because those have explicit parens, and we can control
|
||||
// whether spaces cross that boundary.
|
||||
let chop_off = before
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|&&s| matches!(s, CommentOrNewline::Newline))
|
||||
.count();
|
||||
before = &before[..before.len() - chop_off];
|
||||
}
|
||||
|
||||
if !is_multiline {
|
||||
was_multiline |= before.iter().any(|s| s.is_comment());
|
||||
fmt_comments_only(buf, before.iter(), NewlineAt::Bottom, indent_more)
|
||||
} else {
|
||||
was_multiline |= true;
|
||||
fmt_spaces(buf, before.iter(), indent_more);
|
||||
}
|
||||
}
|
||||
|
||||
if add_newlines {
|
||||
buf.ensure_ends_with_newline();
|
||||
}
|
||||
|
||||
fmt_pattern_only(&arg.item, buf, parens, indent_more, arg.item.is_multiline());
|
||||
|
||||
last_after = arg.after;
|
||||
|
||||
add_newlines |= was_multiline;
|
||||
}
|
||||
|
||||
if !last_after.is_empty() {
|
||||
if !is_multiline {
|
||||
fmt_comments_only(buf, last_after.iter(), NewlineAt::Bottom, indent_more)
|
||||
} else {
|
||||
fmt_spaces(buf, last_after.iter(), indent_more);
|
||||
}
|
||||
}
|
||||
|
||||
if parens {
|
||||
buf.push(')');
|
||||
}
|
||||
pattern_fmt_apply(
|
||||
buf,
|
||||
loc_pattern.value,
|
||||
*loc_arg_patterns,
|
||||
parens,
|
||||
indent,
|
||||
is_multiline,
|
||||
);
|
||||
}
|
||||
Pattern::RecordDestructure(loc_patterns) => {
|
||||
buf.indent(indent);
|
||||
|
@ -477,6 +401,104 @@ fn fmt_pattern_only(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pattern_fmt_apply(
|
||||
buf: &mut Buf<'_>,
|
||||
func: Pattern<'_>,
|
||||
args: &[Loc<Pattern<'_>>],
|
||||
parens: Parens,
|
||||
indent: u16,
|
||||
is_multiline: bool,
|
||||
) {
|
||||
buf.indent(indent);
|
||||
// Sometimes, an Apply pattern needs parens around it.
|
||||
// In particular when an Apply's argument is itself an Apply (> 0) arguments
|
||||
let parens = !args.is_empty() && (parens == Parens::InApply);
|
||||
|
||||
let indent_more = if is_multiline {
|
||||
indent + INDENT
|
||||
} else {
|
||||
indent
|
||||
};
|
||||
|
||||
if parens {
|
||||
buf.push('(');
|
||||
}
|
||||
|
||||
let func = pattern_lift_spaces(buf.text.bump(), &func);
|
||||
|
||||
if !func.before.is_empty() {
|
||||
if !is_multiline {
|
||||
fmt_comments_only(buf, func.before.iter(), NewlineAt::Bottom, indent)
|
||||
} else {
|
||||
fmt_spaces(buf, func.before.iter(), indent);
|
||||
}
|
||||
}
|
||||
|
||||
fmt_pattern_only(&func.item, buf, Parens::InApply, indent, is_multiline);
|
||||
|
||||
let mut last_after = func.after;
|
||||
|
||||
let mut add_newlines = is_multiline;
|
||||
|
||||
for loc_arg in args.iter() {
|
||||
buf.spaces(1);
|
||||
|
||||
let parens = Parens::InApply;
|
||||
let arg = pattern_lift_spaces(buf.text.bump(), &loc_arg.value);
|
||||
|
||||
let mut was_multiline = arg.item.is_multiline();
|
||||
|
||||
let mut before = merge_spaces(buf.text.bump(), last_after, arg.before);
|
||||
|
||||
if !before.is_empty() {
|
||||
if starts_with_block_str(&arg.item) {
|
||||
// Ick!
|
||||
// The block string will keep "generating" newlines when formatted (it wants to start on its own line),
|
||||
// so we strip one out here.
|
||||
//
|
||||
// Note that this doesn't affect Expr's because those have explicit parens, and we can control
|
||||
// whether spaces cross that boundary.
|
||||
let chop_off = before
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|&&s| matches!(s, CommentOrNewline::Newline))
|
||||
.count();
|
||||
before = &before[..before.len() - chop_off];
|
||||
}
|
||||
|
||||
if !is_multiline {
|
||||
was_multiline |= before.iter().any(|s| s.is_comment());
|
||||
fmt_comments_only(buf, before.iter(), NewlineAt::Bottom, indent_more)
|
||||
} else {
|
||||
was_multiline |= true;
|
||||
fmt_spaces(buf, before.iter(), indent_more);
|
||||
}
|
||||
}
|
||||
|
||||
if add_newlines {
|
||||
buf.ensure_ends_with_newline();
|
||||
}
|
||||
|
||||
fmt_pattern_only(&arg.item, buf, parens, indent_more, arg.item.is_multiline());
|
||||
|
||||
last_after = arg.after;
|
||||
|
||||
add_newlines |= was_multiline;
|
||||
}
|
||||
|
||||
if !last_after.is_empty() {
|
||||
if !is_multiline {
|
||||
fmt_comments_only(buf, last_after.iter(), NewlineAt::Bottom, indent_more)
|
||||
} else {
|
||||
fmt_spaces(buf, last_after.iter(), indent_more);
|
||||
}
|
||||
}
|
||||
|
||||
if parens {
|
||||
buf.push(')');
|
||||
}
|
||||
}
|
||||
|
||||
pub fn starts_with_inline_comment<'a, I: IntoIterator<Item = &'a CommentOrNewline<'a>>>(
|
||||
spaces: I,
|
||||
) -> bool {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue