Skip to content

Commit dd30768

Browse files
author
José Valim
committed
Consider commas when breaking groups, closes #7406
Signed-off-by: José Valim <[email protected]>
1 parent bf1d993 commit dd30768

File tree

5 files changed

+113
-25
lines changed

5 files changed

+113
-25
lines changed

lib/elixir/lib/code/formatter.ex

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ defmodule Code.Formatter do
10771077
#
10781078
defp call_args_to_algebra([], meta, _context, _parens, _list_to_keyword?, state) do
10791079
{args_doc, _join, state} =
1080-
args_to_algebra_with_comments([], meta, false, false, :glue, state, &{&1, &2})
1080+
args_to_algebra_with_comments([], meta, false, :none, :glue, state, &{&1, &2})
10811081

10821082
{{surround("(", args_doc, ")"), state}, false}
10831083
end
@@ -1125,6 +1125,7 @@ defmodule Code.Formatter do
11251125
call_args_to_algebra_with_no_parens_keywords(meta, left, right, context, extra, state)
11261126
else
11271127
next_break_fits? = next_break_fits?(right, state)
1128+
last_arg_mode = if next_break_fits?, do: :next_break_fits, else: :none
11281129
force_keyword? = keyword? and force_keyword?(right)
11291130
non_empty_eol? = left != [] and not next_break_fits? and Keyword.get(meta, :eol, false)
11301131
join = if generators_count > 1 or force_keyword? or non_empty_eol?, do: :line, else: :glue
@@ -1135,7 +1136,7 @@ defmodule Code.Formatter do
11351136
args,
11361137
meta,
11371138
skip_parens?,
1138-
next_break_fits?,
1139+
last_arg_mode,
11391140
join,
11401141
state,
11411142
&quoted_to_algebra(&1, context, &2)
@@ -1171,12 +1172,12 @@ defmodule Code.Formatter do
11711172
to_algebra_fun = &quoted_to_algebra(&1, context, &2)
11721173

11731174
{left_doc, _join, state} =
1174-
args_to_algebra_with_comments(left, meta, true, false, :glue, state, to_algebra_fun)
1175+
args_to_algebra_with_comments(left, meta, true, :force_comma, :glue, state, to_algebra_fun)
11751176

11761177
{right_doc, _join, state} =
1177-
args_to_algebra_with_comments(right, meta, false, false, :glue, state, to_algebra_fun)
1178+
args_to_algebra_with_comments(right, meta, false, :none, :glue, state, to_algebra_fun)
11781179

1179-
right_doc = "," |> glue(right_doc) |> force_keyword(right) |> group(:inherit)
1180+
right_doc = break() |> concat(right_doc) |> force_keyword(right) |> group(:inherit)
11801181

11811182
doc =
11821183
with_next_break_fits(true, right_doc, fn right_doc ->
@@ -1320,7 +1321,7 @@ defmodule Code.Formatter do
13201321
{args_doc, join, state} =
13211322
args
13221323
|> Enum.with_index()
1323-
|> args_to_algebra_with_comments(meta, false, false, join, state, to_algebra_fun)
1324+
|> args_to_algebra_with_comments(meta, false, :none, join, state, to_algebra_fun)
13241325

13251326
if join == :flex_glue do
13261327
{"<<" |> concat(args_doc) |> nest(2) |> concat(">>") |> group(), state}
@@ -1379,7 +1380,7 @@ defmodule Code.Formatter do
13791380
fun = &quoted_to_algebra(&1, :parens_arg, &2)
13801381

13811382
{args_doc, _join, state} =
1382-
args_to_algebra_with_comments(args, meta, false, false, join, state, fun)
1383+
args_to_algebra_with_comments(args, meta, false, :none, join, state, fun)
13831384

13841385
{surround("[", args_doc, "]"), state}
13851386
end
@@ -1390,7 +1391,7 @@ defmodule Code.Formatter do
13901391
{left_doc, state} = fun.(left, state)
13911392

13921393
{right_doc, _join, state} =
1393-
args_to_algebra_with_comments(right, meta, false, false, join, state, fun)
1394+
args_to_algebra_with_comments(right, meta, false, :none, join, state, fun)
13941395

13951396
args_doc =
13961397
left_doc
@@ -1406,7 +1407,7 @@ defmodule Code.Formatter do
14061407
fun = &quoted_to_algebra(&1, :parens_arg, &2)
14071408

14081409
{args_doc, _join, state} =
1409-
args_to_algebra_with_comments(args, meta, false, false, join, state, fun)
1410+
args_to_algebra_with_comments(args, meta, false, :none, join, state, fun)
14101411

14111412
name_doc = "%" |> concat(name_doc) |> concat("{")
14121413
{surround(name_doc, args_doc, "}"), state}
@@ -1417,7 +1418,7 @@ defmodule Code.Formatter do
14171418
fun = &quoted_to_algebra(&1, :parens_arg, &2)
14181419

14191420
{args_doc, join, state} =
1420-
args_to_algebra_with_comments(args, meta, false, false, join, state, fun)
1421+
args_to_algebra_with_comments(args, meta, false, :none, join, state, fun)
14211422

14221423
if join == :flex_glue do
14231424
{"{" |> concat(args_doc) |> nest(1) |> concat("}") |> group(), state}
@@ -1527,18 +1528,19 @@ defmodule Code.Formatter do
15271528
defp heredoc_line(["", _ | _]), do: nest(line(), :reset)
15281529
defp heredoc_line(_), do: line()
15291530

1530-
defp args_to_algebra_with_comments(args, meta, skip_parens?, next_break_fits?, join, state, fun) do
1531+
defp args_to_algebra_with_comments(args, meta, skip_parens?, last_arg_mode, join, state, fun) do
15311532
min_line = line(meta)
15321533
max_line = end_line(meta)
15331534

15341535
arg_to_algebra = fn arg, args, newlines, state ->
15351536
{doc, state} = fun.(arg, state)
15361537

15371538
doc =
1538-
cond do
1539-
args != [] -> concat(doc, ",")
1540-
next_break_fits? -> next_break_fits(doc, :enabled)
1541-
true -> doc
1539+
case args do
1540+
[_ | _] -> concat_to_last_group(doc, ",")
1541+
[] when last_arg_mode == :force_comma -> concat_to_last_group(doc, ",")
1542+
[] when last_arg_mode == :next_break_fits -> next_break_fits(doc, :enabled)
1543+
[] when last_arg_mode == :none -> doc
15421544
end
15431545

15441546
{doc, @empty, newlines, state}
@@ -1753,7 +1755,7 @@ defmodule Code.Formatter do
17531755
fun = &clause_args_to_algebra/2
17541756

17551757
{args_docs, _join, state} =
1756-
args_to_algebra_with_comments([args], meta, false, false, :glue, state, fun)
1758+
args_to_algebra_with_comments([args], meta, false, :none, :glue, state, fun)
17571759

17581760
{args_docs, state}
17591761
end
@@ -2122,6 +2124,20 @@ defmodule Code.Formatter do
21222124

21232125
## Algebra helpers
21242126

2127+
# Relying on the inner document is brittle and error prone.
2128+
# It would be best if we had a mechanism to apply this.
2129+
defp concat_to_last_group({:doc_cons, left, right}, concat) do
2130+
{:doc_cons, left, concat_to_last_group(right, concat)}
2131+
end
2132+
2133+
defp concat_to_last_group({:doc_group, group, mode}, concat) do
2134+
{:doc_group, {:doc_cons, group, concat}, mode}
2135+
end
2136+
2137+
defp concat_to_last_group(other, concat) do
2138+
{:doc_cons, other, concat}
2139+
end
2140+
21252141
defp ungroup_if_group({:doc_group, group, _mode}), do: group
21262142
defp ungroup_if_group(other), do: other
21272143

lib/elixir/test/elixir/code_formatter/calls_test.exs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,21 @@ defmodule Code.Formatter.CallsTest do
201201
assert_format bad, good, @short_length
202202
end
203203

204+
test "with arguments on comma limit" do
205+
bad = """
206+
import(foo(abc, cde), :next)
207+
"""
208+
209+
good = """
210+
import(
211+
foo(abc, cde),
212+
:next
213+
)
214+
"""
215+
216+
assert_format bad, good, @medium_length
217+
end
218+
204219
test "with keyword lists" do
205220
assert_same "foo(foo: 1, bar: 2)"
206221

@@ -263,6 +278,22 @@ defmodule Code.Formatter.CallsTest do
263278
assert_format bad, good, @short_length
264279
end
265280

281+
test "without parens on comma limit" do
282+
bad = """
283+
import foo(abc, cde), :next
284+
"""
285+
286+
good = """
287+
import foo(
288+
abc,
289+
cde
290+
),
291+
:next
292+
"""
293+
294+
assert_format bad, good, @medium_length
295+
end
296+
266297
test "without parens and with keyword lists preserves multiline" do
267298
assert_same """
268299
defstruct foo: 1,
@@ -276,6 +307,22 @@ defmodule Code.Formatter.CallsTest do
276307
"""
277308
end
278309

310+
test "without parens and with keyword lists on comma limit" do
311+
bad = """
312+
import foo(abc, cde), opts: :next
313+
"""
314+
315+
good = """
316+
import foo(
317+
abc,
318+
cde
319+
),
320+
opts: :next
321+
"""
322+
323+
assert_format bad, good, @medium_length
324+
end
325+
279326
test "without parens and with keyword lists on line limit" do
280327
assert_same "import :atom, opts: [foo: :bar]"
281328

lib/elixir/test/elixir/code_formatter/containers_test.exs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,25 @@ defmodule Code.Formatter.ContainersTest do
178178
assert_same keyword, @short_length
179179
end
180180

181+
test "with keyword lists on comma line limit" do
182+
bad = """
183+
[
184+
foooo: 1,
185+
barrr: 2
186+
]
187+
"""
188+
189+
good = """
190+
[
191+
foooo:
192+
1,
193+
barrr: 2
194+
]
195+
"""
196+
197+
assert_format bad, good, @short_length
198+
end
199+
181200
test "with quoted keyword lists" do
182201
assert_same ~S(["with spaces": 1])
183202
assert_same ~S(["one #{two} three": 1])
@@ -455,7 +474,7 @@ defmodule Code.Formatter.ContainersTest do
455474
}
456475
"""
457476

458-
assert_format bad, good, @short_length
477+
assert_format bad, good, line_length: 11
459478
end
460479

461480
test "removes trailing comma" do
@@ -559,7 +578,7 @@ defmodule Code.Formatter.ContainersTest do
559578
}
560579
"""
561580

