Merge branch 'nd/completion-negation'

Continuing with the idea to programmatically enumerate various
pieces of data required for command line completion, the codebase
has been taught to enumerate options prefixed with "--no-" to
negate them.

* nd/completion-negation:
  completion: collapse extra --no-.. options
  completion: suppress some -no- options
  parse-options: option to let --git-completion-helper show negative form
This commit is contained in:
Junio C Hamano 2018-06-28 12:53:32 -07:00
commit 6da2d95951
4 changed files with 136 additions and 34 deletions

View File

@ -1120,10 +1120,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"), OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"),
BRANCH_TRACK_EXPLICIT), BRANCH_TRACK_EXPLICIT),
OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")), OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"), OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
2), N_("checkout our version for unmerged files"),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"), 2, PARSE_OPT_NONEG),
3), OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
N_("checkout their version for unmerged files"),
3, PARSE_OPT_NONEG),
OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"), OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
PARSE_OPT_NOCOMPLETE), PARSE_OPT_NOCOMPLETE),
OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),

View File

@ -330,9 +330,32 @@ __gitcomp ()
case "$cur_" in case "$cur_" in
--*=) --*=)
;; ;;
--no-*)
local c i=0 IFS=$' \t\n'
for c in $1; do
if [[ $c == "--" ]]; then
continue
fi
c="$c${4-}"
if [[ $c == "$cur_"* ]]; then
case $c in
--*=*|*.) ;;
*) c="$c " ;;
esac
COMPREPLY[i++]="${2-}$c"
fi
done
;;
*) *)
local c i=0 IFS=$' \t\n' local c i=0 IFS=$' \t\n'
for c in $1; do for c in $1; do
if [[ $c == "--" ]]; then
c="--no-...${4-}"
if [[ $c == "$cur_"* ]]; then
COMPREPLY[i++]="${2-}$c "
fi
break
fi
c="$c${4-}" c="$c${4-}"
if [[ $c == "$cur_"* ]]; then if [[ $c == "$cur_"* ]]; then
case $c in case $c in
@ -1161,7 +1184,7 @@ _git_am ()
return return
;; ;;
--*) --*)
__gitcomp_builtin am "--no-utf8" \ __gitcomp_builtin am "" \
"$__git_am_inprogress_options" "$__git_am_inprogress_options"
return return
esac esac
@ -1261,9 +1284,7 @@ _git_branch ()
__git_complete_refs --cur="${cur##--set-upstream-to=}" __git_complete_refs --cur="${cur##--set-upstream-to=}"
;; ;;
--*) --*)
__gitcomp_builtin branch "--no-color --no-abbrev __gitcomp_builtin branch
--no-track --no-column
"
;; ;;
*) *)
if [ $only_local_ref = "y" -a $has_r = "n" ]; then if [ $only_local_ref = "y" -a $has_r = "n" ]; then
@ -1304,7 +1325,7 @@ _git_checkout ()
__gitcomp "diff3 merge" "" "${cur##--conflict=}" __gitcomp "diff3 merge" "" "${cur##--conflict=}"
;; ;;
--*) --*)
__gitcomp_builtin checkout "--no-track --no-recurse-submodules" __gitcomp_builtin checkout
;; ;;
*) *)
# check if --track, --no-track, or --no-guess was specified # check if --track, --no-track, or --no-guess was specified
@ -1367,7 +1388,7 @@ _git_clone ()
{ {
case "$cur" in case "$cur" in
--*) --*)
__gitcomp_builtin clone "--no-single-branch" __gitcomp_builtin clone
return return
;; ;;
esac esac
@ -1400,7 +1421,7 @@ _git_commit ()
return return
;; ;;
--*) --*)
__gitcomp_builtin commit "--no-edit --verify" __gitcomp_builtin commit
return return
esac esac
@ -1503,7 +1524,7 @@ _git_fetch ()
return return
;; ;;
--*) --*)
__gitcomp_builtin fetch "--no-tags" __gitcomp_builtin fetch
return return
;; ;;
esac esac
@ -1540,7 +1561,7 @@ _git_fsck ()
{ {
case "$cur" in case "$cur" in
--*) --*)
__gitcomp_builtin fsck "--no-reflogs" __gitcomp_builtin fsck
return return
;; ;;
esac esac
@ -1646,7 +1667,7 @@ _git_ls_files ()
{ {
case "$cur" in case "$cur" in
--*) --*)
__gitcomp_builtin ls-files "--no-empty-directory" __gitcomp_builtin ls-files
return return
;; ;;
esac esac
@ -1797,12 +1818,7 @@ _git_merge ()
case "$cur" in case "$cur" in
--*) --*)
__gitcomp_builtin merge "--no-rerere-autoupdate __gitcomp_builtin merge
--no-commit --no-edit --no-ff
--no-log --no-progress
--no-squash --no-stat
--no-verify-signatures
"
return return
esac esac
__git_complete_refs __git_complete_refs
@ -1901,10 +1917,7 @@ _git_pull ()
return return
;; ;;
--*) --*)
__gitcomp_builtin pull "--no-autostash --no-commit --no-edit __gitcomp_builtin pull
--no-ff --no-log --no-progress --no-rebase
--no-squash --no-stat --no-tags
--no-verify-signatures"
return return
;; ;;
@ -2095,7 +2108,7 @@ _git_status ()
return return
;; ;;
--*) --*)
__gitcomp_builtin status "--no-column" __gitcomp_builtin status
return return
;; ;;
esac esac
@ -2345,7 +2358,7 @@ _git_remote ()
case "$subcommand,$cur" in case "$subcommand,$cur" in
add,--*) add,--*)
__gitcomp_builtin remote_add "--no-tags" __gitcomp_builtin remote_add
;; ;;
add,*) add,*)
;; ;;
@ -2425,7 +2438,7 @@ _git_revert ()
fi fi
case "$cur" in case "$cur" in
--*) --*)
__gitcomp_builtin revert "--no-edit" \ __gitcomp_builtin revert "" \
"$__git_revert_inprogress_options" "$__git_revert_inprogress_options"
return return
;; ;;
@ -2495,7 +2508,7 @@ _git_show_branch ()
{ {
case "$cur" in case "$cur" in
--*) --*)
__gitcomp_builtin show-branch "--no-color" __gitcomp_builtin show-branch
return return
;; ;;
esac esac

