Skip to content

Commit 6da2d95

Browse files
committed
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
2 parents 5eb8da8 + b221b5a commit 6da2d95

File tree

4 files changed

+136
-34
lines changed

4 files changed

+136
-34
lines changed

builtin/checkout.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,10 +1120,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
11201120
OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"),
11211121
BRANCH_TRACK_EXPLICIT),
11221122
OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
1123-
OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"),
1124-
2),
1125-
OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
1126-
3),
1123+
OPT_SET_INT_F('2', "ours", &opts.writeout_stage,
1124+
N_("checkout our version for unmerged files"),
1125+
2, PARSE_OPT_NONEG),
1126+
OPT_SET_INT_F('3', "theirs", &opts.writeout_stage,
1127+
N_("checkout their version for unmerged files"),
1128+
3, PARSE_OPT_NONEG),
11271129
OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
11281130
PARSE_OPT_NOCOMPLETE),
11291131
OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),

contrib/completion/git-completion.bash

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,32 @@ __gitcomp ()
330330
case "$cur_" in
331331
--*=)
332332
;;
333+
--no-*)
334+
local c i=0 IFS=$' \t\n'
335+
for c in $1; do
336+
if [[ $c == "--" ]]; then
337+
continue
338+
fi
339+
c="$c${4-}"
340+
if [[ $c == "$cur_"* ]]; then
341+
case $c in
342+
--*=*|*.) ;;
343+
*) c="$c " ;;
344+
esac
345+
COMPREPLY[i++]="${2-}$c"
346+
fi
347+
done
348+
;;
333349
*)
334350
local c i=0 IFS=$' \t\n'
335351
for c in $1; do
352+
if [[ $c == "--" ]]; then
353+
c="--no-...${4-}"
354+
if [[ $c == "$cur_"* ]]; then
355+
COMPREPLY[i++]="${2-}$c "
356+
fi
357+
break
358+
fi
336359
c="$c${4-}"
337360
if [[ $c == "$cur_"* ]]; then
338361
case $c in
@@ -1161,7 +1184,7 @@ _git_am ()
11611184
return
11621185
;;
11631186
--*)
1164-
__gitcomp_builtin am "--no-utf8" \
1187+
__gitcomp_builtin am "" \
11651188
"$__git_am_inprogress_options"
11661189
return
11671190
esac
@@ -1261,9 +1284,7 @@ _git_branch ()
12611284
__git_complete_refs --cur="${cur##--set-upstream-to=}"
12621285
;;
12631286
--*)
1264-
__gitcomp_builtin branch "--no-color --no-abbrev
1265-
--no-track --no-column
1266-
"
1287+
__gitcomp_builtin branch
12671288
;;
12681289
*)
12691290
if [ $only_local_ref = "y" -a $has_r = "n" ]; then
@@ -1304,7 +1325,7 @@ _git_checkout ()
13041325
__gitcomp "diff3 merge" "" "${cur##--conflict=}"
13051326
;;
13061327
--*)
1307-
__gitcomp_builtin checkout "--no-track --no-recurse-submodules"
1328+
__gitcomp_builtin checkout
13081329
;;
13091330
*)
13101331
# check if --track, --no-track, or --no-guess was specified
@@ -1367,7 +1388,7 @@ _git_clone ()
13671388
{
13681389
case "$cur" in
13691390
--*)
1370-
__gitcomp_builtin clone "--no-single-branch"
1391+
__gitcomp_builtin clone
13711392
return
13721393
;;
13731394
esac
@@ -1400,7 +1421,7 @@ _git_commit ()
14001421
return
14011422
;;
14021423
--*)
1403-
__gitcomp_builtin commit "--no-edit --verify"
1424+
__gitcomp_builtin commit
14041425
return
14051426
esac
14061427

@@ -1503,7 +1524,7 @@ _git_fetch ()
15031524
return
15041525
;;
15051526
--*)
1506-
__gitcomp_builtin fetch "--no-tags"
1527+
__gitcomp_builtin fetch
15071528
return
15081529
;;
15091530
esac
@@ -1540,7 +1561,7 @@ _git_fsck ()
15401561
{
15411562
case "$cur" in
15421563
--*)
1543-
__gitcomp_builtin fsck "--no-reflogs"
1564+
__gitcomp_builtin fsck
15441565
return
15451566
;;
15461567
esac
@@ -1646,7 +1667,7 @@ _git_ls_files ()
16461667
{
16471668
case "$cur" in
16481669
--*)
1649-
__gitcomp_builtin ls-files "--no-empty-directory"
1670+
__gitcomp_builtin ls-files
16501671
return
16511672
;;
16521673
esac
@@ -1797,12 +1818,7 @@ _git_merge ()
17971818

