Skip to content

Commit b221b5a

Browse files
pcloudsgitster
authored andcommitted
completion: collapse extra --no-.. options
The commands that make use of --git-completion-helper feature could now produce a lot of --no-xxx options that a command can take. This in many case could nearly double the amount of completable options, using more screen estate and also harder to search for the wanted option. This patch attempts to mitigate that by collapsing extra --no- options, the ones that are added by --git-completion-helper and not in original struct option arrays. The "--no-..." option will be displayed in this case to hint about more options, e.g. > ~/w/git $ git clone -- --bare --origin= --branch= --progress --checkout --quiet --config= --recurse-submodules --depth= --reference= --dissociate --reference-if-able= --filter= --separate-git-dir= --hardlinks --shallow-exclude= --ipv4 --shallow-since= --ipv6 --shallow-submodules --jobs= --shared --local --single-branch --mirror --tags --no-... --template= --no-checkout --upload-pack= --no-hardlinks --verbose --no-tags and when you complete it with --no-<tab>, all negative options will be presented: > ~/w/git $ git clone --no- --no-bare --no-quiet --no-branch --no-recurse-submodules --no-checkout --no-reference --no-config --no-reference-if-able --no-depth --no-separate-git-dir --no-dissociate --no-shallow-exclude --no-filter --no-shallow-since --no-hardlinks --no-shallow-submodules --no-ipv4 --no-shared --no-ipv6 --no-single-branch --no-jobs --no-tags --no-local --no-template --no-mirror --no-upload-pack --no-origin --no-verbose --no-progress Corner case: to make sure that people will never accidentally complete the fake option "--no-..." there must be one real --no- in the first complete listing even if it's not from the original struct option. PS. This could could be made simpler with ";&" to fall through from "--no-*" block and share the code but ";&" is not available on bash-3 (i.e. Mac) Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3fe735e commit b221b5a

File tree

3 files changed

+115
-29
lines changed

3 files changed

+115
-29
lines changed

contrib/completion/git-completion.bash

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,32 @@ __gitcomp ()
266266
case "$cur_" in
267267
--*=)
268268
;;
269+
--no-*)
270+
local c i=0 IFS=$' \t\n'
271+
for c in $1; do
272+
if [[ $c == "--" ]]; then
273+
continue
274+
fi
275+
c="$c${4-}"
276+
if [[ $c == "$cur_"* ]]; then
277+
case $c in
278+
--*=*|*.) ;;
279+
*) c="$c " ;;
280+
esac
281+
COMPREPLY[i++]="${2-}$c"
282+
fi
283+
done
284+
;;
269285
*)
270286
local c i=0 IFS=$' \t\n'
271287
for c in $1; do
288+
if [[ $c == "--" ]]; then
289+
c="--no-...${4-}"
290+
if [[ $c == "$cur_"* ]]; then
291+
COMPREPLY[i++]="${2-}$c "
292+
fi
293+
break
294+
fi
272295
c="$c${4-}"
273296
if [[ $c == "$cur_"* ]]; then
274297
case $c in

parse-options.c

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -427,12 +427,61 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
427427
parse_options_check(options);
428428
}
429429

