Skip to content

Commit

Permalink
Introduce semi-expanded format for function calls (#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
michalmuskala authored Jun 18, 2021
1 parent 64d3a60 commit ea1ad21
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 21 deletions.
55 changes: 46 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,28 +185,28 @@ not perfect. Therefore some manual intervention to help the formatter out might
be needed. For example, given the following code:
```erlang unformatted split_tokens
split_tokens([{TokenType, Meta, TokenValue} | Rest], Acc, CAcc) ->
split_tokens(Rest, [{TokenType, token_anno(erl_anno:to_term(Meta), #{}), TokenValue} | Acc], CAcc).
split_tokens([{TokenType, Meta, TokenValue} | Rest], TokenAcc, CommentAcc) ->
split_tokens(Rest, [{TokenType, token_anno(erl_anno:to_term(Meta), #{}), TokenValue} | TokenAcc], CommentAcc).
```
Because the line-length is exceeded, the formatter will produce the following:
```erlang formatted split_tokens
split_tokens([{TokenType, Meta, TokenValue} | Rest], Acc, CAcc) ->
split_tokens([{TokenType, Meta, TokenValue} | Rest], TokenAcc, CommentAcc) ->
split_tokens(
Rest,
[{TokenType, token_anno(erl_anno:to_term(Meta), #{}), TokenValue} | Acc],
CAcc
[{TokenType, token_anno(erl_anno:to_term(Meta), #{}), TokenValue} | TokenAcc],
CommentAcc
).
```
It might be more desirable, though, to extract a variable and allow the call to
still be rendered in a single line, for example:
```erlang formatted split_tokens2
split_tokens([{TokenType, Meta, TokenValue} | Rest], Acc, CAcc) ->
split_tokens([{TokenType, Meta, TokenValue} | Rest], TokenAcc, CommentAcc) ->
Token = {TokenType, token_anno(erl_anno:to_term(Meta), #{}), TokenValue},
split_tokens(Rest, [Token | Acc], CAcc).
split_tokens(Rest, [Token | TokenAcc], CommentAcc).
```
A similar situation could happen with long patterns in function heads,
Expand Down Expand Up @@ -245,8 +245,8 @@ The formatter keeps the original decisions in two key places
### In containers
For containers like lists, tuples, maps, records, function calls, macro calls,
etc, there are two possible layouts - "collapsed" where the entire collection
For containers like lists, tuples, maps, records, etc,
there are two possible layouts - "collapsed" where the entire collection
is printed in a single line; and "expanded" where each element is printed on a
separate line. The formatter respects this choice, if possible. If there is a
newline between the opening bracket/brace/parenthesis and the first element,
Expand Down Expand Up @@ -282,6 +282,43 @@ Foo, Bar]
and re-running the formatter, will produce the initial format again.
### In functions
For function calls, function definitions, types, and macros, there are two possible
layouts similar to lists, but also a third extra layout that puts all the arguments
on the same line. This new format is preserved:
```erlang formatted function
function(
Argument1, Argument2, Argument3
)
```
Similar to the fully expanded format:
```erlang formatted function-full
function(
Argument1,
Argument2,
Argument3
)
```
The expanded forms can be forced by including a newline between the parentheses
and the first argument or in-between the arguments:
```erlang unformatted function
function(
Argument1, Argument2, Argument3)
```
Will be formatted to the first snippet, while:
```erlang unformatted function-full
function(Argument1,
Argument2, Argument3)
```
Will be formatted to the second snippet.
### In clauses
A similar approach is followed, when laying our clause sequences in functions,
Expand Down
24 changes: 20 additions & 4 deletions src/erlfmt_format.erl
Original file line number Diff line number Diff line change
Expand Up @@ -421,21 +421,31 @@ flex_container(Meta, Values, Left, Right) ->
container_common(Meta, Values, Left, Right, flex_break, last_fits).

call(Meta, Values, Left, Right) ->
container_common(Meta, Values, Left, Right, break, last_fits).
container_common(Meta, Values, Left, Right, group_break, last_fits).

container_common(_Meta, [], Left, Right, _, _) ->
concat(Left, Right);
container_common(Meta, Values, Left, Right, BreakKind0, LastFits0) ->
LastFitsFun = last_fits_fun(LastFits0, Values),
container_common(Meta, Values, Left, Right, BreakKind0, LastFits) ->
LastFitsFun = last_fits_fun(LastFits, Values),
BreakKind = break_behaviour(Meta, Values, BreakKind0),
BreakFun = break_fun(BreakKind),
ValuesVD = map_inner_values(LastFitsFun, Values),
Doc = join_inner_values(BreakFun, ValuesVD),
Surrounded = surround_container(BreakKind, Left, Doc, Right),
LastFitsFun(Surrounded, disabled).

-type break_kind() :: break | flex_break | line | no_break.
-type break_kind() :: break | group_break | flex_break | line | group_line | no_break.

