Skip to content

Commit

Permalink
Merge branch 'ladios-bash-completion'
Browse files Browse the repository at this point in the history
  • Loading branch information
mislav committed Jul 2, 2013
2 parents 7010fa5 + da76c63 commit 66a3502
Show file tree
Hide file tree
Showing 4 changed files with 361 additions and 10 deletions.
331 changes: 331 additions & 0 deletions etc/hub.bash_completion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,335 @@ EOF

# Ensure cached commands are cleared
__git_all_commands=""

##########################
# hub command completions
##########################

# hub alias [-s] [SHELL]
_git_alias() {
local i c=2 s=-s sh shells="bash zsh sh ksh csh fish"
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
-s)
unset s
;;
*)
for sh in $shells; do
if [ "$sh" = "$i" ]; then
unset shells
break
fi
done
;;
esac
((c++))
done
__gitcomp "$s $shells"
}

# hub browse [-u] [--|[USER/]REPOSITORY] [SUBPAGE]
_git_browse() {
local i c=2 u=-u repo subpage
local -A subpages
subpages["/"]="commits issues tree wiki pulls branches stargazers
contributors network network/ graphs graphs/"
subpages["/network"]="members"
subpages["/graphs"]="commit-activity code-frequency punch-card"
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
-u)
unset u
;;
*)
if [ -z "$repo" ]; then
repo=$i
else
subpage=$i
fi
;;
esac
((c++))
done
if [ -z "$repo" ]; then
__gitcomp "$u -- $(__hub_github_repos '\p')"
elif [ -z "$subpage" ]; then
case "$cur" in
*/*)
local pfx="${cur%/*}" cur_="${cur#*/}"
__gitcomp "${subpages[/$pfx]}" "$pfx/" "$cur_"
;;
*)
__gitcomp "$u ${subpages[/]}"
;;
esac
else
__gitcomp "$u"
fi
}

# hub compare [-u] [USER[/REPOSITORY]] [[START...]END]
_git_compare() {
local i c=$((cword - 1)) u=-u user remote owner repo arg_repo rev
while [ $c -gt 1 ]; do
i="${words[c]}"
case "$i" in
-u)
unset u
;;
*)
if [ -z "$rev" ]; then
# Even though the logic below is able to complete both user/repo
# and revision in the right place, when there is only one argument
# (other than -u) in the command, that argument will be taken as
# revision. For example:
# $ hub compare -u upstream
# > https://github.com/USER/REPO/compare/upstream
if __hub_github_repos '\p' | grep -Eqx "^$i(/[^/]+)?"; then
arg_repo=$i
else
rev=$i
fi
elif [ -z "$arg_repo" ]; then
arg_repo=$i
fi
;;
esac
((c--))
done

