chiark / gitweb /
Add bash completion script for TopGit
[topgit.git] / contrib / tg-completion.bash
1 #
2 # bash completion support for TopGit.
3 #
4 # Copyright (C) 2008 Jonas Fonseca <fonseca@diku.dk>
5 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
6 # Based git's git-completion.sh: http://repo.or.cz/w/git/fastimport.git
7 # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
8 # Distributed under the GNU General Public License, version 2.0.
9 #
10 # The contained completion routines provide support for completing:
11 #
12 #    *) local and remote branch names
13 #    *) local and remote tag names
14 #    *) .git/remotes file names
15 #    *) git 'subcommands'
16 #    *) tree paths within 'ref:path/to/file' expressions
17 #    *) common --long-options
18 #
19 # To use these routines:
20 #
21 #    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
22 #    2) Added the following line to your .bashrc:
23 #        source ~/.git-completion.sh
24 #
25 #    3) You may want to make sure the git executable is available
26 #       in your PATH before this script is sourced, as some caching
27 #       is performed while the script loads.  If git isn't found
28 #       at source time then all lookups will be done on demand,
29 #       which may be slightly slower.
30 #
31 #    4) Consider changing your PS1 to also show the current branch:
32 #        PS1='[\u@\h \W$(__tg_ps1 " (%s)")]\$ '
33 #
34 #       The argument to __tg_ps1 will be displayed only if you
35 #       are currently in a git repository.  The %s token will be
36
37 case "$COMP_WORDBREAKS" in
38 *:*) : great ;;
39 *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
40 esac
41
42 ### {{{ Utilities
43
44 __tgdir ()
45 {
46         if [ -z "$1" ]; then
47                 if [ -n "$__tg_dir" ]; then
48                         echo "$__tg_dir"
49                 elif [ -d .git ]; then
50                         echo .git
51                 else
52                         git rev-parse --git-dir 2>/dev/null
53                 fi
54         elif [ -d "$1/.git" ]; then
55                 echo "$1/.git"
56         else
57                 echo "$1"
58         fi
59 }
60
61 __tgcomp_1 ()
62 {
63         local c IFS=' '$'\t'$'\n'
64         for c in $1; do
65                 case "$c$2" in
66                 --*=*) printf %s$'\n' "$c$2" ;;
67                 *.)    printf %s$'\n' "$c$2" ;;
68                 *)     printf %s$'\n' "$c$2 " ;;
69                 esac
70         done
71 }
72
73 __tgcomp ()
74 {
75         local cur="${COMP_WORDS[COMP_CWORD]}"
76         if [ $# -gt 2 ]; then
77                 cur="$3"
78         fi
79         case "$cur" in
80         --*=)
81                 COMPREPLY=()
82                 ;;
83         *)
84                 local IFS=$'\n'
85                 COMPREPLY=($(compgen -P "$2" \
86                         -W "$(__tgcomp_1 "$1" "$4")" \
87                         -- "$cur"))
88                 ;;
89         esac
90 }
91
92 __tg_heads ()
93 {
94         local cmd i is_hash=y dir="$(__tgdir "$1")"
95         if [ -d "$dir" ]; then
96                 git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
97                         refs/heads
98                 return
99         fi
100         for i in $(git ls-remote "$1" 2>/dev/null); do
101                 case "$is_hash,$i" in
102                 y,*) is_hash=n ;;
103                 n,*^{}) is_hash=y ;;
104                 n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
105                 n,*) is_hash=y; echo "$i" ;;
106                 esac
107         done
108 }
109
110 __tg_refs ()
111 {
112         local cmd i is_hash=y dir="$(__tgdir "$1")"
113         if [ -d "$dir" ]; then
114                 if [ -e "$dir/HEAD" ]; then echo HEAD; fi
115                 git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
116                         refs/tags refs/heads refs/remotes
117                 return
118         fi
119         for i in $(git ls-remote "$dir" 2>/dev/null); do
120                 case "$is_hash,$i" in
121                 y,*) is_hash=n ;;
122                 n,*^{}) is_hash=y ;;
123                 n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
124                 n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
125                 n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
126                 n,*) is_hash=y; echo "$i" ;;
127                 esac
128         done
129 }
130
131 __tg_refs2 ()
132 {
133         local i
134         for i in $(__tg_refs "$1"); do
135                 echo "$i:$i"
136         done
137 }
138
139 __tg_refs_remotes ()
140 {
141         local cmd i is_hash=y
142         for i in $(git ls-remote "$1" 2>/dev/null); do
143                 case "$is_hash,$i" in
144                 n,refs/heads/*)
145                         is_hash=y
146                         echo "$i:refs/remotes/$1/${i#refs/heads/}"
147                         ;;
148                 y,*) is_hash=n ;;
149                 n,*^{}) is_hash=y ;;
150                 n,refs/tags/*) is_hash=y;;
151                 n,*) is_hash=y; ;;
152                 esac
153         done
154 }
155
156 __tg_remotes ()
157 {
158         local i ngoff IFS=$'\n' d="$(__tgdir)"
159         shopt -q nullglob || ngoff=1
160         shopt -s nullglob
161         for i in "$d/remotes"/*; do
162                 echo ${i#$d/remotes/}
163         done
164         [ "$ngoff" ] && shopt -u nullglob
165         for i in $(git --git-dir="$d" config --list); do
166                 case "$i" in
167                 remote.*.url=*)
168                         i="${i#remote.}"
169                         echo "${i/.url=*/}"
170                         ;;
171                 esac
172         done
173 }
174
175 __tg_complete_revlist ()
176 {
177         local pfx cur="${COMP_WORDS[COMP_CWORD]}"
178         case "$cur" in
179         *...*)
180                 pfx="${cur%...*}..."
181                 cur="${cur#*...}"
182                 __tgcomp "$(__tg_refs)" "$pfx" "$cur"
183                 ;;
184         *..*)
185                 pfx="${cur%..*}.."
186                 cur="${cur#*..}"
187                 __tgcomp "$(__tg_refs)" "$pfx" "$cur"
188                 ;;
189         *)
190                 __tgcomp "$(__tg_refs)"
191                 ;;
192         esac
193 }
194
195 __tg_topics ()
196 {
197         tg summary | cut -f 2
198 }
199
200 __tg_commands ()
201 {
202         if [ -n "$__tg_all_commandlist" ]; then
203                 echo "$__tg_all_commandlist"
204                 return
205         fi
206         local i IFS=" "$'\n'
207         for i in $(tg help | sed -n 's/^Usage:.*(\(.*\)).*/\1/p' | tr '|' ' ')
208         do
209                 case $i in
210                 *--*)             : helper pattern;;
211                 *) echo $i;;
212                 esac
213         done
214 }
215 __tg_all_commandlist=
216 __tg_all_commandlist="$(__tg_commands 2>/dev/null)"
217
218 __tg_complete_arg ()
219 {
220         if [ $COMP_CWORD -gt 2 ] && [ "${COMP_WORDS[$COMP_CWORD - 1]}" = "$1" ]; then
221                 return 0
222         fi
223         return 1
224 }
225
226 ### }}}
227 ### {{{ Commands
228
229 _tg_create ()
230 {
231         local cmd="$1"
232         local cur="${COMP_WORDS[COMP_CWORD]}"
233
234         # Name must be the first arg after the create command
235         if [ $((cmd + 1)) = $COMP_CWORD ]; then
236                 __tgcomp "$(__tg_topics)"
237                 return
238         fi
239
240         __tg_complete_arg "-r" && {
241                 __tgcomp "$(__tg_remotes)"
242                 return
243         }
244
245         case "$cur" in
246         -*)
247                 __tgcomp "
248                         -r
249                 "
250                 ;;
251         *)
252                 __tgcomp "$(__tg_refs)"
253         esac
254 }
255
256 _tg_delete ()
257 {
258         local cur="${COMP_WORDS[COMP_CWORD]}"
259
260         case "$cur" in
261         -*)
262                 __tgcomp "
263                         -f
264                 "
265                 ;;
266         *)
267                 __tgcomp "$(__tg_topics)"
268         esac
269 }
270
271 _tg_export ()
272 {
273         local cur="${COMP_WORDS[COMP_CWORD]}"
274
275         __tg_complete_arg "--collapse" && {
276                 __tgcomp "$(__tg_heads)"
277                 return
278         }
279
280         __tg_complete_arg "--quilt" && {
281                 return
282         }
283
284         case "$cur" in
285         *)
286                 __tgcomp "
287                         --collapse
288                         --quilt
289                 "
290         esac
291 }
292
293 _tg_help ()
294 {
295         local cur="${COMP_WORDS[COMP_CWORD]}"
296         case "$cur" in
297         -*)
298                 COMPREPLY=()
299                 return
300                 ;;
301         esac
302         __tgcomp "$(__tg_commands)"
303 }
304
305 _tg_import ()
306 {
307         local cur="${COMP_WORDS[COMP_CWORD]}"
308
309         __tg_complete_arg "-p" && {
310                 COMPREPLY=()
311                 return
312         }
313
314         case "$cur" in
315         -*)
316                 __tgcomp "
317                         -p
318                 "
319                 ;;
320         *)
321                 __tg_complete_revlist
322         esac
323 }
324
325 _tg_info ()
326 {
327         local cur="${COMP_WORDS[COMP_CWORD]}"
328
329         case "$cur" in
330         *)
331                 __tgcomp "$(__tg_topics)"
332         esac
333 }
334
335 _tg_mail ()
336 {
337         local cur="${COMP_WORDS[COMP_CWORD]}"
338
339         case "$cur" in
340         *)
341                 __tgcomp "$(__tg_topics)"
342         esac
343 }
344
345 _tg_patch ()
346 {
347         local cur="${COMP_WORDS[COMP_CWORD]}"
348
349         case "$cur" in
350         *)
351                 __tgcomp "$(__tg_topics)"
352         esac
353 }
354
355 _tg_remote ()
356 {
357         local cur="${COMP_WORDS[COMP_CWORD]}"
358
359         case "$cur" in
360         *)
361                 __tgcomp "$(__tg_remotes)"
362         esac
363 }
364
365 _tg_summary ()
366 {
367         COMPREPLY=()
368 }
369
370 _tg_update ()
371 {
372         COMPREPLY=()
373 }
374
375 ### }}}
376 ### {{{ tg completion
377
378 _tg ()
379 {
380         local i c=1 command __tg_dir
381
382         while [ $c -lt $COMP_CWORD ]; do
383                 i="${COMP_WORDS[c]}"
384                 case "$i" in
385                 -r) 
386                         c=$((++c))
387                         if [ $c -lt $COMP_CWORD ]; then
388                                 __tgcomp "$(__tg_remotes)"
389                                 return
390                         fi
391                         ;;
392                 -h|--help) command="help"; break ;;
393                 *) command="$i"; break ;;
394                 esac
395                 c=$((++c))
396         done
397
398         if [ -z "$command" ]; then
399                 case "${COMP_WORDS[COMP_CWORD]}" in
400                 -*)     __tgcomp "
401                                 -r
402                                 -h
403                                 --help
404                         "
405                         ;;
406                 *)     __tgcomp "$(__tg_commands)" ;;
407                 esac
408                 return
409         fi
410
411         case "$command" in
412         create)      _tg_create "$c" ;;
413         delete)      _tg_delete ;;
414         export)      _tg_export ;;
415         help)        _tg_help ;;
416         import)      _tg_import ;;
417         info)        _tg_info ;;
418         mail)        _tg_mail ;;
419         patch)       _tg_patch ;;
420         remote)      _tg_remote ;;
421         summary)     _tg_summary ;;
422         update)      _tg_update ;;
423         *)           COMPREPLY=() ;;
424         esac
425 }
426
427 ### }}}
428
429         __tgcomp "$(__tg_refs top-bases)"
430 complete -o default -o nospace -F _tg tg
431
432 # The following are necessary only for Cygwin, and only are needed
433 # when the user has tab-completed the executable name and consequently
434 # included the '.exe' suffix.
435 #
436 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
437 complete -o default -o nospace -F _tg tg.exe
438 fi