bash: get --pretty=m<tab> completion to work with bash v4

Bash's programmable completion provides the COMP_WORDS array variable,
which holds the individual words in the current command line.  In bash
versions prior to v4 "words are split on shell metacharacters as the
shell parser would separate them" (quote from bash v3.2.48's man
page).  This behavior has changed with bash v4, and the command line
"is split into words as readline would split it, using COMP_WORDBREAKS
as" "the set of characters that the readline library treats as word
separators" (quote from bash v4's man page).

Since COMP_WORDBREAKS contains the characters : and = by default, this
behavior change in bash affects git's completion script.  For example,
before bash 4, running

	$ git log --pretty=m <tab><tab>

would give a list of pretty-printing formats starting with 'm' but now
it completes on branch names.

It would be possible to work around this by removing '=' and ':' from
COMP_WORDBREAKS, but as noticed in v1.5.6.4~9^2 (bash completion:
Resolve git show ref:path<tab> losing ref: portion, 2008-07-15), that
would break *other* completion scripts.  The bash-completion library
includes a better workaround: the _get_comp_words_by_ref function
re-assembles a copy of COMP_WORDS, excluding a collection of word
separators of the caller's choice.  Use it.

As a bonus, this also improves behavior when tab is pressed with the
cursor in the middle of a word.

To avoid breaking setups with the bash-completion library not already
loaded, if the _get_comp_words_by_ref function is not defined then a
shim that just reads COMP_WORDS will be used instead (no change from
the current behavior in that case).

Signed-off-by: Peter van der Does <peter@avirtualhome.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Explained-by: SZEDER Gábor <szeder@ira.uka.de>
This commit is contained in:
Peter van der Does 2010-12-02 02:17:13 -06:00 committed by Jonathan Nieder
parent 2850c1a882
commit da48616f1d

View File

