Current section
Files
Jump to
Current section
Files
src/erlfmt_cli.erl
%% Copyright (c) Facebook, Inc. and its affiliates.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(erlfmt_cli).
-export([opts/0, do/2]).
-record(config, {
verbose = false :: boolean(),
pragma = ignore :: erlfmt:pragma(),
out = standard_out :: erlfmt:out()
}).
-spec opts() -> [getopt:option_spec()].
opts() ->
[
{help, $h, "help", undefined, "print this message"},
{version, $v, "version", undefined, "print version"},
{write, $w, "write", undefined, "modify formatted files in place"},
{out, $o, "out", binary, "output directory"},
{verbose, undefined, "verbose", undefined, "include debug output"},
{require_pragma, undefined, "require-pragma", undefined,
"Require a special comment @format, called a pragma, "
"to be present in the file's first docblock comment in order for prettier to format it."},
{insert_pragma, undefined, "insert-pragma", undefined,
"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."},
{files, undefined, undefined, string, "files to format, - for stdin"}
].
-spec do(list(), string()) -> ok.
do(Opts, Name) ->
try do_unprotected(Opts, Name)
catch
Kind:Reason:Stack ->
io:format(standard_error, "~s Internal Error~n~s:~p~n~p~n", [Name, Kind, Reason, Stack]),
erlang:halt(127)
end.
-spec do_unprotected(list(), string()) -> ok.
do_unprotected(Opts, Name) ->
case parse_opts(Opts, Name, [], #config{}) of
{format, Files, Config} ->
case format_files(Files, Config, _HadErrors = false) of
true -> erlang:halt(4);
false -> ok
end;
{error, Message} ->
io:put_chars(standard_error, [Message, "\n\n"]),
getopt:usage(opts(), Name),
erlang:halt(2)
end.
format_files([FileName | FileNames], Config, HadErrors) ->
case Config#config.verbose of
true -> io:format(standard_error, "Formatting ~s\n", [FileName]);
false -> ok
end,
ErlfmtConfig = {Config#config.pragma, Config#config.out},
case erlfmt:format_file(FileName, ErlfmtConfig) of
{ok, Warnings} ->
[print_error_info(Warning) || Warning <- Warnings],
format_files(FileNames, Config, HadErrors);
skip when Config#config.verbose ->
io:format(standard_error, "Skipping ~s because of missing @format pragma\n", [FileName]),
format_files(FileNames, Config, HadErrors);
skip ->
format_files(FileNames, Config, HadErrors);
{error, Error} ->
print_error_info(Error),
format_files(FileNames, Config, true)
end;
format_files([], _Config, HadErrors) ->
HadErrors.
parse_opts([help | _Rest], Name, _Files, _Config) ->
getopt:usage(opts(), Name),
erlang:halt(0);
parse_opts([version | _Rest], Name, _Files, _Config) ->
{ok, Vsn} = application:get_key(erlfmt, vsn),
io:format("~s version ~s\n", [Name, Vsn]),
erlang:halt(0);
parse_opts([write | Rest], Name, Files, Config) ->
parse_opts(Rest, Name, Files, Config#config{out = replace});
parse_opts([{out, Path} | Rest], Name, Files, Config) ->
parse_opts(Rest, Name, Files, Config#config{out = {path, Path}});
parse_opts([verbose | Rest], Name, Files, Config) ->
parse_opts(Rest, Name, Files, Config#config{verbose = true});
parse_opts([require_pragma | _Rest], _Name, _Files, #config{pragma = insert}) ->
{error, "Cannot use both --insert-pragma and --require-pragma options together."};
parse_opts([require_pragma | Rest], Name, Files, Config) ->
parse_opts(Rest, Name, Files, Config#config{pragma = require});
parse_opts([insert_pragma | _Rest], _Name, _Files, #config{pragma = require}) ->
{error, "Cannot use both --insert-pragma and --require-pragma options together."};
parse_opts([insert_pragma | Rest], Name, Files, Config) ->
parse_opts(Rest, Name, Files, Config#config{pragma = insert});
parse_opts([{files, NewFiles} | Rest], Name, Files0, Config) ->
parse_opts(Rest, Name, expand_files(NewFiles, Files0), Config);
parse_opts([], _Name, [stdin], #config{out = Out}) when Out =/= standard_out ->
{error, "stdin mode can't be combined with out or write options"};
parse_opts([], _Name, [stdin], Config) ->
{format, [stdin], Config};
parse_opts([], _Name, [], _Config) ->
{error, "no files provided to format"};
parse_opts([], _Name, Files, Config) ->
case lists:member(stdin, Files) of
true -> {error, "stdin mode can't be combined with other files"};
false -> {format, lists:reverse(Files), Config}
end.
expand_files("-", Files) ->
[stdin | Files];
expand_files(NewFile, Files) when is_integer(hd(NewFile)) ->
case filelib:is_regular(NewFile) of
true ->
[NewFile | Files];
false ->
case filelib:wildcard(NewFile) of
[] ->
io:format(standard_error, "no file matching '~s'", [NewFile]),
Files;
NewFiles ->
NewFiles ++ Files
end
end;
expand_files(NewFiles, Files) when is_list(NewFiles) ->
lists:foldl(fun expand_files/2, Files, NewFiles).
print_error_info(Info) ->
io:put_chars(standard_error, [erlfmt:format_error_info(Info), $\n]).