Commit | Line | Data |
---|---|---|
6c8a90e1 KH |
1 | import textwrap |
2 | import stgit.commands | |
3 | from stgit import argparse | |
051e292e | 4 | import itertools |
6c8a90e1 KH |
5 | |
6 | def fun(name, *body): | |
7 | return ['%s ()' % name, '{', list(body), '}'] | |
8 | ||
9 | def fun_desc(name, desc, *body): | |
10 | return ['# %s' % desc] + fun(name, *body) | |
11 | ||
12 | def 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 | ||
19 | def 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 | ||
26 | def patch_list_fun(type): | |
27 | return fun('_%s_patches' % type, 'local g=$(_gitdir)', | |
28 | 'test "$g" && cat "$g/patches/$(_current_branch)/%s"' % type) | |
29 | ||
30 | def file_list_fun(name, cmd): | |
31 | return fun('_%s_files' % name, 'local g=$(_gitdir)', | |
32 | 'test "$g" && %s' % cmd) | |
33 | ||
34 | def 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 | ||
39 | def 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 | ||
74 | def command_list(commands): | |
75 | return ['_stg_commands="%s"\n' % ' '.join(sorted(commands.iterkeys()))] | |
76 | ||
77 | def 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 | ||
97 | def 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 | ||
131 | def 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 | |
135 | def 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, '')) |