View File

@ -427,13 +427,59 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
parse_options_check(options); parse_options_check(options);
} }
/* static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
* TODO: we are not completing the --no-XXX form yet because there are {
* many options that do not suppress it properly. int printed_dashdash = 0;
*/
for (; opts->type != OPTION_END; opts++) {
int has_unset_form = 0;
const char *name;
if (!opts->long_name)
continue;
if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
continue;
if (opts->flags & PARSE_OPT_NONEG)
continue;
switch (opts->type) {
case OPTION_STRING:
case OPTION_FILENAME:
case OPTION_INTEGER:
case OPTION_MAGNITUDE:
case OPTION_CALLBACK:
case OPTION_BIT:
case OPTION_NEGBIT:
case OPTION_COUNTUP:
case OPTION_SET_INT:
has_unset_form = 1;
break;
default:
break;
}
if (!has_unset_form)
continue;
if (skip_prefix(opts->long_name, "no-", &name)) {
if (nr_noopts < 0)
printf(" --%s", name);
} else if (nr_noopts >= 0) {
if (nr_noopts && !printed_dashdash) {
printf(" --");
printed_dashdash = 1;
}
printf(" --no-%s", opts->long_name);
nr_noopts++;
}
}
}
static int show_gitcomp(struct parse_opt_ctx_t *ctx, static int show_gitcomp(struct parse_opt_ctx_t *ctx,
const struct option *opts) const struct option *opts)
{ {
const struct option *original_opts = opts;
int nr_noopts = 0;
for (; opts->type != OPTION_END; opts++) { for (; opts->type != OPTION_END; opts++) {
const char *suffix = ""; const char *suffix = "";
@ -463,8 +509,12 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx,
} }
if (opts->flags & PARSE_OPT_COMP_ARG) if (opts->flags & PARSE_OPT_COMP_ARG)
suffix = "="; suffix = "=";
if (starts_with(opts->long_name, "no-"))
nr_noopts++;
printf(" --%s%s", opts->long_name, suffix); printf(" --%s%s", opts->long_name, suffix);
} }
show_negated_gitcomp(original_opts, -1);
show_negated_gitcomp(original_opts, nr_noopts);
fputc('\n', stdout); fputc('\n', stdout);
exit(0); exit(0);
} }

View File

@ -501,6 +501,42 @@ test_expect_success '__gitcomp - suffix' '
EOF EOF
' '
test_expect_success '__gitcomp - ignore optional negative options' '
test_gitcomp "--" "--abc --def --no-one -- --no-two" <<-\EOF
--abc Z
--def Z
--no-one Z
--no-... Z
EOF
'
test_expect_success '__gitcomp - ignore/narrow optional negative options' '
test_gitcomp "--a" "--abc --abcdef --no-one -- --no-two" <<-\EOF
--abc Z
--abcdef Z
EOF
'
test_expect_success '__gitcomp - ignore/narrow optional negative options' '
test_gitcomp "--n" "--abc --def --no-one -- --no-two" <<-\EOF
--no-one Z
--no-... Z
EOF
'
test_expect_success '__gitcomp - expand all negative options' '
test_gitcomp "--no-" "--abc --def --no-one -- --no-two" <<-\EOF
--no-one Z
--no-two Z
EOF
'
test_expect_success '__gitcomp - expand/narrow all negative options' '
test_gitcomp "--no-o" "--abc --def --no-one -- --no-two" <<-\EOF
--no-one Z
EOF
'
test_expect_success '__gitcomp - doesnt fail because of invalid variable name' ' test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name" __gitcomp "$invalid_variable_name"
' '
@ -1398,8 +1434,8 @@ test_expect_success 'double dash "git checkout"' '
--ignore-other-worktrees Z --ignore-other-worktrees Z
--recurse-submodules Z --recurse-submodules Z
--progress Z --progress Z
--no-track Z --no-quiet Z
--no-recurse-submodules Z --no-... Z
EOF EOF
' '
@ -1607,6 +1643,7 @@ test_expect_success 'completion used <cmd> completion for alias: !f() { : git <c
test_expect_success 'completion without explicit _git_xxx function' ' test_expect_success 'completion without explicit _git_xxx function' '
test_completion "git version --" <<-\EOF test_completion "git version --" <<-\EOF
--build-options Z --build-options Z
--no-build-options Z
EOF EOF
' '