Skip to content

Commit

Permalink
closes #2507
Browse files Browse the repository at this point in the history
  • Loading branch information
giacomocavalieri authored and lpil committed Jan 4, 2024
1 parent a0d8e43 commit c6d49b9
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 24 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
- Fixed some quirk with the formatting of binary operators.
- Fixed a bug where the formatter would move a function call's closed
parentheses on a new line instead of splitting the function's arguments.
- Now the formatter will format tuples as if they were functions, trying to
first split just the last element before splitting the whole tuple.

### Build tool

Expand Down
6 changes: 6 additions & 0 deletions compiler-core/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,12 @@ impl CallArg<UntypedExpr> {
}
}

impl<T> HasLocation for CallArg<T> {
fn location(&self) -> SrcSpan {
self.location
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RecordUpdateSpread {
pub base: Box<UntypedExpr>,
Expand Down
63 changes: 46 additions & 17 deletions compiler-core/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,10 +704,7 @@ impl<'comments> Formatter<'comments> {
.append(".")
.append(label.as_str()),

UntypedExpr::Tuple { elems, .. } => "#"
.to_doc()
.append(wrap_args(elems.iter().map(|e| self.expr(e))))
.group(),
UntypedExpr::Tuple { elems, .. } => self.tuple(elems),

UntypedExpr::BitArray { segments, .. } => bit_array(
segments
Expand Down Expand Up @@ -845,7 +842,11 @@ impl<'comments> Formatter<'comments> {
}
}

fn call<'a>(&mut self, fun: &'a UntypedExpr, args: &'a [CallArg<UntypedExpr>]) -> Document<'a> {
fn call<'a>(
&mut self,
fun: &'a UntypedExpr,
args: &'a Vec<CallArg<UntypedExpr>>,
) -> Document<'a> {
let expr = match fun {
UntypedExpr::Placeholder { .. } => panic!("Placeholders should not be formatted"),

Expand All @@ -872,29 +873,57 @@ impl<'comments> Formatter<'comments> {
| UntypedExpr::NegateInt { .. } => self.expr(fun),
};

match init_and_last(args) {
Some((initial_args, last_arg))
if is_breakable_argument(&last_arg.value, args.len())
&& !self.any_comments(last_arg.location.start) =>
self.append_inlinable_wrapped_args(
expr,
args,
|arg| &arg.value,
|self_, arg| self_.call_arg(arg),
)
}

fn tuple<'a>(&mut self, elements: &'a Vec<UntypedExpr>) -> Document<'a> {
self.append_inlinable_wrapped_args("#".to_doc(), elements, |e| e, |self_, e| self_.expr(e))
}

// Appends to the given docs a comma-separated list of documents wrapped by
// parentheses. If the last item of the argument list is splittable the
// resulting document will try to first split that before splitting all the
// other arguments.
// This is used for function calls and tuples.
fn append_inlinable_wrapped_args<'a, T, ToExpr, ToDoc>(
&mut self,
doc: Document<'a>,
values: &'a Vec<T>,
to_expr: ToExpr,
to_doc: ToDoc,
) -> Document<'a>
where
T: HasLocation,
ToExpr: Fn(&T) -> &UntypedExpr,
ToDoc: Fn(&mut Self, &'a T) -> Document<'a>,
{
match init_and_last(values) {
Some((initial_values, last_value))
if is_breakable_argument(to_expr(last_value), values.len())
&& !self.any_comments(last_value.location().start) =>
{
let last_arg_doc = self
.call_arg(last_arg)
let last_value_doc = to_doc(self, last_value)
.group()
.next_break_fits(NextBreakFitsMode::Enabled);

expr.append(wrap_function_call_args(
initial_args
doc.append(wrap_function_call_args(
initial_values
.iter()
.map(|a| self.call_arg(a))
.chain(std::iter::once(last_arg_doc)),
.map(|value| to_doc(self, value))
.chain(std::iter::once(last_value_doc)),
))
.next_break_fits(NextBreakFitsMode::Disabled)
.group()
}

Some(_) | None => expr
Some(_) | None => doc
.append(wrap_function_call_args(
args.iter().map(|a| self.call_arg(a)),
values.iter().map(|value| to_doc(self, value)),
))
.group(),
}
Expand Down
11 changes: 4 additions & 7 deletions compiler-core/src/format/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,13 +643,10 @@ fn compact_single_argument_call() {
fn expr_tuple() {
assert_format!(
r#"fn main(one, two, three) {
#(
1,
{
1
2
},
)
#(1, {
1
2
})
}
"#
);
Expand Down
1 change: 1 addition & 0 deletions compiler-core/src/format/tests/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ fn nested_breakable_tuples_in_function_calls() {
html(#(attribute("lang", "en")), #(
head(#(attribute("foo", "bar")), #(
title(#(), #(text("Hello this is some HTML"))),
body(#(), #(text("Hello this is some HTML"))),
)),
body(#(), #(h1(#(), #(text("Hello, lisp!"))))),
))
Expand Down
27 changes: 27 additions & 0 deletions compiler-core/src/format/tests/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,30 @@ fn index_block() {
"#
);
}

#[test]
fn tuple_with_last_splittable_arg() {
assert_format!(
r#"fn on_attribute_change() -> Dict(String, Decoder(Msg)) {
dict.from_list([
#("value", fn(attr) {
attr
|> dynamic.int
|> result.map(Value)
|> result.map(AttributeChanged)
}),
])
}
"#
);

assert_format!(
r#"pub fn main() {
#("value", [
"a long list that needs to be split on multiple lines",
"another long string",
])
}
"#
);
}

0 comments on commit c6d49b9

Please sign in to comment.