New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Function with multi-clause style collapses awkwardly with single clause #7718
Comments
This should be a straight-forward change. Either we are only checking for newlines if you have more than two clauses or the newline checking is only between clauses, which means it won't work here. |
Can you provide an example? My formatted result is a bit different than yours (but still awkward) # Ex 1
# Original
SomeModule.long_function_name_that_approaches_max_columns(argument, acc, fn
%SomeStruct{key: key}, acc -> more_code(key, acc)
end)
# Formatted
SomeModule.long_function_name_that_approaches_max_columns(argument, acc, fn %SomeStruct{key: key},
acc ->
more_code(key, acc)
end)
# Ex 2
# Original
SomeModule.long_function_name_that_approaches_max_columns(argument, acc, fn
%SomeStruct{key: key}, %SomeStruct{key: key}, acc -> more_code(key, acc)
end)
# Formatted
SomeModule.long_function_name_that_approaches_max_columns(argument, acc, fn %SomeStruct{key: key},
%SomeStruct{key: key},
acc ->
more_code(key, acc)
end) |
@Sihui I believe he meant:
|
Thanks, @josevalim. I took a look. Seems like to some extends, that's actually what we intended. The place that does that is Code.formatter#anon_fun_to_algebra: # fn x -> y end
# fn x ->
# y
# end
defp anon_fun_to_algebra([{:->, meta, [args, body]}] = clauses, _min_line, max_line, state) do
So if the fn x ->
y
end
fn a,
b,
c ->
y
end It becomes awkward when the string before SomeModule.long_function_name_that_approaches_max_columns(argument, acc, fn a,
b,
c ->
y
end
The reason multi-clasues funs being formatted differently is because we have a different method handing that case. # fn
# args1 ->
# block1
# args2 ->
# block2
# end
defp anon_fun_to_algebra(clauses, min_line, max_line, state) do
{clauses_doc, state} = clauses_to_algebra(clauses, min_line, max_line, state)
{"fn" |> line(clauses_doc) |> nest(2) |> line("end") |> force_unfit(), state}
end So in the case of multi-clasue fun, I don't think we are checking user-inputted new lines. We just always put the clauses in a new line. It would be great if there's a way to check if |
Hi @Sihui, great analysis. In order to know if In that line, you can see the parsing rule that handles
The '$1' syntax means you will pass the first token on the right hand side of If you do so, you can run |
Thanks a lot, @josevalim! I don't think annotate_newlines({_, {_, _, Count}}, {Left, Meta, Right}) when is_integer(Count), is_list(Meta) ->
case ?formatter_metadata() of
true -> {Left, [{newlines, Count} | Meta], Right};
false -> {Left, Meta, Right}
end;
annotate_newlines(_, Expr) ->
Expr.
I thought we have to change fn -> fn_expr : ['$1'].
fn -> fn eoe fn_expr : [annotate_newlines('$2', '$3') | '$1'].
fn_eoe -> 'fn' : '$1'.
fn_eoe -> 'fn' eoe : '$1'.
fn_expr -> expr :
'$1'.
fn_expr -> fn_op_eol_and_expr :
build_op(element(1, '$1'), [], element(2, '$1')).
fn_expr -> empty_paren fn_op_eol_and_expr :
build_op(element(1, '$2'), [], element(2, '$2')).
fn_expr -> empty_paren when_op expr fn_op_eol_and_expr :
build_op(element(1, '$4'), [{'when', meta_from_token('$2'), ['$3']}], element(2, '$4')).
fn_expr -> call_args_no_parens_all fn_op_eol_and_expr :
build_op(element(1, '$2'), unwrap_when(unwrap_splice('$1')), element(2, '$2')).
fn_expr -> fn_parens_many fn_op_eol_and_expr :
build_op(element(1, '$2'), unwrap_splice('$1'), element(2, '$2')).
fn_expr -> fn_parens_many when_op expr fn_op_eol_and_expr :
build_op(element(1, '$4'), [{'when', meta_from_token('$2'), unwrap_splice('$1') ++ ['$3']}], element(2, '$4')).
fn_op_eol_and_expr -> fn_op_eol expr : {'$1', '$2'}.
fn_op_eol_and_expr -> fn_op_eol : warn_empty_fn_clause('$1'), {'$1', handle_literal(nil, '$1')}.
fn_parens_many -> open_paren call_args_no_parens_kw close_paren : ['$2'].
fn_parens_many -> open_paren call_args_no_parens_many close_paren : '$2'. I also saw that we have build_fn already. Maybe I should find a way to leverage that? Sorry for all the back and forth, I'm pretty new to both Elixir and Erlang. Thank you for your patience! |
You are correct. I am not sure how to move this forward then, I would also need to sit down and try some different approaches, and I won't have the time to do it right now. Sorry. |
No worries. I might take another look during the weekend and see if I can come up with something. The |
Not sure which behavior we want, so I have two PRs out. V1: When fn is followed by a newline, use multi-clause formatting style. For this example: SomeModule.long_function_name_that_approaches_max_columns(argument, acc, fn
%SomeStruct{key: key}, acc -> more_code(key, acc)
end) In V1, the example will stay the same. SomeModule.long_function_name_that_approaches_max_columns(argument, acc, fn
%SomeStruct{key: key}, acc ->
more_code(key, acc)
end) As you can see in the test, V1 also changes how we format comments in some cases. But V2 doesn't. |
Environment
Current behavior
I've found functions with multi-clause style collapses awkwardly when a single clause is used. Given the following code block:
The formatter produces:
Expected behavior
The formatter does not change the original code. I realize using the multi-clause style could be "cheating" in this case, but if you add a second clause, the formatter does not change the code, and the formatted code in this case is very awkwardly styled. Thanks for taking a look!
The text was updated successfully, but these errors were encountered: