Skip to content

Commit 7f39939

Browse files
committed
Fix non-idempotent formatting of multi-line binary strings
When a binary contains a string with newlines, string_to_algebra splits it into multiple lines. On re-parse, the AST represents this as a concat node instead of a string node. break_behaviour and is_tag only matched the string form, causing different layout decisions on the second pass. Fix by checking is_multiline_string in both break_behaviour and is_tag so that a multi-line string in a bin_element is treated the same way as the concat that it will become after formatting.
1 parent 3b157c0 commit 7f39939

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

src/erlfmt_format.erl

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,11 @@ break_behaviour(Meta, Values, Break) ->
528528
case Break =:= flex_break andalso not ForceOuter of
529529
true ->
530530
case Values of
531-
[{bin_element, _, {string, _, _}, _, _}] ->
532-
no_break;
531+
[{bin_element, _, {string, SMeta, _}, _, _}] ->
532+
case is_multiline_string(SMeta) of
533+
true -> #break{outer = false, inner = false};
534+
false -> no_break
535+
end;
533536
[SoleElement] ->
534537
case is_container(SoleElement) of
535538
true -> no_break;
@@ -550,8 +553,8 @@ break_behaviour(Meta, Values, Break) ->
550553
end.
551554

552555
%% Allow inlining binary literals
553-
is_tag({bin, _, [{bin_element, _, {string, _, _}, _, _}]}) ->
554-
true;
556+
is_tag({bin, _, [{bin_element, _, {string, SMeta, _}, _, _}]}) ->
557+
not is_multiline_string(SMeta);
555558
is_tag({bin, _, []}) ->
556559
true;
557560
%% Allow inlining argless macros
@@ -565,6 +568,14 @@ is_tag(Expr) ->
565568
is_container(Expr) ->
566569
lists:member(element(1, Expr), [tuple, bin, list, map, record, call, macro_call, lc, bc]).
567570

571+
is_multiline_string(Meta) ->
572+
Text = erlfmt_scan:get_anno(text, Meta),
573+
case string:split(Text, "\n", all) of
574+
[_] -> false;
575+
[_, "\""] -> false;
576+
_ -> true
577+
end.
578+
568579
surround_container(no_break, Left, Doc, Right) ->
569580
concat(Left, Doc, Right);
570581
surround_container(flex_break, Left, Doc, Right) ->

test/erlfmt_SUITE.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
map_comprehension/1,
5454
snapshot_simple_comments/1,
5555
snapshot_big_binary/1,
56+
snapshot_big_binary_string/1,
5657
snapshot_attributes/1,
5758
snapshot_escript/1,
5859
snapshot_emulator_args/1,
@@ -163,6 +164,7 @@ groups() ->
163164
{snapshot_tests, [parallel], [
164165
snapshot_simple_comments,
165166
snapshot_big_binary,
167+
snapshot_big_binary_string,
166168
snapshot_attributes,
167169
snapshot_escript,
168170
snapshot_emulator_args,
@@ -1068,6 +1070,7 @@ parse_forms(String) ->
10681070
snapshot_simple_comments(Config) -> snapshot_same("simple_comments.erl", Config).
10691071

10701072
snapshot_big_binary(Config) -> snapshot_same("big_binary.erl", Config).
1073+
snapshot_big_binary_string(Config) -> snapshot_same("big_binary_string.erl", Config).
10711074

10721075
snapshot_attributes(Config) -> snapshot_same("attributes.erl", Config).
10731076

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-module(big_binary_string).
2+
3+
-export([big_binary_string/0]).
4+
5+
big_binary_string() ->
6+
<<
7+
"{\"key\": \"value\", \"nested\": {\"items\": [\"item1\", \"item2\", \"item3\"]}\n"
8+
"\"description\": \"A somewhat long description that goes on and on and is very verbose\"\n"
9+
>>.

0 commit comments

Comments
 (0)