430+
static void show_negated_gitcomp(const struct option *opts, int nr_noopts)
431+
{
432+
int printed_dashdash = 0;
433+
434+
for (; opts->type != OPTION_END; opts++) {
435+
int has_unset_form = 0;
436+
const char *name;
437+
438+
if (!opts->long_name)
439+
continue;
440+
if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
441+
continue;
442+
if (opts->flags & PARSE_OPT_NONEG)
443+
continue;
444+
445+
switch (opts->type) {
446+
case OPTION_STRING:
447+
case OPTION_FILENAME:
448+
case OPTION_INTEGER:
449+
case OPTION_MAGNITUDE:
450+
case OPTION_CALLBACK:
451+
case OPTION_BIT:
452+
case OPTION_NEGBIT:
453+
case OPTION_COUNTUP:
454+
case OPTION_SET_INT:
455+
has_unset_form = 1;
456+
break;
457+
default:
458+
break;
459+
}
460+
if (!has_unset_form)
461+
continue;
462+
463+
if (skip_prefix(opts->long_name, "no-", &name)) {
464+
if (nr_noopts < 0)
465+
printf(" --%s", name);
466+
} else if (nr_noopts >= 0) {
467+
if (nr_noopts && !printed_dashdash) {
468+
printf(" --");
469+
printed_dashdash = 1;
470+
}
471+
printf(" --no-%s", opts->long_name);
472+
nr_noopts++;
473+
}
474+
}
475+
}
476+
430477
static int show_gitcomp(struct parse_opt_ctx_t *ctx,
431478
const struct option *opts)
432479
{
480+
const struct option *original_opts = opts;
481+
int nr_noopts = 0;
482+
433483
for (; opts->type != OPTION_END; opts++) {
434484
const char *suffix = "";
435-
int has_unset_form = 0;
436485

437486
if (!opts->long_name)
438487
continue;
@@ -447,8 +496,6 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx,
447496
case OPTION_INTEGER:
448497
case OPTION_MAGNITUDE:
449498
case OPTION_CALLBACK:
450-
has_unset_form = 1;
451-
452499
if (opts->flags & PARSE_OPT_NOARG)
453500
break;
454501
if (opts->flags & PARSE_OPT_OPTARG)
@@ -457,28 +504,17 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx,
457504
break;
458505
suffix = "=";
459506
break;
460-
case OPTION_BIT:
461-
case OPTION_NEGBIT:
462-
case OPTION_COUNTUP:
463-
case OPTION_SET_INT:
464-
has_unset_form = 1;
465-
break;
466507
default:
467508
break;
468509
}
469510
if (opts->flags & PARSE_OPT_COMP_ARG)
470511
suffix = "=";
512+
if (starts_with(opts->long_name, "no-"))
513+
nr_noopts++;
471514
printf(" --%s%s", opts->long_name, suffix);
472-
473-
if (has_unset_form && !(opts->flags & PARSE_OPT_NONEG)) {
474-
const char *name;
475-
476-
if (skip_prefix(opts->long_name, "no-", &name))
477-
printf(" --%s", name);
478-
else
479-
printf(" --no-%s", opts->long_name);
480-
}
481515
}
516+
show_negated_gitcomp(original_opts, -1);
517+
show_negated_gitcomp(original_opts, nr_noopts);
482518
fputc('\n', stdout);
483519
exit(0);
484520
}

t/t9902-completion.sh

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,42 @@ test_expect_success '__gitcomp - suffix' '
459459
EOF
460460
'
461461

462+
test_expect_success '__gitcomp - ignore optional negative options' '
463+
test_gitcomp "--" "--abc --def --no-one -- --no-two" <<-\EOF
464+
--abc Z
465+
--def Z
466+
--no-one Z
467+
--no-... Z
468+
EOF
469+
'
470+
471+
test_expect_success '__gitcomp - ignore/narrow optional negative options' '
472+
test_gitcomp "--a" "--abc --abcdef --no-one -- --no-two" <<-\EOF
473+
--abc Z
474+
--abcdef Z
475+
EOF
476+
'
477+
478+
test_expect_success '__gitcomp - ignore/narrow optional negative options' '
479+
test_gitcomp "--n" "--abc --def --no-one -- --no-two" <<-\EOF
480+
--no-one Z
481+
--no-... Z
482+
EOF
483+
'
484+
485+
test_expect_success '__gitcomp - expand all negative options' '
486+
test_gitcomp "--no-" "--abc --def --no-one -- --no-two" <<-\EOF
487+
--no-one Z
488+
--no-two Z
489+
EOF
490+
'
491+
492+
test_expect_success '__gitcomp - expand/narrow all negative options' '
493+
test_gitcomp "--no-o" "--abc --def --no-one -- --no-two" <<-\EOF
494+
--no-one Z
495+
EOF
496+
'
497+
462498
test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
463499
__gitcomp "$invalid_variable_name"
464500
'
@@ -1237,29 +1273,20 @@ test_expect_success 'double dash "git" itself' '
12371273
test_expect_success 'double dash "git checkout"' '
12381274
test_completion "git checkout --" <<-\EOF
12391275
--quiet Z
1240-
--no-quiet Z
12411276
--detach Z
1242-
--no-detach Z
12431277
--track Z
1244-
--no-track Z
12451278
--orphan=Z
1246-
--no-orphan Z
12471279
--ours Z
12481280
--theirs Z
12491281
--merge Z
1250-
--no-merge Z
12511282
--conflict=Z
1252-
--no-conflict Z
12531283
--patch Z
1254-
--no-patch Z
12551284
--ignore-skip-worktree-bits Z
1256-
--no-ignore-skip-worktree-bits Z
12571285
--ignore-other-worktrees Z
1258-
--no-ignore-other-worktrees Z
12591286
--recurse-submodules Z
1260-
--no-recurse-submodules Z
12611287
--progress Z
1262-
--no-progress Z
1288+
--no-quiet Z
1289+
--no-... Z
12631290
EOF
12641291
'
12651292

0 commit comments

Comments
 (0)