@ -321,11 +321,39 @@ __gitcomp_1 ()
done
}
if ! type _get_comp_words_by_ref >/dev/null 2>&1; then
_get_comp_words_by_ref ()
{
while [ $# -gt 0 ]; do
case "$1" in
cur)
cur=${COMP_WORDS[COMP_CWORD]}
;;
prev)
prev=${COMP_WORDS[COMP_CWORD-1]}
;;
words)
words=("${COMP_WORDS[@]}")
;;
cword)
cword=$COMP_CWORD
;;
-n)
# assume COMP_WORDBREAKS is already set sanely
shift
;;
esac
shift
done
}
fi
# __gitcomp accepts 1, 2, 3, or 4 arguments
# generates completion reply with compgen
__gitcomp ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
if [ $# -gt 2 ]; then
cur="$3"
fi
@ -384,7 +412,8 @@ __git_tags ()
__git_refs ()
{
local i is_hash=y dir="$(__gitdir "${1-}")"
local cur="${COMP_WORDS[COMP_CWORD]}" format refs
local cur format refs
_get_comp_words_by_ref -n =: cur
if [ -d "$dir" ]; then
case "$cur" in
refs|refs/*)
@ -482,7 +511,8 @@ __git_compute_merge_strategies ()
__git_complete_file ()
{
local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
local pfx ls ref cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
?*:*)
ref="${cur%%:*}"
@ -530,7 +560,8 @@ __git_complete_file ()
__git_complete_revlist ()
{
local pfx cur="${COMP_WORDS[COMP_CWORD]}"
local pfx cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
*...*)
pfx="${cur%...*}..."
@ -550,11 +581,12 @@ __git_complete_revlist ()
__git_complete_remote_or_refspec ()
{
local cmd="${COMP_WORDS[1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur words cword
_get_comp_words_by_ref -n =: cur words cword
local cmd="${words[1]}"
local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
while [ $c -lt $COMP_CWORD ]; do
i="${COMP_WORDS[c]}"
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
--all)
@ -622,13 +654,14 @@ __git_complete_remote_or_refspec ()
__git_complete_strategy ()
{
local cur prev
_get_comp_words_by_ref -n =: cur prev
__git_compute_merge_strategies
case "${COMP_WORDS[COMP_CWORD-1]}" in
case "$prev" in
-s|--strategy)
__gitcomp "$__git_merge_strategies"
return 0
esac
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--strategy=*)
__gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
@ -788,10 +821,10 @@ __git_aliased_command ()
# __git_find_on_cmdline requires 1 argument
__git_find_on_cmdline ()
{
local word subcommand c=1
while [ $c -lt $COMP_CWORD ]; do
word="${COMP_WORDS[c]}"
local word subcommand c=1 words cword
_get_comp_words_by_ref -n =: words cword
while [ $c -lt $cword ]; do
word="${words[c]}"
for subcommand in $1; do
if [ "$subcommand" = "$word" ]; then
echo "$subcommand"
@ -804,9 +837,10 @@ __git_find_on_cmdline ()
__git_has_doubledash ()
{
local c=1
while [ $c -lt $COMP_CWORD ]; do
if [ "--" = "${COMP_WORDS[c]}" ]; then
local c=1 words cword
_get_comp_words_by_ref -n =: words cword
while [ $c -lt $cword ]; do
if [ "--" = "${words[c]}" ]; then
return 0
fi
c=$((++c))
@ -818,7 +852,8 @@ __git_whitespacelist="nowarn warn error error-all fix"
_git_am ()
{
local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
local cur dir="$(__gitdir)"
_get_comp_words_by_ref -n =: cur
if [ -d "$dir"/rebase-apply ]; then
__gitcomp "--skip --continue --resolved --abort"
return
@ -842,7 +877,8 @@ _git_am ()
_git_apply ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--whitespace=*)
__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
@ -865,7 +901,8 @@ _git_add ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -879,7 +916,8 @@ _git_add ()
_git_archive ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--format=*)
__gitcomp "$(git archive --list)" "" "${cur##--format=}"
@ -923,10 +961,11 @@ _git_bisect ()
_git_branch ()
{
local i c=1 only_local_ref="n" has_r="n"
local i c=1 only_local_ref="n" has_r="n" cur words cword
while [ $c -lt $COMP_CWORD ]; do
i="${COMP_WORDS[c]}"
_get_comp_words_by_ref -n =: cur words cword
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
-d|-m) only_local_ref="y" ;;
-r) has_r="y" ;;
@ -934,7 +973,7 @@ _git_branch ()
c=$((++c))
done
case "${COMP_WORDS[COMP_CWORD]}" in
case "$cur" in
--*)
__gitcomp "
--color --no-color --verbose --abbrev= --no-abbrev
@ -954,8 +993,10 @@ _git_branch ()
_git_bundle ()
{
local cmd="${COMP_WORDS[2]}"
case "$COMP_CWORD" in
local words cword
_get_comp_words_by_ref -n =: words cword
local cmd="${words[2]}"
case "$cword" in
2)
__gitcomp "create list-heads verify unbundle"
;;
@ -976,7 +1017,8 @@ _git_checkout ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--conflict=*)
__gitcomp "diff3 merge" "" "${cur##--conflict=}"
@ -1000,7 +1042,8 @@ _git_cherry ()
_git_cherry_pick ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--edit --no-commit"
@ -1015,7 +1058,8 @@ _git_clean ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--dry-run --quiet"
@ -1027,7 +1071,8 @@ _git_clean ()
_git_clone ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -1054,7 +1099,8 @@ _git_commit ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--cleanup=*)
__gitcomp "default strip verbatim whitespace
@ -1089,7 +1135,8 @@ _git_commit ()
_git_describe ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -1121,7 +1168,8 @@ _git_diff ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
@ -1142,7 +1190,8 @@ _git_difftool ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--tool=*)
__gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
@ -1167,7 +1216,8 @@ __git_fetch_options="
_git_fetch ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "$__git_fetch_options"
@ -1179,7 +1229,8 @@ _git_fetch ()
_git_format_patch ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--thread=*)
__gitcomp "
@ -1211,7 +1262,8 @@ _git_format_patch ()
_git_fsck ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -1226,7 +1278,8 @@ _git_fsck ()
_git_gc ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--prune --aggressive"
@ -1245,7 +1298,8 @@ _git_grep ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -1268,7 +1322,8 @@ _git_grep ()
_git_help ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--all --info --man --web"
@ -1286,7 +1341,8 @@ _git_help ()
_git_init ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--shared=*)
__gitcomp "
@ -1306,7 +1362,8 @@ _git_ls_files ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--cached --deleted --modified --others --ignored
@ -1360,12 +1417,13 @@ _git_log ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local g="$(git rev-parse --git-dir 2>/dev/null)"
local merge=""
if [ -f "$g/MERGE_HEAD" ]; then
merge="--merge"
fi
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--pretty=*)
__gitcomp "$__git_log_pretty_formats
@ -1419,7 +1477,8 @@ _git_merge ()
{
__git_complete_strategy && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "$__git_merge_options"
@ -1430,7 +1489,8 @@ _git_merge ()
_git_mergetool ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--tool=*)
__gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
@ -1451,7 +1511,8 @@ _git_merge_base ()
_git_mv ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--dry-run"
@ -1469,12 +1530,14 @@ _git_name_rev ()
_git_notes ()
{
local subcommands="edit show"
local words cword
_get_comp_words_by_ref -n =: words cword
if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
__gitcomp "$subcommands"
return
fi
case "${COMP_WORDS[COMP_CWORD-1]}" in
case "${words[cword-1]}" in
-m|-F)
COMPREPLY=()
;;
@ -1488,7 +1551,8 @@ _git_pull ()
{
__git_complete_strategy && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -1504,8 +1568,9 @@ _git_pull ()
_git_push ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
case "${COMP_WORDS[COMP_CWORD-1]}" in
local cur prev
_get_comp_words_by_ref -n =: cur prev
case "$prev" in
--repo)
__gitcomp "$(__git_remotes)"
return
@ -1528,7 +1593,9 @@ _git_push ()
_git_rebase ()
{
local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
local dir="$(__gitdir)"
local cur
_get_comp_words_by_ref -n =: cur
if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
__gitcomp "--continue --skip --abort"
return
@ -1558,7 +1625,8 @@ __git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"
_git_send_email ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--confirm=*)
__gitcomp "
@ -1600,9 +1668,11 @@ _git_stage ()
__git_config_get_set_variables ()
{
local prevword word config_file= c=$COMP_CWORD
local words cword
_get_comp_words_by_ref -n =: words cword
local prevword word config_file= c=$cword
while [ $c -gt 1 ]; do
word="${COMP_WORDS[c]}"
word="${words[c]}"
case "$word" in
--global|--system|--file=*)
config_file="$word"
@ -1630,9 +1700,9 @@ __git_config_get_set_variables ()
_git_config ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local prv="${COMP_WORDS[COMP_CWORD-1]}"
case "$prv" in
local cur prev
_get_comp_words_by_ref -n =: cur prev
case "$prev" in
branch.*.remote)
__gitcomp "$(__git_remotes)"
return
@ -1642,13 +1712,13 @@ _git_config ()
return
;;
remote.*.fetch)
local remote="${prv#remote.}"
local remote="${prev#remote.}"
remote="${remote%.fetch}"
__gitcomp "$(__git_refs_remotes "$remote")"
return
;;
remote.*.push)
local remote="${prv#remote.}"
local remote="${prev#remote.}"
remote="${remote%.push}"
__gitcomp "$(git --git-dir="$(__gitdir)" \
for-each-ref --format='%(refname):%(refname)' \
@ -2039,7 +2109,8 @@ _git_reset ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--merge --mixed --hard --soft --patch"
@ -2051,7 +2122,8 @@ _git_reset ()
_git_revert ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--edit --mainline --no-edit --no-commit --signoff"
@ -2065,7 +2137,8 @@ _git_rm ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--cached --dry-run --ignore-unmatch --quiet"
@ -2079,7 +2152,8 @@ _git_shortlog ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -2097,7 +2171,8 @@ _git_show ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--pretty=*)
__gitcomp "$__git_log_pretty_formats
@ -2121,7 +2196,8 @@ _git_show ()
_git_show_branch ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "
@ -2138,7 +2214,8 @@ _git_show_branch ()
_git_stash ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
local save_opts='--keep-index --no-keep-index --quiet --patch'
local subcommands='save list show apply clear drop pop create branch'
local subcommand="$(__git_find_on_cmdline "$subcommands")"
@ -2183,7 +2260,8 @@ _git_submodule ()
local subcommands="add status init update summary foreach sync"
if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "--quiet --cached"
@ -2227,7 +2305,8 @@ _git_svn ()
--edit --rmdir --find-copies-harder --copy-similarity=
"
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
_get_comp_words_by_ref -n =: cur
case "$subcommand,$cur" in
fetch,--*)
__gitcomp "--revision= --fetch-all $fc_opts"
@ -2299,8 +2378,10 @@ _git_svn ()
_git_tag ()
{
local i c=1 f=0
while [ $c -lt $COMP_CWORD ]; do
i="${COMP_WORDS[c]}"
local words cword prev
_get_comp_words_by_ref -n =: words cword prev
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
-d|-v)
__gitcomp "$(__git_tags)"
@ -2313,7 +2394,7 @@ _git_tag ()
c=$((++c))
done
case "${COMP_WORDS[COMP_CWORD-1]}" in
case "$prev" in
-m|-F)
COMPREPLY=()
;;
@ -2339,8 +2420,10 @@ _git ()
{
local i c=1 command __git_dir
while [ $c -lt $COMP_CWORD ]; do
i="${COMP_WORDS[c]}"
local cur words cword
_get_comp_words_by_ref -n =: cur words cword
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
--git-dir=*) __git_dir="${i#--git-dir=}" ;;
--bare) __git_dir="." ;;
@ -2352,7 +2435,7 @@ _git ()
done
if [ -z "$command" ]; then
case "${COMP_WORDS[COMP_CWORD]}" in
case "$cur" in
--*) __gitcomp "
--paginate
--no-pager
@ -2385,12 +2468,13 @@ _gitk ()
{
__git_has_doubledash && return
local cur="${COMP_WORDS[COMP_CWORD]}"
local cur
local g="$(__gitdir)"
local merge=""
if [ -f "$g/MERGE_HEAD" ]; then
merge="--merge"
fi
_get_comp_words_by_ref -n =: cur
case "$cur" in
--*)
__gitcomp "