diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 952e660f06..425d06256f 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -266,9 +266,32 @@ __gitcomp () 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' 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-}" if [[ $c == "$cur_"* ]]; then case $c in diff --git a/parse-options.c b/parse-options.c index b86612148f..7db84227ab 100644 --- a/parse-options.c +++ b/parse-options.c @@ -427,12 +427,61 @@ void parse_options_start(struct parse_opt_ctx_t *ctx, parse_options_check(options); } +static void show_negated_gitcomp(const struct option *opts, int nr_noopts) +{ + 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, const struct option *opts) { + const struct option *original_opts = opts; + int nr_noopts = 0; + for (; opts->type != OPTION_END; opts++) { const char *suffix = ""; - int has_unset_form = 0; if (!opts->long_name) continue; @@ -447,8 +496,6 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx, case OPTION_INTEGER: case OPTION_MAGNITUDE: case OPTION_CALLBACK: - has_unset_form = 1; - if (opts->flags & PARSE_OPT_NOARG) break; if (opts->flags & PARSE_OPT_OPTARG) @@ -457,28 +504,17 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx, break; suffix = "="; break; - case OPTION_BIT: - case OPTION_NEGBIT: - case OPTION_COUNTUP: - case OPTION_SET_INT: - has_unset_form = 1; - break; default: break; } if (opts->flags & PARSE_OPT_COMP_ARG) suffix = "="; + if (starts_with(opts->long_name, "no-")) + nr_noopts++; printf(" --%s%s", opts->long_name, suffix); - - if (has_unset_form && !(opts->flags & PARSE_OPT_NONEG)) { - const char *name; - - if (skip_prefix(opts->long_name, "no-", &name)) - printf(" --%s", name); - else - printf(" --no-%s", opts->long_name); - } } + show_negated_gitcomp(original_opts, -1); + show_negated_gitcomp(original_opts, nr_noopts); fputc('\n', stdout); exit(0); } diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 7e5e3ad5b1..157ee7085d 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -459,6 +459,42 @@ test_expect_success '__gitcomp - suffix' ' 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' ' __gitcomp "$invalid_variable_name" ' @@ -1237,29 +1273,20 @@ test_expect_success 'double dash "git" itself' ' test_expect_success 'double dash "git checkout"' ' test_completion "git checkout --" <<-\EOF --quiet Z - --no-quiet Z --detach Z - --no-detach Z --track Z - --no-track Z --orphan=Z - --no-orphan Z --ours Z --theirs Z --merge Z - --no-merge Z --conflict=Z - --no-conflict Z --patch Z - --no-patch Z --ignore-skip-worktree-bits Z - --no-ignore-skip-worktree-bits Z --ignore-other-worktrees Z - --no-ignore-other-worktrees Z --recurse-submodules Z - --no-recurse-submodules Z --progress Z - --no-progress Z + --no-quiet Z + --no-... Z EOF '