Skip to content
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

preserve a single empty line between attributes #239

Merged
merged 1 commit into from
Jan 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions erlang_ls.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@ include_dirs:
- "_build/*/lib/*/include"
deps_dirs:
- "_build/*/lib/*"
diagnostics:
disabled:
- xref
102 changes: 17 additions & 85 deletions src/erlfmt.erl
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,8 @@ insert_pragma_node(Node) ->
NewPreComments =
case PreComments of
[] ->
[
{comment, #{end_location => {2, 1}, location => {1, 1}}, [
"%% @format",
""
]}
];
Loc = erlfmt_scan:get_anno(location, Node),
[{comment, #{location => Loc, end_location => Loc}, ["%% @format", ""]}];
_ ->
{comment, Loc, LastComments} = lists:last(PreComments),
lists:droplast(PreComments) ++
Expand Down Expand Up @@ -238,7 +234,7 @@ read_nodes({ok, Tokens, Comments, Cont}, FileName, Pragma, [], Warnings0, TextAc
erlfmt_scan:continue(Cont),
FileName,
Pragma,
[erlfmt_scan:put_anno(end_location, {1, 1}, Node)],
[Node],
Warnings,
TextAcc ++ LastString
);
Expand Down Expand Up @@ -267,39 +263,20 @@ read_nodes(
TextAcc
) ->
{Node, Warnings} = parse_node(Tokens, Comments, FileName, Cont, Warnings0),
case {Pragma, contains_pragma_node(Node), Node} of
{require, false, _} ->
case {Pragma, contains_pragma_node(Node)} of
{require, false} ->
{LastString, _Anno} = erlfmt_scan:last_node_string(Cont),
case erlfmt_scan:read_rest(Cont) of
{ok, Rest} ->
{skip, [TextAcc, LastString | Rest]};
{error, {ErrLoc, Mod, Reason}} ->
throw({error, {FileName, ErrLoc, Mod, Reason}})
end;
{_, _, {raw_string, RawAnno, RawString}} ->
ShebangNodeWithPostComments =
erlfmt_scan:put_anno(post_comments, [{comment, RawAnno, [RawString]}], ShebangNode),
read_nodes_loop(
erlfmt_scan:continue(Cont),
FileName,
[ShebangNodeWithPostComments],
Warnings
);
_ ->
{NodeWithoutPreComments, ShebangNodeWithPostComments} =
case erlfmt_scan:get_anno(pre_comments, Node, undefined) of
undefined ->
{Node, ShebangNode};
PreComments ->
{
erlfmt_scan:delete_anno(pre_comments, Node),
erlfmt_scan:put_anno(post_comments, PreComments, ShebangNode)
}
end,
read_nodes_loop(
erlfmt_scan:continue(Cont),
FileName,
[NodeWithoutPreComments, ShebangNodeWithPostComments],
[Node, ShebangNode],
Warnings
)
end;
Expand All @@ -316,8 +293,8 @@ read_nodes_loop({error, {ErrLoc, Mod, Reason}, _Loc}, FileName, _Acc, _Warnings)

parse_node([], _Comments, _FileName, Cont, Warnings) ->
{node_string(Cont), Warnings};
parse_node([{shebang, Meta, String}], [], _FileName, _Cont, Warnings) ->
{{shebang, Meta, String}, Warnings};
parse_node([{shebang, Meta, String}], Comments, _FileName, _Cont, Warnings) ->
{{shebang, erlfmt_recomment:put_post_comments(Meta, Comments), String}, Warnings};
parse_node([Token | _] = Tokens, Comments, FileName, Cont, Warnings) ->
{PreComments, _} = erlfmt_recomment:take_comments(erlfmt_scan:get_line(Token), Comments),
case lists:any(fun contains_ignore_comment/1, PreComments) of
Expand Down Expand Up @@ -350,36 +327,15 @@ format_nodes(Nodes, PrintWidth) ->
[$\n | Formatted] = format_nodes_loop(Nodes, PrintWidth),
Formatted.

format_nodes_loop([{attribute, _, {atom, _, spec}, _} = Attr | [_ | _] = Rest], PrintWidth) ->
[$\n, format_node(Attr, PrintWidth)] ++
format_nodes_loop(Rest, PrintWidth);
format_nodes_loop([{attribute, _, {atom, _, Name}, _} = Attr | [Next | _] = Rest], PrintWidth) when
Name =:= 'if'; Name =:= 'ifdef'; Name =:= 'ifndef'; Name =:= 'else'
->
% preserve empty line after
[$\n, format_node(Attr, PrintWidth)] ++
maybe_empty_line(Attr, Next) ++ format_nodes_loop(Rest, PrintWidth);
format_nodes_loop([Node | [{attribute, _, {atom, _, Name}, _} = Attr | _] = Rest], PrintWidth) when
Name =:= 'else'; Name =:= 'endif'
->
% preserve empty line before
[$\n, format_node(Node, PrintWidth)] ++
maybe_empty_line(Node, Attr) ++ format_nodes_loop(Rest, PrintWidth);
format_nodes_loop([{attribute, _, {atom, _, RepeatedName}, _} | _] = Nodes, PrintWidth) ->
{Attrs, Rest} = split_attrs(RepeatedName, Nodes),
MaybeEmptyLine =
case Rest of
[{attribute, _, {atom, _, Name}, _} = Attr | _] when
Name =:= 'else'; Name =:= 'endif'
->
% preserve empty line before
maybe_empty_line(lists:last(Attrs), Attr);
_ ->
"\n"
end,
format_attrs(Attrs, PrintWidth) ++ MaybeEmptyLine ++ format_nodes_loop(Rest, PrintWidth);
format_nodes_loop([Node | Rest], PrintWidth) ->
[$\n, format_node(Node, PrintWidth), $\n | format_nodes_loop(Rest, PrintWidth)];
format_nodes_loop([Node | [Next | _] = Rest], PrintWidth) ->
[
$\n,
format_node(Node, PrintWidth),
maybe_empty_line(Node, Next)
| format_nodes_loop(Rest, PrintWidth)
];
format_nodes_loop([Node], PrintWidth) ->
[$\n, format_node(Node, PrintWidth), $\n];
format_nodes_loop([], _PrintWidth) ->
[].

Expand All @@ -396,33 +352,9 @@ format_node(Node, PrintWidth) ->
Doc = erlfmt_format:to_algebra(Node),
erlfmt_algebra:format(Doc, PrintWidth).

split_attrs(PredName, Nodes) ->
lists:splitwith(
fun
({attribute, _, {atom, _, Name}, _}) -> PredName =:= Name;
(_) -> false
end,
Nodes
).

format_attrs([Attr], PrintWidth) ->
[$\n, format_node(Attr, PrintWidth)];
format_attrs([Attr | [Attr2 | _] = Rest], PrintWidth) ->
FAttr = format_node(Attr, PrintWidth),
case has_empty_line_between(Attr, Attr2) orelse has_non_comment_newline(FAttr) of
true -> [$\n, FAttr, $\n | format_attrs(Rest, PrintWidth)];
false -> [$\n, FAttr | format_attrs(Rest, PrintWidth)]
end.

has_empty_line_between(Left, Right) ->
erlfmt_scan:get_end_line(Left) + 1 < erlfmt_scan:get_line(Right).

has_non_comment_newline(String) ->
length(lists:filter(fun is_not_comment/1, string:split(String, "\n", all))) >= 2.

is_not_comment(String) ->
not (string:is_empty(String) orelse string:equal(string:slice(String, 0, 1), "%")).

verify_nodes(FileName, Nodes, Formatted) ->
case read_nodes_string(FileName, unicode:characters_to_list(Formatted)) of
{ok, Nodes2, _} ->
Expand Down
39 changes: 31 additions & 8 deletions src/erlfmt_scan.erl
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,19 @@ continue(#state{scan = Scan, inner = Inner, loc = Loc, buffer = Buffer}) ->
continue(Scan, Inner0, Loc0, []) ->
case Scan(Inner0, Loc0) of
{{ok, [{'#', _}, {'!', _} | _] = Tokens0, Loc}, Inner} ->
{ShebangTokens, Buffer} = split_shebang(Tokens0),
{ShebangTokens, Comments, Buffer} = split_shebang(Tokens0),
Shebang = unicode:characters_to_list(stringify_tokens(ShebangTokens)),
Anno = #{location => Loc0, text => Shebang},
LastAnno = erl_anno:to_term(element(2, lists:last(ShebangTokens))),
#{end_location := EndLoc} = atomic_anno(LastAnno),
Anno = #{location => Loc0, end_location => EndLoc, text => Shebang},
State = #state{
scan = Scan,
inner = Inner,
loc = Loc,
original = ShebangTokens,
buffer = Buffer
},
{ok, [{shebang, Anno, Shebang}], [], State};
{ok, [{shebang, Anno, Shebang}], Comments, State};
{{ok, Tokens, Loc}, Inner} ->
continue(Scan, Inner, Loc, Tokens);
{{error, Reason}, _Inner} ->
Expand Down Expand Up @@ -182,11 +184,32 @@ has_new_line({white_space, _, [$\n | _]}) -> true;
has_new_line(_) -> false.

split_shebang(Tokens) ->
{ShebangTokens, Rest} = lists:splitwith(
fun(Token) -> not (has_new_line(Token)) end,
Tokens
),
{ShebangTokens, Rest}.
{ShebangTokens, Rest0} = take_line(Tokens),
{ShebangComments, Rest} = take_shebang_comments(Rest0),
{ShebangTokens, ShebangComments, Rest}.

take_line(Tokens) ->
lists:splitwith(fun(Token) -> not has_new_line(Token) end, Tokens).

take_shebang_comments([{white_space, _, _}, {comment, _, "%%!" ++ _} = Comment0 | Rest]) ->
{Comment, []} = collect_comments([], Comment0),
{[Comment], drop_initial_white_space(Rest)};
take_shebang_comments([
{white_space, _, _},
{comment, _, _} = Comment1,
{white_space, _, _},
{comment, _, "%%!" ++ _} = Comment2
| Rest
]) ->
{Comment, []} = collect_comments([Comment2], Comment1),
{[Comment], drop_initial_white_space(Rest)};
take_shebang_comments(Tokens) ->
{[], Tokens}.

drop_initial_white_space([{white_space, _, _} | Rest]) ->
drop_initial_white_space(Rest);
drop_initial_white_space(Rest) ->
Rest.

stringify_tokens(Tokens) ->
lists:map(fun erl_scan:text/1, Tokens).
Expand Down
12 changes: 6 additions & 6 deletions test/erlfmt_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1081,7 +1081,7 @@ snapshot_same(Module, Config) ->
case erlfmt:format_string(Original, [{pragma, Pragma}]) of
{ok, Original, _} -> ok;
{skip, _} -> ok;
{ok, Other, _} -> ct:fail("unexpected:~n~s~n", [Other]);
{ok, Other, _} -> ct:fail({unexpected, Other, expected, Original});
Other -> ct:fail("unexpected: ~p~n", [Other])
end.

Expand Down Expand Up @@ -1282,7 +1282,7 @@ insert_pragma(Config) when is_list(Config) ->
"-export([f/3]).\n",
insert_pragma_string(
"%% attached comment\n"
"-module(pragma)\n."
"-module(pragma).\n"
"\n"
"-export([f/3]).\n"
)
Expand All @@ -1297,7 +1297,7 @@ insert_pragma(Config) when is_list(Config) ->
insert_pragma_string(
"%% single comment\n"
"\n"
"-module(pragma)\n."
"-module(pragma).\n"
"\n"
"-export([f/3]).\n"
)
Expand All @@ -1318,7 +1318,7 @@ insert_pragma(Config) when is_list(Config) ->
"%% LICENSE\n"
"%% LICENSE\n"
"\n"
"-module(pragma)\n."
"-module(pragma).\n"
"\n"
"-export([f/3]).\n"
)
Expand Down Expand Up @@ -1354,8 +1354,8 @@ insert_pragma_string(String) ->
{ok, StringWithPragma, []} = erlfmt:format_string(String, [{pragma, insert}]),
%% check that insert_pragma_nodes doesn't insert a pragma, when one has already been inserted.
?assertEqual(
{ok, StringWithPragma, []},
erlfmt:format_string(StringWithPragma, [{pragma, insert}])
erlfmt:format_string(StringWithPragma, [{pragma, insert}]),
{ok, StringWithPragma, []}
),
StringWithPragma.

Expand Down
1 change: 0 additions & 1 deletion test/erlfmt_SUITE_data/broken.erl.formatted
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
-module(broken).

-export([foo/0, bar/0]).

-define(PARENS, ()).

foo?PARENS %% comment
Expand Down
9 changes: 9 additions & 0 deletions test/erlfmt_format_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,15 @@ attribute(Config) when is_list(Config) ->
" foo/2, foo/3,\n"
" bar/2\n"
"]).\n"
),
?assertFormat(
"-type str() :: string().\n"
"\n"
"\n"
"-type int() :: integer().\n",
"-type str() :: string().\n"
"\n"
"-type int() :: integer().\n"
).

exportimport(Config) when is_list(Config) ->
Expand Down