Skip to content

Commit

Permalink
Add support for abbreviated command names
Browse files Browse the repository at this point in the history
This change makes it possible to type the beginning (the prefix) of a
command name and rebar will guess the full name of the command,
thereby saving the user precious keystrokes.  As long as the prefix
matches only one command, rebar runs that command, otherwise rebar
prints a list of candidate command names. The "-" character is
considered to be a word separator and the prefix matching is done per
word.

Example prefix matches:

    co       ==> compile
    cl       ==> clean
    create   ==> create
    create-a ==> create-app
    c-a      ==> create-app
    c-app    ==> create-app
  • Loading branch information
klajo authored and Tuncer Ayaz committed Jan 24, 2011
1 parent d1ff83a commit 6978504
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 12 deletions.
66 changes: 65 additions & 1 deletion src/rebar.erl
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ parse_args(Args) ->

%% Filter all the flags (i.e. strings of form key=value) from the
%% command line arguments. What's left will be the commands to run.
filter_flags(NonOptArgs, []);
unabbreviate_command_names(filter_flags(NonOptArgs, []));

{error, {Reason, Data}} ->
?ERROR("Error: ~s ~p~n~n", [Reason, Data]),
Expand Down Expand Up @@ -222,3 +222,67 @@ filter_flags([Item | Rest], Commands) ->
?CONSOLE("Ignoring command line argument: ~p\n", [Other]),
filter_flags(Rest, Commands)
end.

command_names() ->
["build-plt", "check-deps", "check-plt", "clean", "compile", "create",
"create-app", "create-node", "ct", "delete-deps", "dialyze", "doc",
"eunit", "generate", "get-deps", "help", "list-templates", "update-deps",
"version", "xref"].

unabbreviate_command_names([]) ->
[];
unabbreviate_command_names([Command | Commands]) ->
case get_command_name_candidates(Command) of
[] ->
%% let the rest of the code detect that the command doesn't exist
%% (this would perhaps be a good place to fail)
[Command | unabbreviate_command_names(Commands)];
[FullCommand] ->
[FullCommand | unabbreviate_command_names(Commands)];
Candidates ->
?ABORT("Found more than one match for abbreviated command name "
" '~s',~nplease be more specific. Possible candidates:~n"
" ~s~n",
[Command, string:join(Candidates, ", ")])
end.

get_command_name_candidates(Command) ->
%% Get the command names which match the given (abbreviated) command name.
%% * "c" matches commands like compile, clean and create-app
%% * "create" matches command create only, since it's unique
%% * "create-" matches commands starting with create-
%% * "c-a" matches create-app
%% * "create-a" matches create-app
%% * "c-app" matches create-app
Candidates = [Candidate || Candidate <- command_names(),
is_command_name_candidate(Command, Candidate)],
%% Is there a complete match? If so return only that, return a
%% list of candidates otherwise
case Candidates of
[Command] = Match -> Match;
_ -> Candidates
end.

is_command_name_candidate(Command, Candidate) ->
lists:prefix(Command, Candidate)
orelse is_command_name_sub_word_candidate(Command, Candidate).

is_command_name_sub_word_candidate(Command, Candidate) ->
%% Allow for parts of commands to be abbreviated, i.e. create-app
%% can be shortened to "create-a", "c-a" or "c-app" (but not
%% "create-" since that would be ambiguous).
CommandSubWords = re:split(Command, "-", [{return, list}]),
CandidateSubWords = re:split(Candidate, "-", [{return, list}]),
is_command_name_sub_word_candidate_aux(CommandSubWords, CandidateSubWords).

is_command_name_sub_word_candidate_aux([CmdSW | CmdSWs], [CandSW | CandSWs]) ->
case lists:prefix(CmdSW, CandSW) of
true ->
is_command_name_sub_word_candidate_aux(CmdSWs, CandSWs);
false ->
false
end;
is_command_name_sub_word_candidate_aux([], []) ->
true;
is_command_name_sub_word_candidate_aux(_CmdSWs, _CandSWs) ->
false.
21 changes: 10 additions & 11 deletions src/rebar_core.erl
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,21 @@
%% Public API
%% ===================================================================

run(["help"]) ->
rebar:help(),
ok;
run(["version"]) ->
%% Load application spec and display vsn and build time info
ok = application:load(rebar),
rebar:version(),
ok;
run(RawArgs) ->
%% Pre-load the rebar app so that we get default configuration
ok = application:load(rebar),

%% Parse out command line arguments -- what's left is a list of commands to
%% run
Commands = rebar:parse_args(RawArgs),
%% run -- and start running commands
run_aux(rebar:parse_args(RawArgs)).

run_aux(["help"]) ->
rebar:help(),
ok;
run_aux(["version"]) ->
%% Display vsn and build time info
rebar:version(),
ok;
run_aux(Commands) ->
%% Make sure crypto is running
ok = crypto:start(),

Expand Down

0 comments on commit 6978504

Please sign in to comment.