Skip to content

Commit

Permalink
Properly indent tripple-quoted strings
Browse files Browse the repository at this point in the history
  • Loading branch information
michalmuskala committed Sep 3, 2024
1 parent 8a7d8f6 commit 925e48a
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 5 deletions.
24 changes: 22 additions & 2 deletions src/erlfmt_format.erl
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,8 @@ surround_block(Left, Doc, Right) ->
string_to_algebra(Text) ->
case string:split(Text, "\n", all) of
["\"\"\"" | _Rest] ->
string(Text);
["\"\"\"" ++ _ = Quote | Rest] ->
tripple_quoted_to_algebra(Quote, Rest);
[Line] ->
string(Line);
[First, "\""] ->
Expand All @@ -302,6 +302,26 @@ string_to_algebra(Text) ->
concat([force_breaks(), FirstD, line(), LinesD])
end.

%% tripple-quoted string might have actually more than tripple quotes
%% indentation to remove from each line is given from the closing parenteses per documentation
%% see: https://www.erlang.org/doc/system/data_types#string
tripple_quoted_to_algebra(Quote, LinesWithFinalQuote) ->
{Lines, FinalQuote} = splitlast(LinesWithFinalQuote, []),
IndentToRemove = calculate_indentation(Quote, FinalQuote, []),
CleanedLinesD = [string(remove_indentation(Line, IndentToRemove)) || Line <- Lines],
LinesD = fold_doc(fun erlfmt_algebra:line/2, CleanedLinesD),
QuoteD = string(Quote),
line(QuoteD, line(LinesD, QuoteD)).

splitlast([T], Acc) -> {lists:reverse(Acc), T};
splitlast([H | T], Acc) -> splitlast(T, [H | Acc]).

calculate_indentation(Quote, Quote, Acc) -> lists:reverse(Acc);
calculate_indentation(Quote, [I | Rest], Acc) -> calculate_indentation(Quote, Rest, [I | Acc]).

remove_indentation(Line, []) -> Line;
remove_indentation([H | LineRest], [H | IndentRest]) -> remove_indentation(LineRest, IndentRest).

string_lines_to_algebra([LastLine]) ->
string(["\"" | LastLine]);
string_lines_to_algebra([Line, "\""]) ->
Expand Down
14 changes: 13 additions & 1 deletion test/erlfmt_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
snapshot_enclosing_range2/1,
snapshot_enclosing_range_no_leak/1,
snapshot_range_reinjected/1,
snapshot_tripple_string/1,
contains_pragma/1,
insert_pragma/1,
overlong_warning/1,
Expand All @@ -115,6 +116,11 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.

init_per_group(otp_27_snapshot_tests, Config) ->
case erlang:system_info(otp_release) >= "27" of
true -> Config;
false -> {skip, "Skipping tests for features from OTP >= 27"}
end;
init_per_group(_GroupName, Config) ->
Config.

Expand Down Expand Up @@ -172,7 +178,11 @@ groups() ->
snapshot_ignore_format_old,
snapshot_ignore_format_many_old,
snapshot_empty,
format_string_unicode
format_string_unicode,
{group, otp_27_snapshot_tests}
]},
{otp_27_snapshot_tests, [parallel], [
snapshot_tripple_string
]},
{error_tests, [parallel], [
error_ignore_begin_ignore,
Expand Down Expand Up @@ -1099,6 +1109,8 @@ snapshot_empty(Config) -> snapshot_same("empty.erl", Config).
snapshot_insert_pragma_with(Config) when is_list(Config) ->
snapshot_same("pragma.erl", [{pragma, insert} | Config]).

snapshot_tripple_string(Config) -> snapshot_formatted("tripple_string.erl", Config).

snapshot_same(Module, Config) ->
Pragma = proplists:get_value(pragma, Config, ignore),
snapshot_match(Module, Module, Config, [{pragma, Pragma}]).
Expand Down
38 changes: 38 additions & 0 deletions test/erlfmt_SUITE_data/tripple_string.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-module(triple_string).
-moduledoc """
abc
""".

-doc "foobar".
-type my_type :: integer().

-doc """
baz
extra
""".
-opaque otype :: integer().

-doc "qux".
foo() ->
"""
foo
""".

bar() ->
"""
the
long
string
""".

baz() ->
"foo
".

four_quotes() ->
""""
"""
the
long
string
"""".
37 changes: 37 additions & 0 deletions test/erlfmt_SUITE_data/tripple_string.erl.formatted
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
-module(triple_string).
-moduledoc """
abc
""".

-doc "foobar".
-type my_type :: integer().

-doc """
baz
extra
""".
-opaque otype :: integer().

-doc "qux".
foo() ->
"""
foo
""".

bar() ->
"""
the
long
string
""".

baz() ->
"foo\n".

four_quotes() ->
""""
"""
the
long
string
"""".
19 changes: 17 additions & 2 deletions test/erlfmt_format_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,28 @@ sigils(Config) when is_list(Config) ->
%% The modifier X does not technically exist, but there seems to be no supported
%% modifiers yet even though they are correctly parsed.
?assertSame("~b\"abc\\txyz\"x\n"),
?assertSame("~s\"\"\"\n\\tabc\n\\tdef\n\"\"\"\n"),
%% https://www.erlang.org/blog/highlights-otp-27/#triple-quoted-strings
?assertSame(
?assertFormat(
"\t\"\"\"\n"
"\t\tTest\n"
"\t\t Test\n"
"\t\"\"\"\n",
"\"\"\"\n"
"\tTest\n"
"\t Test\n"
"\"\"\"\n"
),
?assertFormat(
" \"\"\"\n"
" Test\n"
" Test\n"
" \"\"\"\n",
"\"\"\"\n"
"Test\n"
" Test\n"
"\"\"\"\n"
),
?assertSame("~s\"\"\"\n\\tabc\n\\tdef\n\"\"\"\n"),
?assertSame("\"\"\"\nTest\nMultiline\n\"\"\"\n"),
?assertSame("~\"\"\"\nTest\nMultiline\n\"\"\"\n").

Expand Down

0 comments on commit 925e48a

Please sign in to comment.