Add bash completion for the blame subcommand
[tig] / contrib / tig-completion.bash
1 ##
2 # bash completion support for tig
3 #
4 # Copyright (C) 2007 Jonas fonseca
5 # Copyright (C) 2006,2007 Shawn Pearce
6 #
7 # Based git's git-completion.sh: http://repo.or.cz/w/git/fastimport.git
8 #
9 # The contained completion routines provide support for completing:
10 #
11 #    *) local and remote branch names
12 #    *) local and remote tag names
13 #    *) tig 'subcommands'
14 #    *) tree paths within 'ref:path/to/file' expressions
15 #
16 # To use these routines:
17 #
18 #    1) Copy this file to somewhere (e.g. ~/.tig-completion.sh).
19 #    2) Added the following line to your .bashrc:
20 #        source ~/.tig-completion.sh
21 #
22 #    3) You may want to make sure the git executable is available
23 #       in your PATH before this script is sourced, as some caching
24 #       is performed while the script loads.  If git isn't found
25 #       at source time then all lookups will be done on demand,
26 #       which may be slightly slower.
27 #
28
29 __tigdir ()
30 {
31         if [ -z "$1" ]; then
32                 if [ -n "$__git_dir" ]; then
33                         echo "$__git_dir"
34                 elif [ -d .git ]; then
35                         echo .git
36                 else
37                         git rev-parse --git-dir 2>/dev/null
38                 fi
39         elif [ -d "$1/.git" ]; then
40                 echo "$1/.git"
41         else
42                 echo "$1"
43         fi
44 }
45
46 _tigcomp ()
47 {
48         local all c s=$'\n' IFS=' '$'\t'$'\n'
49         local cur="${COMP_WORDS[COMP_CWORD]}"
50         if [ $# -gt 2 ]; then
51                 cur="$3"
52         fi
53         for c in $1; do
54                 case "$c$4" in
55                 --*=*) all="$all$c$4$s" ;;
56                 *.)    all="$all$c$4$s" ;;
57                 *)     all="$all$c$4 $s" ;;
58                 esac
59         done
60         IFS=$s
61         COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur"))
62         return
63 }
64
65 __tig_refs ()
66 {
67         local cmd i is_hash=y dir="$(__tigdir "$1")"
68         if [ -d "$dir" ]; then
69                 if [ -e "$dir/HEAD" ]; then echo HEAD; fi
70                 for i in $(git --git-dir="$dir" \
71                         for-each-ref --format='%(refname)' \
72                         refs/tags refs/heads refs/remotes); do
73                         case "$i" in
74                                 refs/tags/*)    echo "${i#refs/tags/}" ;;
75                                 refs/heads/*)   echo "${i#refs/heads/}" ;;
76                                 refs/remotes/*) echo "${i#refs/remotes/}" ;;
77                                 *)              echo "$i" ;;
78                         esac
79                 done
80                 return
81         fi
82         for i in $(git-ls-remote "$dir" 2>/dev/null); do
83                 case "$is_hash,$i" in
84                 y,*) is_hash=n ;;
85                 n,*^{}) is_hash=y ;;
86                 n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
87                 n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
88                 n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
89                 n,*) is_hash=y; echo "$i" ;;
90                 esac
91         done
92 }
93
94 __tig_complete_file ()
95 {
96         local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
97         case "$cur" in
98         ?*:*)
99                 ref="${cur%%:*}"
100                 cur="${cur#*:}"
101                 case "$cur" in
102                 ?*/*)
103                         pfx="${cur%/*}"
104                         cur="${cur##*/}"
105                         ls="$ref:$pfx"
106                         pfx="$pfx/"
107                         ;;
108                 *)
109                         ls="$ref"
110                         ;;
111             esac
112                 COMPREPLY=($(compgen -P "$pfx" \
113                         -W "$(git --git-dir="$(__tigdir)" ls-tree "$ls" \
114                                 | sed '/^100... blob /s,^.*     ,,
115                                        /^040000 tree /{
116                                            s,^.*        ,,
117                                            s,$,/,
118                                        }
119                                        s/^.*    //')" \
120                         -- "$cur"))
121                 ;;
122         *)
123                 _tigcomp "$(__tig_refs)"
124                 ;;
125         esac
126 }
127
128 __tig_complete_revlist ()
129 {
130         local pfx cur="${COMP_WORDS[COMP_CWORD]}"
131         case "$cur" in
132         *...*)
133                 pfx="${cur%...*}..."
134                 cur="${cur#*...}"
135                 _tigcomp "$(__tig_refs)" "$pfx" "$cur"
136                 ;;
137         *..*)
138                 pfx="${cur%..*}.."
139                 cur="${cur#*..}"
140                 _tigcomp "$(__tig_refs)" "$pfx" "$cur"
141                 ;;
142         *.)
143                 _tigcomp "$cur."
144                 ;;
145         *)
146                 _tigcomp "$(__tig_refs)"
147                 ;;
148         esac
149 }
150
151 _tig_options ()
152 {
153         local cur="${COMP_WORDS[COMP_CWORD]}"
154         case "$cur" in
155         --pretty=*)
156                 _tigcomp "
157                         oneline short medium full fuller email raw
158                         " "" "${cur##--pretty=}"
159                 return
160                 ;;
161         --*)
162                 _tigcomp "
163                         --max-count= --max-age= --since= --after=
164                         --min-age= --before= --until=
165                         --root --not --topo-order --date-order
166                         --no-merges
167                         --abbrev-commit --abbrev=
168                         --relative-date
169                         --author= --committer= --grep=
170                         --all-match
171                         --pretty= --name-status --name-only
172                         --not --all
173                         --help --version
174                         "
175                 return
176                 ;;
177         -*)
178                 _tigcomp "-v -h"
179                 return
180                 ;;
181         esac
182         __tig_complete_revlist
183 }
184
185 _tig_blame ()
186 {
187         local reply="" ref=HEAD cur="${COMP_WORDS[COMP_CWORD]}"
188
189         if test "$COMP_CWORD" -lt 3; then
190                 reply="$(__tig_refs)"
191         else
192                 ref="${COMP_WORDS[2]}"
193         fi
194
195         reply="$reply $(git --git-dir="$(__tigdir)" ls-tree "$ref" \
196                         | sed '/^100... blob /s,^.*     ,,
197                                /^040000 tree /{
198                                    s,^.*        ,,
199                                    s,$,/,
200                                }
201                                s/^.*    //')"
202         _tigcomp "$reply"
203 }
204
205 _tig_show ()
206 {
207         local cur="${COMP_WORDS[COMP_CWORD]}"
208         case "$cur" in
209         --pretty=*)
210                 _tigcomp "
211                         oneline short medium full fuller email raw
212                         " "" "${cur##--pretty=}"
213                 return
214                 ;;
215         --*)
216                 _tigcomp "--pretty="
217                 return
218                 ;;
219         esac
220         __tig_complete_file
221 }
222
223 _tig ()
224 {
225         local i c=1 command __tig_dir
226
227         while [ $c -lt $COMP_CWORD ]; do
228                 i="${COMP_WORDS[c]}"
229                 case "$i" in
230                 --) command="log"; break;;
231                 -*) ;;
232                 *) command="$i"; break ;;
233                 esac
234                 c=$((++c))
235         done
236
237         if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
238                 case "${COMP_WORDS[COMP_CWORD]}" in
239                 --*=*) COMPREPLY=() ;;
240                 -*)   _tig_options ;;
241                 *)    _tigcomp "blame status show $(__tig_refs)" ;;
242                 esac
243                 return
244         fi
245
246         case "$command" in
247         blame)  _tig_blame ;;
248         show)   _tig_show ;;
249         status) ;;
250         *)      _tigcomp "
251                         $(__tig_complete_file)
252                         $(__tig_refs)
253                 " ;;
254         esac
255 }
256
257 complete -o default -o nospace -F _tig tig
258
259 # The following are necessary only for Cygwin, and only are needed
260 # when the user has tab-completed the executable name and consequently
261 # included the '.exe' suffix.
262 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
263 complete -o default -o nospace -F _tig tig.exe
264 fi