Make bash completion fail to bashdefault before default completion.
[stgit] / stgit / completion.py
CommitLineData
6c8a90e1
KH
1import textwrap
2import stgit.commands
3from stgit import argparse
051e292e 4import itertools
6c8a90e1
KH
5
6def fun(name, *body):
7 return ['%s ()' % name, '{', list(body), '}']
8
9def fun_desc(name, desc, *body):
10 return ['# %s' % desc] + fun(name, *body)
11
12def flatten(stuff, sep):
13 r = stuff[0]
14 for s in stuff[1:]:
15 r.append(sep)
16 r.extend(s)
17 return r
18
19def write(f, stuff, indent = 0):
20 for s in stuff:
21 if isinstance(s, str):
22 f.write((' '*4*indent + s).rstrip() + '\n')
23 else:
24 write(f, s, indent + 1)
25
26def patch_list_fun(type):
27 return fun('_%s_patches' % type, 'local g=$(_gitdir)',
28 'test "$g" && cat "$g/patches/$(_current_branch)/%s"' % type)
29
30def file_list_fun(name, cmd):
31 return fun('_%s_files' % name, 'local g=$(_gitdir)',
32 'test "$g" && %s' % cmd)
33
34def ref_list_fun(name, prefix):
35 return fun(name, 'local g=$(_gitdir)',
36 ("test \"$g\" && git show-ref | grep ' %s/' | sed 's,.* %s/,,'"
37 % (prefix, prefix)))
38
39def util():
40 r = [fun_desc('_gitdir',
41 "The path to .git, or empty if we're not in a repository.",
42 'echo "$(git rev-parse --git-dir 2>/dev/null)"'),
43 fun_desc('_current_branch',
44 "Name of the current branch, or empty if there isn't one.",
45 'local b=$(git symbolic-ref HEAD 2>/dev/null)',
46 'echo ${b#refs/heads/}'),
47 fun_desc('_other_applied_patches',
48 'List of all applied patches except the current patch.',
49 'local b=$(_current_branch)',
50 'local g=$(_gitdir)',
51 ('test "$g" && cat "$g/patches/$b/applied" | grep -v'
52 ' "^$(tail -n 1 $g/patches/$b/applied 2> /dev/null)$"')),
53 fun('_patch_range', 'local patches="$1"', 'local cur="$2"',
54 'case "$cur" in', [
55 '*..*)', ['local pfx="${cur%..*}.."', 'cur="${cur#*..}"',
56 'compgen -P "$pfx" -W "$patches" -- "$cur"', ';;'],
57 '*)', ['compgen -W "$patches" -- "$cur"', ';;']],
58 'esac'),
59 fun('_stg_branches',
60 'local g=$(_gitdir)', 'test "$g" && (cd $g/patches/ && echo *)'),
61 ref_list_fun('_all_branches', 'refs/heads'),
62 ref_list_fun('_tags', 'refs/tags'),
63 ref_list_fun('_remotes', 'refs/remotes')]
64 for type in ['applied', 'unapplied', 'hidden']:
65 r.append(patch_list_fun(type))
66 for name, cmd in [('conflicting',
67 r"git ls-files --unmerged | sed 's/.*\t//g' | sort -u"),
68 ('dirty', 'git diff-index --name-only HEAD'),
69 ('unknown', 'git ls-files --others --exclude-standard'),
70 ('known', 'git ls-files')]:
71 r.append(file_list_fun(name, cmd))
72 return flatten(r, '')
73
74def command_list(commands):
75 return ['_stg_commands="%s"\n' % ' '.join(sorted(commands.iterkeys()))]
76
77def command_fun(cmd, modname):
78 mod = stgit.commands.get_command(modname)
79 def cg(args, flags):
80 return argparse.compjoin(list(args) + [argparse.strings(*flags)]
81 ).command('$cur')
82 return fun(
83 '_stg_%s' % cmd,
84 'local flags="%s"' % ' '.join(sorted(
051e292e
GH
85 itertools.chain(
86 ('--help',),
87 (flag for opt in mod.options
88 for flag in opt.flags if flag.startswith('--'))))),
6c8a90e1
KH
89 'local prev="${COMP_WORDS[COMP_CWORD-1]}"',
90 'local cur="${COMP_WORDS[COMP_CWORD]}"',
91 'case "$prev" in', [
92 '%s) COMPREPLY=($(%s)) ;;' % ('|'.join(opt.flags), cg(opt.args, []))
93 for opt in mod.options if opt.args] + [
94 '*) COMPREPLY=($(%s)) ;;' % cg(mod.args, ['$flags'])],
95 'esac')
96
97def main_switch(commands):
98 return fun(
99 '_stg',
100 'local i',
101 'local c=1',
102 'local command',
103 '',
104 'while test $c -lt $COMP_CWORD; do', [
105 'if test $c == 1; then', [
106 'command="${COMP_WORDS[c]}"'],
107 'fi',
108 'c=$((++c))'],
109 'done',
110 '',
111 ('# Complete name of subcommand if the user has not finished'
112 ' typing it yet.'),
113 'if test $c -eq $COMP_CWORD -a -z "$command"; then', [
e22e6f48 114 ('COMPREPLY=($(compgen -W "help version copyright $_stg_commands" --'
6c8a90e1
KH
115 ' "${COMP_WORDS[COMP_CWORD]}"))'),
116 'return'],
117 'fi',
118 '',
119 '# Complete arguments to subcommands.',
120 'case "$command" in', [
e22e6f48
TP
121 'help) ', [
122 ('COMPREPLY=($(compgen -W "$_stg_commands" --'
123 ' "${COMP_WORDS[COMP_CWORD]}"))'),
124 'return ;;'],
125 'version) return ;;',
126 'copyright) return ;;'], [
6c8a90e1
KH
127 '%s) _stg_%s ;;' % (cmd, cmd)
128 for cmd in sorted(commands.iterkeys())],
129 'esac')
130
131def install():
e799c7fa
TP
132 return ['complete -o bashdefault -o default -F _stg stg 2>/dev/null \\', [
133 '|| complete -o default -F _stg stg' ] ]
6c8a90e1
KH
134
135def write_completion(f):
136 commands = stgit.commands.get_commands(allow_cached = False)
137 r = [["""# -*- shell-script -*-
138# bash completion script for StGit (automatically generated)
139#
140# To use these routines:
141#
142# 1. Copy this file to somewhere (e.g. ~/.stgit-completion.bash).
143#
144# 2. Add the following line to your .bashrc:
145# . ~/.stgit-completion.bash"""]]
146 r += [util(), command_list(commands)]
147 for cmd, (modname, _, _) in sorted(commands.iteritems()):
148 r.append(command_fun(cmd, modname))
149 r += [main_switch(commands), install()]
150 write(f, flatten(r, ''))