# Here we want to find out the git remote name of user/repo, in order to
# generate an appropriate revision list
if [ -z "$arg_repo" ]; then
user=$(__hub_github_user)
if [ -z "$user" ]; then
for i in $(__hub_github_repos); do
remote=${i%%:*}
repo=${i#*:}
if [ "$remote" = origin ]; then
break
fi
done
else
for i in $(__hub_github_repos); do
remote=${i%%:*}
repo=${i#*:}
owner=${repo%%/*}
if [ "$user" = "$owner" ]; then
break
fi
done
fi
else
for i in $(__hub_github_repos); do
remote=${i%%:*}
repo=${i#*:}
owner=${repo%%/*}
case "$arg_repo" in
"$repo"|"$owner")
break
;;
esac
done
fi

local pfx cur_="$cur"
case "$cur_" in
*..*)
pfx="${cur_%%..*}..."
cur_="${cur_##*..}"
__gitcomp_nl "$(__hub_revlist $remote)" "$pfx" "$cur_"
;;
*)
if [ -z "${arg_repo}${rev}" ]; then
__gitcomp "$u $(__hub_github_repos '\o\n\p') $(__hub_revlist $remote)"
elif [ -z "$rev" ]; then
__gitcomp "$u $(__hub_revlist $remote)"
else
__gitcomp "$u"
fi
;;
esac
}

# hub create [NAME] [-p] [-d DESCRIPTION] [-h HOMEPAGE]
_git_create() {
local i c=2 name repo flags="-p -d -h"
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
-d|-h)
((c++))
;;&
-p|-d|-h)
flags=${flags/$i/}
;;
*)
name=$i
;;
esac
((c++))
done
if [ -z "$name" ]; then
repo=$(basename "$(pwd)")
fi
case "$prev" in
-d|-h)
COMPREPLY=()
;;
-p|*)
__gitcomp "$repo $flags"
;;
esac
}

# hub fork [--no-remote]
_git_fork() {
local i c=2 remote=yes
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
--no-remote)
unset remote
;;
esac
((c++))
done
if [ -n "$remote" ]; then
__gitcomp "--no-remote"
fi
}

# hub pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-b <BASE>] [-h <HEAD>]
_git_pull_request() {
local i c=2 flags="-f -m -F -i -b -h"
while [ $c -lt $cword ]; do
i="${words[c]}"
case "$i" in
-m|-F|-i|-b|-h)
((c++))
;;&
-f|-m|-F|-i|-b|-h)
flags=${flags/$i/}
;;
esac
((c++))
done
case "$prev" in
-i)
COMPREPLY=()
;;
-b|-h)
# (Doesn't seem to need this...)
# Uncomment the following line when 'owner/repo:[TAB]' misbehaved
#_get_comp_words_by_ref -n : cur
__gitcomp_nl "$(__hub_heads)"
__ltrim_colon_completions "$cur"
;;
-f|*)
__gitcomp "$flags"
;;
esac
}

###################
# Helper functions
###################

# __hub_github_user [HOST]
# Return $GITHUB_USER or the default github user defined in hub config
# HOST - Host to be looked-up in hub config. Default is "github.com"
__hub_github_user() {
if [ -n "$GITHUB_USER" ]; then
echo $GITHUB_USER
return
fi
local line h k v host=${1:-github.com} config=${HUB_CONFIG:-~/.config/hub}
if [ -f "$config" ]; then
while read line; do
if [ "$line" = "---" ]; then
continue
fi
k=${line%%:*}
v=${line#*:}
if [ -z "$v" ]; then
if [ "$h" = "$host" ]; then
break
fi
h=$k
continue
fi
k=${k#* }
v=${v#* }
if [ "$h" = "$host" ] && [ "$k" = "user" ]; then
echo "$v"
break
fi
done < "$config"
fi
}

# __hub_github_repos [FORMAT]
# List all github hosted repository
# FORMAT - Format string contains multiple of these:
# \m remote
# \p owner/repo
# \o owner
# escaped characters (\n, \t ...etc) work
# If omitted, prints all github repos in the format of "remote:owner/repo"
__hub_github_repos() {
local f format=$1
if [ -z "$(__gitdir)" ]; then
return
fi
if [ -z "$format" ]; then
format='\1:\2'
else
format=${format//\m/\1}
format=${format//\p/\2}
format=${format//\o/\3}
fi
command git config --get-regexp 'remote\.[^.]*\.url' |
grep -E ' ((https?|git)://|git@)github\.com[:/][^:/]+/[^/]+$' |
sed -E 's#^remote\.([^.]+)\.url +.+[:/](([^/]+)/[^.]+)(\.git)?$#'"$format"'#'
}

# __hub_heads
# List all local "branch", and remote "owner/repo:branch"
__hub_heads() {
local i remote repo branch dir=$(__gitdir)
if [ -d "$dir" ]; then
command git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
"refs/heads/"
for i in $(__hub_github_repos); do
remote=${i%%:*}
repo=${i#*:}
command git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
"refs/remotes/${remote}/" | while read branch; do
echo "${repo}:${branch#${remote}/}"
done
done
fi
}

# __hub_revlist [REMOTE]
# List all tags, and branches under REMOTE, without the "remote/" prefix
# REMOTE - Remote name to search branches from. Default is "origin"
__hub_revlist() {
local i remote=${1:-origin} dir=$(__gitdir)
if [ -d "$dir" ]; then
command git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
"refs/remotes/${remote}/" | while read i; do
echo "${i#${remote}/}"
done
command git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
"refs/tags/"
fi
}

# Enable completion for hub even when not using the alias
complete -o bashdefault -o default -o nospace -F _git hub 2>/dev/null \
|| complete -o default -o nospace -F _git hub
fi
32 changes: 23 additions & 9 deletions features/bash_completion.feature
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
@completion
Feature: bash tab-completion

Scenario: "pu" matches multiple commands including "pull-request"
Background:
Given my shell is bash
And I'm using git-distributed base git completions

Scenario: "pu" matches multiple commands including "pull-request"
When I type "git pu" and press <Tab>
Then the command should not expand
When I press <Tab> again
Then the completion menu should offer "pull pull-request push"

Scenario: "ci-" expands to "ci-status"
Given my shell is bash
And I'm using git-distributed base git completions
When I type "git ci-" and press <Tab>
Then the command should expand to "git ci-status"

# In this combination, zsh uses completion support from a bash script.
Scenario: "ci-" expands to "ci-status"
Given my shell is zsh
And I'm using git-distributed base git completions
When I type "git ci-" and press <Tab>
Then the command should expand to "git ci-status"
Scenario: Offers pull-request flags
When I type "git pull-request -" and press <Tab>
When I press <Tab> again
Then the completion menu should offer "-F -b -f -h -i -m"

Scenario: Browse to issues
When I type "git browse -- i" and press <Tab>
Then the command should expand to "git browse -- issues"

Scenario: Browse to punch-card graph
When I type "git browse -- graphs/p" and press <Tab>
Then the command should expand to "git browse -- graphs/punch-card"

Scenario: Completion of fork argument
When I type "git fork -" and press <Tab>
Then the command should expand to "git fork --no-remote"

Scenario: Completion of user/repo in "browse"
Scenario: Completion of branch names in "compare"
Scenario: Completion of "owner/repo:branch" in "pull-request -h/b"
2 changes: 1 addition & 1 deletion features/support/completion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
link_completion = lambda { |from, name = nil|
name ||= from.basename
raise ArgumentError, from.to_s unless File.exist?(from)
FileUtils.ln_s(from, cpldir + name)
FileUtils.ln_s(from, cpldir + name, :force => true)
}

setup_tmp_home = lambda { |shell|
Expand Down

0 comments on commit 66a3502

Please sign in to comment.