completion: wrap __git_refs() for better option parsing

__git_refs() currently accepts two optional positional parameters: a
remote and a flag for 'git checkout's tracking DWIMery.  To fix a
minor bug, and, more importantly, for faster refs completion, this
series will add three more parameters: a prefix, the current word to
be completed and a suffix, i.e. the options accepted by __gitcomp() &
friends, and will change __git_refs() to list only refs matching that
given current word and to add that given prefix and suffix to the
listed refs.

However, __git_refs() is the helper function that is most likely used
in users' custom completion scriptlets for their own git commands, and
we don't want to break those, so

  - we can't change __git_refs()'s default output format, i.e. we
    can't by default append a trailing space to every listed ref,
    meaning that the suffix parameter containing the default trailing
    space would have to be specified on every invocation, and

  - we can't change the position of existing positional parameters
    either, so there would have to be plenty of set-but-empty
    placeholder positional parameters all over the completion script.

Furthermore, with five positional parameters it would be really hard
to remember which position means what.

To keep callsites simple, add the new wrapper function
__git_complete_refs() around __git_refs(), which:

  - instead of positional parameters accepts real '--opt=val'-style
    options and with minimalistic option parsing translates them to
    __git_refs()'s and __gitcomp_nl()'s positional parameters, and

  - includes the '__gitcomp_nl "$(__git_refs ...)" ...' command
    substitution to make its behavior match its name and the behavior
    of other __git_complete_* functions, and to limit future changes
    in this series to __git_refs() and this new wrapper function.

Call this wrapper function instead of __git_refs() wherever possible
throughout the completion script, i.e. when __git_refs()'s output is
fed to __gitcomp_nl() right away without further processing, which
means all callsites except a single one in the __git_refs2() helper.

Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
SZEDER Gábor 2017-03-23 16:29:12 +01:00 committed by Junio C Hamano
parent c977eefd55
commit 15b4a16395
2 changed files with 173 additions and 35 deletions

View File

