diff --git a/src/erlfmt.erl b/src/erlfmt.erl index 2daccc15..7d78ab57 100644 --- a/src/erlfmt.erl +++ b/src/erlfmt.erl @@ -30,7 +30,7 @@ -export_type([error_info/0, config/0, pragma/0]). -type error_info() :: {file:name_all(), erl_anno:location(), module(), Reason :: any()}. --type pragma() :: require | insert | ignore. +-type pragma() :: require | insert | delete | ignore. -type config() :: [{pragma, pragma()} | {print_width, pos_integer()} | verbose]. -define(DEFAULT_WIDTH, 100). @@ -65,14 +65,15 @@ format_file(FileName, Options) -> Pragma = proplists:get_value(pragma, Options, ignore), try case file_read_nodes(FileName, Pragma) of - {ok, Nodes, Warnings} -> - NodesWithPragma = + {ok, Nodes0, Warnings} -> + Nodes = case Pragma of - insert -> insert_pragma_nodes(Nodes); - _ -> Nodes + insert -> insert_pragma_nodes(Nodes0); + delete -> remove_pragma_nodes(Nodes0); + _ -> Nodes0 end, - Formatted = format_nodes(NodesWithPragma, PrintWidth), - verify_nodes(FileName, NodesWithPragma, Formatted), + Formatted = format_nodes(Nodes, PrintWidth), + verify_nodes(FileName, Nodes, Formatted), VerboseWarnings = case proplists:get_bool(verbose, Options) of true -> check_line_lengths(FileName, PrintWidth, Formatted); @@ -93,14 +94,15 @@ format_string(String, Options) -> Pragma = proplists:get_value(pragma, Options, ignore), try case read_nodes_string("nofile", String, Pragma) of - {ok, Nodes, Warnings} -> - NodesWithPragma = + {ok, Nodes0, Warnings} -> + Nodes = case Pragma of - insert -> insert_pragma_nodes(Nodes); - _ -> Nodes + insert -> insert_pragma_nodes(Nodes0); + delete -> remove_pragma_nodes(Nodes0); + _ -> Nodes0 end, - Formatted = format_nodes(NodesWithPragma, PrintWidth), - verify_nodes("nofile", NodesWithPragma, Formatted), + Formatted = format_nodes(Nodes, PrintWidth), + verify_nodes("nofile", Nodes, Formatted), VerboseWarnings = case proplists:get_bool(verbose, Options) of true -> check_line_lengths("nofile", PrintWidth, Formatted); @@ -150,6 +152,42 @@ insert_pragma_node(Node) -> end, erlfmt_scan:put_anno(pre_comments, NewPreComments, Node). +remove_pragma_nodes([]) -> + []; +remove_pragma_nodes([{shebang, _, _} = Node | Nodes]) -> + case contains_pragma_node(Node) of + true -> [remove_pragma_node(Node) | Nodes]; + false -> [Node | remove_pragma_nodes(Nodes)] + end; +remove_pragma_nodes([Node | Nodes]) -> + case contains_pragma_node(Node) of + true -> [remove_pragma_node(Node) | Nodes]; + false -> [Node | Nodes] + end. + +remove_pragma_node(Node0) -> + {PreComments0, _, PostComments0} = erlfmt_format:comments_with_pre_dot(Node0), + PreComments = remove_pragma_comment_blocks(PreComments0), + PostComments = remove_pragma_comment_blocks(PostComments0), + Node = erlfmt_scan:put_anno(pre_comments, PreComments, Node0), + erlfmt_scan:put_anno(post_comments, PostComments, Node). + +remove_pragma_comment_blocks([]) -> + []; +remove_pragma_comment_blocks([{comment, Loc, Comments} | Rest]) -> + case remove_pragma_comment_block(Comments) of + [] -> remove_pragma_comment_block(Rest); + CleanComments -> [{comment, Loc, CleanComments} | remove_pragma_comment_block(Rest)] + end. + +remove_pragma_comment_block([]) -> + []; +remove_pragma_comment_block([Head | Tail]) -> + case string:find(Head, "@format") of + nomatch -> [Head | remove_pragma_comment_block(Tail)]; + _ -> Tail + end. + -spec format_range( file:name_all(), erlfmt_scan:location(), @@ -238,7 +276,7 @@ read_nodes({ok, Tokens, Comments, Cont}, FileName, Pragma, [], Warnings0, TextAc Warnings, TextAcc ++ LastString ); - {require, false, _} -> + {_, false, _} when Pragma =:= require; Pragma =:= delete -> {LastString, _Anno} = erlfmt_scan:last_node_string(Cont), case erlfmt_scan:read_rest(Cont) of {ok, Rest} -> @@ -264,7 +302,7 @@ read_nodes( ) -> {Node, Warnings} = parse_node(Tokens, Comments, FileName, Cont, Warnings0), case {Pragma, contains_pragma_node(Node)} of - {require, false} -> + {_, false} when Pragma =:= require; Pragma =:= delete -> {LastString, _Anno} = erlfmt_scan:last_node_string(Cont), case erlfmt_scan:read_rest(Cont) of {ok, Rest} -> diff --git a/src/erlfmt_cli.erl b/src/erlfmt_cli.erl index dab48e26..a8405529 100644 --- a/src/erlfmt_cli.erl +++ b/src/erlfmt_cli.erl @@ -47,6 +47,9 @@ opts() -> "Insert a @format pragma to the top of formatted files when pragma is absent. " "Works well when used in tandem with --require-pragma, " "but it is not allowed to use require-pragma and insert-pragma at the same time."}, + {delete_pragma, undefined, "delete-pragma", undefined, + "Deletes the @format pragma at the top of formatted files. " + "It will also reformat the file, but is only applied to files with a pragma, see --require-pragma."}, {exclude_files, undefined, "exclude-files", string, "files not to format. " "This overrides the files specified to format"}, @@ -292,12 +295,22 @@ parse_opts([{print_width, Value} | Rest], Files, Exclude, Config) -> parse_opts(Rest, Files, Exclude, Config#config{print_width = Value}); parse_opts([require_pragma | _Rest], _Files, _Exclude, #config{pragma = insert}) -> {error, "Cannot use both --insert-pragma and --require-pragma options together."}; +parse_opts([require_pragma | _Rest], _Files, _Exclude, #config{pragma = delete}) -> + {error, "Cannot use both --delete-pragma and --require-pragma options together."}; parse_opts([require_pragma | Rest], Files, Exclude, Config) -> parse_opts(Rest, Files, Exclude, Config#config{pragma = require}); parse_opts([insert_pragma | _Rest], _Files, _Exclude, #config{pragma = require}) -> {error, "Cannot use both --insert-pragma and --require-pragma options together."}; +parse_opts([insert_pragma | _Rest], _Files, _Exclude, #config{pragma = delete}) -> + {error, "Cannot use both --insert-pragma and --delete-pragma options together."}; parse_opts([insert_pragma | Rest], Files, Exclude, Config) -> parse_opts(Rest, Files, Exclude, Config#config{pragma = insert}); +parse_opts([delete_pragma | _Rest], _Files, _Exclude, #config{pragma = insert}) -> + {error, "Cannot use both --insert-pragma and --delete-pragma options together."}; +parse_opts([delete_pragma | _Rest], _Files, _Exclude, #config{pragma = require}) -> + {error, "Cannot use both --require-pragma and --delete-pragma options together."}; +parse_opts([delete_pragma | Rest], Files, Exclude, Config) -> + parse_opts(Rest, Files, Exclude, Config#config{pragma = delete}); parse_opts([{files, NewFiles} | Rest], Files, Exclude, Config) -> parse_opts(Rest, expand_files(NewFiles, Files), Exclude, Config); parse_opts([{exclude_files, NewExcludes} | Rest], Files, Exclude, Config) -> diff --git a/test/erlfmt_SUITE.erl b/test/erlfmt_SUITE.erl index fe3b6a09..9028ff35 100644 --- a/test/erlfmt_SUITE.erl +++ b/test/erlfmt_SUITE.erl @@ -55,6 +55,9 @@ smoke_test_stdio_with_pragma/1, smoke_test_stdio_insert_pragma_without/1, smoke_test_stdio_insert_and_require_pragma/1, + smoke_test_stdio_delete_pragma/1, + smoke_test_stdio_delete_pragma_without/1, + smoke_test_stdio_delete_pragma_with_copyright/1, smoke_test_stdio_unicode/1, smoke_test_stdio_check/1, exclude_check/1, @@ -138,6 +141,9 @@ groups() -> smoke_test_stdio_with_pragma, smoke_test_stdio_insert_pragma_without, smoke_test_stdio_insert_and_require_pragma, + smoke_test_stdio_delete_pragma, + smoke_test_stdio_delete_pragma_without, + smoke_test_stdio_delete_pragma_with_copyright, smoke_test_stdio_unicode, smoke_test_stdio_check, exclude_check @@ -1013,6 +1019,31 @@ smoke_test_stdio_insert_pragma_without(Config) when is_list(Config) -> "-module(nopragma).\n", ?assertEqual(Expected, Formatted). +smoke_test_stdio_delete_pragma(Config) when is_list(Config) -> + Formatted = os:cmd( + "echo '%% @format\n\n-module(nopragma).' | " ++ escript() ++ " - --delete-pragma" + ), + Expected = + "-module(nopragma).\n", + ?assertEqual(Expected, Formatted). + +smoke_test_stdio_delete_pragma_without(Config) when is_list(Config) -> + Formatted = os:cmd("echo '-module(nopragma).' | " ++ escript() ++ " - --delete-pragma"), + Expected = + "-module(nopragma).\n", + ?assertEqual(Expected, Formatted). + +smoke_test_stdio_delete_pragma_with_copyright(Config) when is_list(Config) -> + Formatted = os:cmd( + "echo '%% @format\n%% copyright\n\n-module(nopragma).' | " ++ escript() ++ + " - --delete-pragma" + ), + Expected = + "%% copyright\n" + "\n" + "-module(nopragma).\n", + ?assertEqual(Expected, Formatted). + smoke_test_stdio_insert_and_require_pragma(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), Path = filename:join(DataDir, "pragma.erl"),