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