break_behaviour(Meta, Values, group_break) ->
case has_trailing_comments(Values) orelse has_any_break_between(Values) of
true ->
line;
false ->
case has_opening_line_break(Meta, Values) of
true -> group_line;
false -> group_break
end
end;
break_behaviour(Meta, Values, BreakKind) ->
case
has_opening_line_break(Meta, Values) orelse
Expand Down Expand Up @@ -485,6 +495,10 @@ surround_container(line, Left, Doc, Right) ->
surround(Left, <<"">>, concat(force_breaks(), Doc), <<"">>, Right);
surround_container(break, Left, Doc, Right) ->
surround(Left, <<"">>, Doc, <<"">>, Right);
surround_container(group_line, Left, Doc, Right) ->
surround(Left, <<"">>, concat(force_breaks(), group(Doc)), <<"">>, Right);
surround_container(group_break, Left, Doc, Right) ->
surround(Left, <<"">>, group(Doc), <<"">>, Right);
surround_container(no_break, Left, Doc, Right) ->
concat(Left, Doc, Right);
surround_container(flex_break, Left, Doc, Right) ->
Expand Down Expand Up @@ -527,6 +541,8 @@ has_any_break_between(_) ->
-spec break_fun(break_kind()) -> erlfmt_algebra:append_fun().
break_fun(line) -> fun erlfmt_algebra:line/2;
break_fun(break) -> fun erlfmt_algebra:break/2;
break_fun(group_line) -> fun erlfmt_algebra:break/2;
break_fun(group_break) -> fun erlfmt_algebra:break/2;
break_fun(flex_break) -> fun erlfmt_algebra:flex_break/2;
break_fun(no_break) -> fun(_, _) -> error(unreachable) end.

Expand Down
2 changes: 1 addition & 1 deletion test/erlfmt_SUITE_data/overlong.erl
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ process(_Arg1, Arg2, _Arg3, _Arg4, _Arg5, _Arg6) ->
% Curabitur nisi metus.
% Overlong, in bytes: 色は匂へど 散りぬるを 我が世誰ぞ 常ならむ 有為の奥山 今日越えて 浅き夢見じ 酔ひもせず
other_module:call('some.atom'),
other_module:call(with_many, extremely_long_arguments, that_should_really, be_broken_up, size(Arg2)),
other_module:call(with_so_very_many, very_very_long, extremely_long_arguments, that_should_really, be_broken_up, size(Arg2)),
ok.
3 changes: 2 additions & 1 deletion test/erlfmt_SUITE_data/overlong.erl.formatted
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ process(_Arg1, Arg2, _Arg3, _Arg4, _Arg5, _Arg6) ->
% Overlong, in bytes: 色は匂へど 散りぬるを 我が世誰ぞ 常ならむ 有為の奥山 今日越えて 浅き夢見じ 酔ひもせず
other_module:call('some.atom'),
other_module:call(
with_many,
with_so_very_many,
very_very_long,
extremely_long_arguments,
that_should_really,
be_broken_up,
Expand Down
67 changes: 61 additions & 6 deletions test/erlfmt_format_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -804,8 +804,7 @@ binary_operator_more(Config) when is_list(Config) ->
"D, E).\n",
"A orelse B orelse\n"
" c(\n"
" D,\n"
" E\n"
" D, E\n"
" ).\n"
).

Expand Down Expand Up @@ -2160,6 +2159,34 @@ call(Config) when is_list(Config) ->
" 80\n"
")\n",
60
),
?assertFormat(
"long_name(Long, Arguments)",
"long_name(\n"
" Long, Arguments\n"
")\n",
25
),
?assertSame(
"long_name(\n"
" Long, Arguments\n"
")\n"
),
?assertFormat(
"long_name(\n"
" Long, Arguments\n"
")\n",
"long_name(\n"
" Long,\n"
" Arguments\n"
")\n",
10
),
?assertSame(
"long_name(\n"
" Long,\n"
" Arguments\n"
")\n"
).

block(Config) when is_list(Config) ->
Expand Down Expand Up @@ -2984,7 +3011,7 @@ macro(Config) when is_list(Config) ->
" X when is_integer(X),\n"
" Y\n"
")\n",
30
25
),
?assertFormat(
"?assertMatch(X when is_integer(X), Y)",
Expand Down Expand Up @@ -3434,10 +3461,10 @@ spec(Config) when is_list(Config) ->
50
),
?assertFormat(
"-spec foo(very_long_type(), another_long_type()) -> some_very:very(long, type) when Int :: integer().",
"-spec foo(very_very_long_type(with_argument), yet_another_long_type()) -> some_very:very(long, type) when Int :: integer().",
"-spec foo(\n"
" very_long_type(),\n"
" another_long_type()\n"
" very_very_long_type(with_argument),\n"
" yet_another_long_type()\n"
") -> some_very:very(long, type) when\n"
" Int :: integer().\n",
50
Expand Down Expand Up @@ -3611,6 +3638,34 @@ spec(Config) when is_list(Config) ->
" String :: string()\n"
"%% before dot\n"
".\n"
),
?assertFormat(
"-spec long_name(Long, Arguments) -> ok.",
"-spec long_name(\n"
" Long, Arguments\n"
") -> ok.\n",
25
),
?assertSame(
"-spec long_name(\n"
" Long, Arguments\n"
") -> ok.\n"
),
?assertFormat(
"-spec long_name(\n"
" Long, Arguments\n"
") -> ok.\n",
"-spec long_name(\n"
" Long,\n"
" Arguments\n"
") -> ok.\n",
10
),
?assertSame(
"-spec long_name(\n"
" Long,\n"
" Arguments\n"
") -> ok.\n"
).

define(Config) when is_list(Config) ->
Expand Down

0 comments on commit ea1ad21

Please sign in to comment.