@ -354,6 +354,8 @@ __git_tags ()
# Can be the name of a configured remote, a path, or a URL. # Can be the name of a configured remote, a path, or a URL.
# 2: In addition to local refs, list unique branches from refs/remotes/ for # 2: In addition to local refs, list unique branches from refs/remotes/ for
# 'git checkout's tracking DWIMery (optional; ignored, if set but empty). # 'git checkout's tracking DWIMery (optional; ignored, if set but empty).
#
# Use __git_complete_refs() instead.
__git_refs () __git_refs ()
{ {
local i hash dir track="${2-}" local i hash dir track="${2-}"
@ -446,6 +448,36 @@ __git_refs ()
esac esac
} }
# Completes refs, short and long, local and remote, symbolic and pseudo.
#
# Usage: __git_complete_refs [<option>]...
# --remote=<remote>: The remote to list refs from, can be the name of a
# configured remote, a path, or a URL.
# --track: List unique remote branches for 'git checkout's tracking DWIMery.
# --pfx=<prefix>: A prefix to be added to each ref.
# --cur=<word>: The current ref to be completed. Defaults to the current
# word to be completed.
# --sfx=<suffix>: A suffix to be appended to each ref instead of the default
# space.
__git_complete_refs ()
{
local remote track pfx cur_="$cur" sfx=" "
while test $# != 0; do
case "$1" in
--remote=*) remote="${1##--remote=}" ;;
--track) track="yes" ;;
--pfx=*) pfx="${1##--pfx=}" ;;
--cur=*) cur_="${1##--cur=}" ;;
--sfx=*) sfx="${1##--sfx=}" ;;
*) return 1 ;;
esac
shift
done
__gitcomp_nl "$(__git_refs "$remote" "$track")" "$pfx" "$cur_" "$sfx"
}
# __git_refs2 requires 1 argument (to pass to __git_refs) # __git_refs2 requires 1 argument (to pass to __git_refs)
__git_refs2 () __git_refs2 ()
{ {
@ -554,15 +586,15 @@ __git_complete_revlist_file ()
*...*) *...*)
pfx="${cur_%...*}..." pfx="${cur_%...*}..."
cur_="${cur_#*...}" cur_="${cur_#*...}"
__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" __git_complete_refs --pfx="$pfx" --cur="$cur_"
;; ;;
*..*) *..*)
pfx="${cur_%..*}.." pfx="${cur_%..*}.."
cur_="${cur_#*..}" cur_="${cur_#*..}"
__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" __git_complete_refs --pfx="$pfx" --cur="$cur_"
;; ;;
*) *)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
;; ;;
esac esac
} }
@ -649,21 +681,21 @@ __git_complete_remote_or_refspec ()
if [ $lhs = 1 ]; then if [ $lhs = 1 ]; then
__gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_" __gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
else else
__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" __git_complete_refs --pfx="$pfx" --cur="$cur_"
fi fi
;; ;;
pull|remote) pull|remote)
if [ $lhs = 1 ]; then if [ $lhs = 1 ]; then
__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_" __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_"
else else
__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" __git_complete_refs --pfx="$pfx" --cur="$cur_"
fi fi
;; ;;
push) push)
if [ $lhs = 1 ]; then if [ $lhs = 1 ]; then
__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_" __git_complete_refs --pfx="$pfx" --cur="$cur_"
else else
__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_" __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_"
fi fi
;; ;;
esac esac
@ -1061,7 +1093,7 @@ _git_bisect ()
case "$subcommand" in case "$subcommand" in
bad|good|reset|skip|start) bad|good|reset|skip|start)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
;; ;;
*) *)
;; ;;
@ -1083,7 +1115,7 @@ _git_branch ()
case "$cur" in case "$cur" in
--set-upstream-to=*) --set-upstream-to=*)
__gitcomp_nl "$(__git_refs)" "" "${cur##--set-upstream-to=}" __git_complete_refs --cur="${cur##--set-upstream-to=}"
;; ;;
--*) --*)
__gitcomp " __gitcomp "
@ -1097,7 +1129,7 @@ _git_branch ()
if [ $only_local_ref = "y" -a $has_r = "n" ]; then if [ $only_local_ref = "y" -a $has_r = "n" ]; then
__gitcomp_nl "$(__git_heads)" __gitcomp_nl "$(__git_heads)"
else else
__gitcomp_nl "$(__git_refs)" __git_complete_refs
fi fi
;; ;;
esac esac
@ -1140,18 +1172,18 @@ _git_checkout ()
*) *)
# check if --track, --no-track, or --no-guess was specified # check if --track, --no-track, or --no-guess was specified
# if so, disable DWIM mode # if so, disable DWIM mode
local flags="--track --no-track --no-guess" track=1 local flags="--track --no-track --no-guess" track_opt="--track"
if [ -n "$(__git_find_on_cmdline "$flags")" ]; then if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
track='' track_opt=''
fi fi
__gitcomp_nl "$(__git_refs '' $track)" __git_complete_refs $track_opt
;; ;;
esac esac
} }
_git_cherry () _git_cherry ()
{ {
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_cherry_pick () _git_cherry_pick ()
@ -1166,7 +1198,7 @@ _git_cherry_pick ()
__gitcomp "--edit --no-commit --signoff --strategy= --mainline" __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
;; ;;
*) *)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
;; ;;
esac esac
} }
@ -1216,7 +1248,7 @@ _git_commit ()
{ {
case "$prev" in case "$prev" in
-c|-C) -c|-C)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
return return
;; ;;
esac esac
@ -1229,7 +1261,7 @@ _git_commit ()
;; ;;
--reuse-message=*|--reedit-message=*|\ --reuse-message=*|--reedit-message=*|\
--fixup=*|--squash=*) --fixup=*|--squash=*)
__gitcomp_nl "$(__git_refs)" "" "${cur#*=}" __git_complete_refs --cur="${cur#*=}"
return return
;; ;;
--untracked-files=*) --untracked-files=*)
@ -1267,7 +1299,7 @@ _git_describe ()
" "
return return
esac esac
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
__git_diff_algorithms="myers minimal patience histogram" __git_diff_algorithms="myers minimal patience histogram"
@ -1453,7 +1485,7 @@ _git_grep ()
;; ;;
esac esac
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_help () _git_help ()
@ -1621,7 +1653,7 @@ _git_merge ()
--rerere-autoupdate --no-rerere-autoupdate --abort --continue" --rerere-autoupdate --no-rerere-autoupdate --abort --continue"
return return
esac esac
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_mergetool () _git_mergetool ()
@ -1646,7 +1678,7 @@ _git_merge_base ()
return return
;; ;;
esac esac
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_mv () _git_mv ()
@ -1684,7 +1716,7 @@ _git_notes ()
,*) ,*)
case "$prev" in case "$prev" in
--ref) --ref)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
;; ;;
*) *)
__gitcomp "$subcommands --ref" __gitcomp "$subcommands --ref"
@ -1693,7 +1725,7 @@ _git_notes ()
;; ;;
add,--reuse-message=*|append,--reuse-message=*|\ add,--reuse-message=*|append,--reuse-message=*|\
add,--reedit-message=*|append,--reedit-message=*) add,--reedit-message=*|append,--reedit-message=*)
__gitcomp_nl "$(__git_refs)" "" "${cur#*=}" __git_complete_refs --cur="${cur#*=}"
;; ;;
add,--*|append,--*) add,--*|append,--*)
__gitcomp '--file= --message= --reedit-message= __gitcomp '--file= --message= --reedit-message=
@ -1712,7 +1744,7 @@ _git_notes ()
-m|-F) -m|-F)
;; ;;
*) *)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
;; ;;
esac esac
;; ;;
@ -1750,10 +1782,10 @@ __git_complete_force_with_lease ()
--*=) --*=)
;; ;;
*:*) *:*)
__gitcomp_nl "$(__git_refs)" "" "${cur_#*:}" __git_complete_refs --cur="${cur_#*:}"
;; ;;
*) *)
__gitcomp_nl "$(__git_refs)" "" "$cur_" __git_complete_refs --cur="$cur_"
;; ;;
esac esac
} }
@ -1829,7 +1861,7 @@ _git_rebase ()
return return
esac esac
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_reflog () _git_reflog ()
@ -1840,7 +1872,7 @@ _git_reflog ()
if [ -z "$subcommand" ]; then if [ -z "$subcommand" ]; then
__gitcomp "$subcommands" __gitcomp "$subcommands"
else else
__gitcomp_nl "$(__git_refs)" __git_complete_refs
fi fi
} }
@ -1986,7 +2018,7 @@ _git_config ()
return return
;; ;;
branch.*.merge) branch.*.merge)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
return return
;; ;;
branch.*.rebase) branch.*.rebase)
@ -2460,7 +2492,7 @@ _git_remote ()
_git_replace () _git_replace ()
{ {
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_reset () _git_reset ()
@ -2473,7 +2505,7 @@ _git_reset ()
return return
;; ;;
esac esac
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_revert () _git_revert ()
@ -2489,7 +2521,7 @@ _git_revert ()
return return
;; ;;
esac esac
__gitcomp_nl "$(__git_refs)" __git_complete_refs
} }
_git_rm () _git_rm ()
@ -2597,7 +2629,7 @@ _git_stash ()
;; ;;
branch,*) branch,*)
if [ $cword -eq 3 ]; then if [ $cword -eq 3 ]; then
__gitcomp_nl "$(__git_refs)"; __git_complete_refs
else else
__gitcomp_nl "$(__git stash list \ __gitcomp_nl "$(__git stash list \
| sed -n -e 's/:.*//p')" | sed -n -e 's/:.*//p')"
@ -2755,7 +2787,7 @@ _git_tag ()
fi fi
;; ;;
*) *)
__gitcomp_nl "$(__git_refs)" __git_complete_refs
;; ;;
esac esac