562-
assert_format bad, good, @short_length
581+
assert_format bad, good, line_length: 11
563582
end
564583

565584
test "removes trailing comma" do

lib/elixir/test/elixir/code_formatter/integration_test.exs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,17 +348,17 @@ defmodule Code.Formatter.IntegrationTest do
348348
test "no parens keywords at the end of the line" do
349349
bad = """
350350
defmodule Mod do
351-
defp token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char), do: token_list_downcase(rest, acc)
352-
defp token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33), do: token_list_downcase(rest, acc)
351+
def token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char), do: token_list_downcase(rest, acc)
352+
def token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33), do: token_list_downcase(rest, acc)
353353
end
354354
"""
355355

356356
assert_format bad, """
357357
defmodule Mod do
358-
defp token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char),
358+
def token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char),
359359
do: token_list_downcase(rest, acc)
360360
361-
defp token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33),
361+
def token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33),
362362
do: token_list_downcase(rest, acc)
363363
end
364364
"""

lib/elixir/test/elixir/kernel/typespec_test.exs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -835,8 +835,12 @@ defmodule Kernel.TypespecTest do
835835
quote(do: @type(literal_keyword_list_type_key() :: [{binary(), integer()}])),
836836
quote(do: @type(literal_empty_map() :: %{})),
837837
quote(do: @type(literal_map_with_key() :: %{key: integer()})),
838-
quote(do: @type(literal_map_with_required_key() :: %{required(bitstring()) => integer()})),
839-
quote(do: @type(literal_map_with_optional_key() :: %{optional(bitstring()) => integer()})),
838+
quote(
839+
do: @type(literal_map_with_required_key() :: %{required(bitstring()) => integer()})
840+
),
841+
quote(
842+
do: @type(literal_map_with_optional_key() :: %{optional(bitstring()) => integer()})
843+
),
840844
quote(do: @type(literal_struct_all_fields_any_type() :: %SomeStruct{})),
841845
quote(do: @type(literal_struct_all_fields_key_type() :: %SomeStruct{key: integer()})),
842846
quote(do: @type(literal_empty_tuple() :: {})),
@@ -863,7 +867,9 @@ defmodule Kernel.TypespecTest do
863867
quote(do: @type(builtin_list() :: list())),
864868
quote(do: @type(builtin_nonempty_list() :: nonempty_list())),
865869
quote(do: @type(builtin_maybe_improper_list() :: maybe_improper_list())),
866-
quote(do: @type(builtin_nonempty_maybe_improper_list() :: nonempty_maybe_improper_list())),
870+
quote(
871+
do: @type(builtin_nonempty_maybe_improper_list() :: nonempty_maybe_improper_list())
872+
),
867873
quote(do: @type(builtin_mfa() :: mfa())),
868874
quote(do: @type(builtin_module() :: module())),
869875
quote(do: @type(builtin_no_return() :: no_return())),

0 commit comments

Comments
 (0)