From ffa8320905400a9eb7671c43741df0815d8e8899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muska=C5=82a?= Date: Mon, 2 Sep 2024 09:09:02 -0700 Subject: [PATCH 1/4] Fix tests: otp_27_features group was actually never running --- test/erlfmt_SUITE.erl | 1 - test/erlfmt_format_SUITE.erl | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/erlfmt_SUITE.erl b/test/erlfmt_SUITE.erl index d751c92..3cba2dc 100644 --- a/test/erlfmt_SUITE.erl +++ b/test/erlfmt_SUITE.erl @@ -80,7 +80,6 @@ error_ignore_begin_ignore_old/1, error_ignore_begin_ignore_begin_old/1, error_ignore_end_old/1, - error_ignore_ignore_old/1, simple_comments_range/1, broken_range/1, snapshot_range_whole_comments/1, diff --git a/test/erlfmt_format_SUITE.erl b/test/erlfmt_format_SUITE.erl index 4fd2247..817b051 100644 --- a/test/erlfmt_format_SUITE.erl +++ b/test/erlfmt_format_SUITE.erl @@ -91,8 +91,6 @@ init_per_group(otp_27_features, Config) -> true -> Config; false -> {skip, "Skipping tests for features from OTP >= 27"} end; -init_per_group(_, Config) -> - Config; init_per_group(_GroupName, Config) -> Config. @@ -179,6 +177,7 @@ all() -> [ {group, expressions}, {group, forms}, + {group, otp_27_features}, comment ]. From 8a7d8f659fa552db79e2c94ecccb4e433a741e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muska=C5=82a?= Date: Mon, 2 Sep 2024 09:12:58 -0700 Subject: [PATCH 2/4] Keep tripple-quoted strings as tripple-quoted even if single-line In general this seesm to align more closely with the stated philosophy of erlfmt --- src/erlfmt_format.erl | 2 -- test/erlfmt_format_SUITE.erl | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/erlfmt_format.erl b/src/erlfmt_format.erl index dcb1c96..0de8891 100644 --- a/src/erlfmt_format.erl +++ b/src/erlfmt_format.erl @@ -290,8 +290,6 @@ surround_block(Left, Doc, Right) -> string_to_algebra(Text) -> case string:split(Text, "\n", all) of - ["\"\"\"", Line, "\"\"\""] -> - string_to_algebra(["\"", Line, "\""]); ["\"\"\"" | _Rest] -> string(Text); [Line] -> diff --git a/test/erlfmt_format_SUITE.erl b/test/erlfmt_format_SUITE.erl index 817b051..06c6d67 100644 --- a/test/erlfmt_format_SUITE.erl +++ b/test/erlfmt_format_SUITE.erl @@ -256,11 +256,10 @@ sigils(Config) when is_list(Config) -> ?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 - ?assertFormat( + ?assertSame( "\"\"\"\n" "Test\n" - "\"\"\"\n", - "\"Test\"\n" + "\"\"\"\n" ), ?assertSame("\"\"\"\nTest\nMultiline\n\"\"\"\n"), ?assertSame("~\"\"\"\nTest\nMultiline\n\"\"\"\n"). From 925e48a63581b501dcf0215b879b7ff979b6b110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muska=C5=82a?= Date: Tue, 3 Sep 2024 02:06:58 -0700 Subject: [PATCH 3/4] Properly indent tripple-quoted strings --- src/erlfmt_format.erl | 24 +++++++++++- test/erlfmt_SUITE.erl | 14 ++++++- test/erlfmt_SUITE_data/tripple_string.erl | 38 +++++++++++++++++++ .../tripple_string.erl.formatted | 37 ++++++++++++++++++ test/erlfmt_format_SUITE.erl | 19 +++++++++- 5 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 test/erlfmt_SUITE_data/tripple_string.erl create mode 100644 test/erlfmt_SUITE_data/tripple_string.erl.formatted diff --git a/src/erlfmt_format.erl b/src/erlfmt_format.erl index 0de8891..fd53479 100644 --- a/src/erlfmt_format.erl +++ b/src/erlfmt_format.erl @@ -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, "\""] -> @@ -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, "\""]) -> diff --git a/test/erlfmt_SUITE.erl b/test/erlfmt_SUITE.erl index 3cba2dc..a8e0a98 100644 --- a/test/erlfmt_SUITE.erl +++ b/test/erlfmt_SUITE.erl @@ -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, @@ -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. @@ -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, @@ -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}]). diff --git a/test/erlfmt_SUITE_data/tripple_string.erl b/test/erlfmt_SUITE_data/tripple_string.erl new file mode 100644 index 0000000..9a00a8a --- /dev/null +++ b/test/erlfmt_SUITE_data/tripple_string.erl @@ -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 + """". diff --git a/test/erlfmt_SUITE_data/tripple_string.erl.formatted b/test/erlfmt_SUITE_data/tripple_string.erl.formatted new file mode 100644 index 0000000..615fd75 --- /dev/null +++ b/test/erlfmt_SUITE_data/tripple_string.erl.formatted @@ -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 + """". diff --git a/test/erlfmt_format_SUITE.erl b/test/erlfmt_format_SUITE.erl index 06c6d67..b33c917 100644 --- a/test/erlfmt_format_SUITE.erl +++ b/test/erlfmt_format_SUITE.erl @@ -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"). From fcb71a3bb38abc7a301015d740fcefae5c212236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muska=C5=82a?= Date: Tue, 3 Sep 2024 02:13:45 -0700 Subject: [PATCH 4/4] Remove docs from doc and moduledoc attributes --- src/erlfmt_format.erl | 6 ++++++ test/erlfmt_format_SUITE.erl | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/erlfmt_format.erl b/src/erlfmt_format.erl index fd53479..2f7d3b5 100644 --- a/src/erlfmt_format.erl +++ b/src/erlfmt_format.erl @@ -93,6 +93,12 @@ to_algebra({attribute, Meta, {atom, _, record}, [Name, {tuple, TMeta, Values} = Doc = surround(HeadD, <<"">>, expr_to_algebra(Tuple), <<"">>, <<")">>), combine_comments_with_dot(Meta, Doc) end; +to_algebra({attribute, Meta, {atom, _, RawName} = Name, [Value]}) when + RawName =:= doc; RawName =:= moduledoc +-> + ValueD = expr_to_algebra(Value), + Doc = concat([<<"-">>, expr_to_algebra(Name), <<" ">>, ValueD]), + combine_comments_with_dot(Meta, Doc); to_algebra({attribute, Meta, Name, Values}) -> Doc = concat(<<"-">>, attribute_to_algebra(Name), call(Meta, Values, <<"(">>, <<")">>)), combine_comments_with_dot(Meta, Doc); diff --git a/test/erlfmt_format_SUITE.erl b/test/erlfmt_format_SUITE.erl index b33c917..7fa0b0f 100644 --- a/test/erlfmt_format_SUITE.erl +++ b/test/erlfmt_format_SUITE.erl @@ -4283,10 +4283,10 @@ comment(Config) when is_list(Config) -> ). doc_attributes(Config) when is_list(Config) -> - ?assertSame("-moduledoc(\"Test\").\n-moduledoc(#{since => <<\"1.0.0\">>}).\n"), - ?assertSame("-moduledoc(\"\"\"\nTest\nMultiline\n\"\"\").\n"), - ?assertSame("-doc(\"Test\").\n-doc(#{since => <<\"1.0.0\">>}).\ntest() -> ok.\n"), - ?assertSame("-doc(\"Test\").\n-doc(#{since => <<\"1.0.0\">>}).\n-type t() :: ok.\n"). + ?assertSame("-moduledoc \"Test\".\n-moduledoc #{since => <<\"1.0.0\">>}.\n"), + ?assertSame("-moduledoc \"\"\"\nTest\nMultiline\n\"\"\".\n"), + ?assertSame("-doc \"Test\".\n-doc #{since => <<\"1.0.0\">>}.\ntest() -> ok.\n"), + ?assertSame("-doc \"Test\".\n-doc #{since => <<\"1.0.0\">>}.\n-type t() :: ok.\n"). doc_macros(Config) when is_list(Config) -> %% Doc Attributes as macros is a common pattern for OTP < 27 compatibility.