View File

@ -775,6 +775,112 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer
test_cmp expected "$actual" test_cmp expected "$actual"
' '
test_expect_success '__git_complete_refs - simple' '
sed -e "s/Z$//" >expected <<-EOF &&
HEAD Z
master Z
matching-branch Z
other/branch-in-other Z
other/master-in-other Z
matching-tag Z
EOF
(
cur= &&
__git_complete_refs &&
print_comp
) &&
test_cmp expected out
'
test_expect_success '__git_complete_refs - matching' '
sed -e "s/Z$//" >expected <<-EOF &&
matching-branch Z
matching-tag Z
EOF
(
cur=mat &&
__git_complete_refs &&
print_comp
) &&
test_cmp expected out
'
test_expect_success '__git_complete_refs - remote' '
sed -e "s/Z$//" >expected <<-EOF &&
HEAD Z
branch-in-other Z
master-in-other Z
EOF
(
cur=
__git_complete_refs --remote=other &&
print_comp
) &&
test_cmp expected out
'
test_expect_success '__git_complete_refs - track' '
sed -e "s/Z$//" >expected <<-EOF &&
HEAD Z
master Z
matching-branch Z
other/branch-in-other Z
other/master-in-other Z
matching-tag Z
branch-in-other Z
master-in-other Z
EOF
(
cur=
__git_complete_refs --track &&
print_comp
) &&
test_cmp expected out
'
test_expect_success '__git_complete_refs - current word' '
sed -e "s/Z$//" >expected <<-EOF &&
matching-branch Z
matching-tag Z
EOF
(
cur="--option=mat" &&
__git_complete_refs --cur="${cur#*=}" &&
print_comp
) &&
test_cmp expected out
'
test_expect_success '__git_complete_refs - prefix' '
sed -e "s/Z$//" >expected <<-EOF &&
v1.0..matching-branch Z
v1.0..matching-tag Z
EOF
(
cur=v1.0..mat &&
__git_complete_refs --pfx=v1.0.. --cur=mat &&
print_comp
) &&
test_cmp expected out
'
test_expect_success '__git_complete_refs - suffix' '
cat >expected <<-EOF &&
HEAD.
master.
matching-branch.
other/branch-in-other.
other/master-in-other.
matching-tag.
EOF
(
cur= &&
__git_complete_refs --sfx=. &&
print_comp
) &&
test_cmp expected out
'
test_expect_success 'teardown after ref completion' ' test_expect_success 'teardown after ref completion' '
git branch -d matching-branch && git branch -d matching-branch &&
git tag -d matching-tag && git tag -d matching-tag &&