17981819
case "$cur" in
17991820
--*)
1800-
__gitcomp_builtin merge "--no-rerere-autoupdate
1801-
--no-commit --no-edit --no-ff
1802-
--no-log --no-progress
1803-
--no-squash --no-stat
1804-
--no-verify-signatures
1805-
"
1821+
__gitcomp_builtin merge
18061822
return
18071823
esac
18081824
__git_complete_refs
@@ -1901,10 +1917,7 @@ _git_pull ()
19011917
return
19021918
;;
19031919
--*)
1904-
__gitcomp_builtin pull "--no-autostash --no-commit --no-edit
1905-
--no-ff --no-log --no-progress --no-rebase
1906-
--no-squash --no-stat --no-tags
1907-
--no-verify-signatures"
1920+
__gitcomp_builtin pull
19081921

19091922
return
19101923
;;
@@ -2095,7 +2108,7 @@ _git_status ()
20952108
return
20962109
;;
20972110
--*)
2098-
__gitcomp_builtin status "--no-column"
2111+
__gitcomp_builtin status
20992112
return
21002113
;;
21012114
esac
@@ -2345,7 +2358,7 @@ _git_remote ()
23452358

23462359
case "$subcommand,$cur" in
23472360
add,--*)
2348-
__gitcomp_builtin remote_add "--no-tags"
2361+
__gitcomp_builtin remote_add
23492362
;;
23502363
add,*)
23512364
;;
@@ -2425,7 +2438,7 @@ _git_revert ()
24252438
fi
24262439
case "$cur" in
24272440
--*)
2428-
__gitcomp_builtin revert "--no-edit" \
2441+
__gitcomp_builtin revert "" \
24292442
"$__git_revert_inprogress_options"
24302443
return
24312444
;;
@@ -2495,7 +2508,7 @@ _git_show_branch ()
24952508
{
24962509
case "$cur" in
24972510
--*)
2498-
__gitcomp_builtin show-branch "--no-color"
2511+
__gitcomp_builtin show-branch
24992512
return
25002513
;;
25012514
esac

parse-options.c

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

430-
/*
431-
* TODO: we are not completing the --no-XXX form yet because there are
432-
* many options that do not suppress it properly.
433-
*/
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+
434477
static int show_gitcomp(struct parse_opt_ctx_t *ctx,
435478
const struct option *opts)
436479
{
480+
const struct option *original_opts = opts;
481+
int nr_noopts = 0;
482+
437483
for (; opts->type != OPTION_END; opts++) {
438484
const char *suffix = "";
439485

@@ -463,8 +509,12 @@ static int show_gitcomp(struct parse_opt_ctx_t *ctx,
463509
}
464510
if (opts->flags & PARSE_OPT_COMP_ARG)
465511
suffix = "=";
512+
if (starts_with(opts->long_name, "no-"))
513+
nr_noopts++;
466514
printf(" --%s%s", opts->long_name, suffix);
467515
}
516+
show_negated_gitcomp(original_opts, -1);
517+
show_negated_gitcomp(original_opts, nr_noopts);
468518
fputc('\n', stdout);
469519
exit(0);
470520
}

t/t9902-completion.sh

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,42 @@ test_expect_success '__gitcomp - suffix' '
501501
EOF
502502
'
503503

504+
test_expect_success '__gitcomp - ignore optional negative options' '
505+
test_gitcomp "--" "--abc --def --no-one -- --no-two" <<-\EOF
506+
--abc Z
507+
--def Z
508+
--no-one Z
509+
--no-... Z
510+
EOF
511+
'
512+
513+
test_expect_success '__gitcomp - ignore/narrow optional negative options' '
514+
test_gitcomp "--a" "--abc --abcdef --no-one -- --no-two" <<-\EOF
515+
--abc Z
516+
--abcdef Z
517+
EOF
518+
'
519+
520+
test_expect_success '__gitcomp - ignore/narrow optional negative options' '
521+
test_gitcomp "--n" "--abc --def --no-one -- --no-two" <<-\EOF
522+
--no-one Z
523+
--no-... Z
524+
EOF
525+
'
526+
527+
test_expect_success '__gitcomp - expand all negative options' '
528+
test_gitcomp "--no-" "--abc --def --no-one -- --no-two" <<-\EOF
529+
--no-one Z
530+
--no-two Z
531+
EOF
532+
'
533+
534+
test_expect_success '__gitcomp - expand/narrow all negative options' '
535+
test_gitcomp "--no-o" "--abc --def --no-one -- --no-two" <<-\EOF
536+
--no-one Z
537+
EOF
538+
'
539+
504540
test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
505541
__gitcomp "$invalid_variable_name"
506542
'
@@ -1398,8 +1434,8 @@ test_expect_success 'double dash "git checkout"' '
13981434
--ignore-other-worktrees Z
13991435
--recurse-submodules Z
14001436
--progress Z
1401-
--no-track Z
1402-
--no-recurse-submodules Z
1437+
--no-quiet Z
1438+
--no-... Z
14031439
EOF
14041440
'
14051441

@@ -1607,6 +1643,7 @@ test_expect_success 'completion used <cmd> completion for alias: !f() { : git <c
16071643
test_expect_success 'completion without explicit _git_xxx function' '
16081644
test_completion "git version --" <<-\EOF
16091645
--build-options Z
1646+
--no-build-options Z
16101647
EOF
16111648
'
16121649

0 commit comments

Comments
 (0)