##// END OF EJS Templates
merge
Ville M. Vainio -
r1085:5e58c777 merge
parent child Browse files
Show More
@@ -1,334 +1,343 b''
1 """ Extension for bzr command tab completer. Supports comlpeting commands and options
2
3 Unlike the core IPython, you should note that this extension is under GPL, not BSD.
4
5 Based on "shell" bzr plugin by Aaron Bentley, license is below. The IPython additions
6 are at the bottom of the file, the rest is left untouched.
7
8 Must be loaded with ip.load('ipy_bzr')
9
10 """
11 a
1 12 # Copyright (C) 2004, 2005 Aaron Bentley
2 13 # <aaron@aaronbentley.com>
3 14 #
4 15 # This program is free software; you can redistribute it and/or modify
5 16 # it under the terms of the GNU General Public License as published by
6 17 # the Free Software Foundation; either version 2 of the License, or
7 18 # (at your option) any later version.
8 19 #
9 20 # This program is distributed in the hope that it will be useful,
10 21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 23 # GNU General Public License for more details.
13 24 #
14 25 # You should have received a copy of the GNU General Public License
15 26 # along with this program; if not, write to the Free Software
16 27 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 28
18 29 import cmd
19 30 from itertools import chain
20 31 import os
21 32 import shlex
22 33 import stat
23 34 import string
24 35 import sys
25 36
26 37 from bzrlib import osutils
27 38 from bzrlib.branch import Branch
28 39 from bzrlib.config import config_dir, ensure_config_dir_exists
29 40 from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
30 41 from bzrlib.errors import BzrError
31 42 from bzrlib.workingtree import WorkingTree
32
43 import bzrlib.plugin
33 44
34 45
35 46 SHELL_BLACKLIST = set(['rm', 'ls'])
36 47 COMPLETION_BLACKLIST = set(['shell'])
37 48
38 49
39 50 class BlackListedCommand(BzrError):
40 51 def __init__(self, command):
41 52 BzrError.__init__(self, "The command %s is blacklisted for shell use" %
42 53 command)
43 54
44 55
45 56 class CompletionContext(object):
46 57 def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
47 58 self.text = text
48 59 self.command = command
49 60 self.prev_opt = prev_opt
50 61 self.arg_pos = None
51 62
52 63 def get_completions(self):
53 64 try:
54 65 return self.get_completions_or_raise()
55 66 except Exception, e:
56 67 print e, type(e)
57 68 return []
58 69
59 70 def get_option_completions(self):
60 71 try:
61 72 command_obj = get_cmd_object(self.command)
62 73 except BzrError:
63 74 return []
64 75 opts = [o+" " for o in iter_opt_completions(command_obj)]
65 76 return list(filter_completions(opts, self.text))
66 77
67 78 def get_completions_or_raise(self):
68 79 if self.command is None:
69 80 if '/' in self.text:
70 81 iter = iter_executables(self.text)
71 82 else:
72 83 iter = (c+" " for c in iter_command_names() if
73 84 c not in COMPLETION_BLACKLIST)
74 85 return list(filter_completions(iter, self.text))
75 86 if self.prev_opt is None:
76 87 completions = self.get_option_completions()
77 88 if self.command == "cd":
78 89 iter = iter_dir_completions(self.text)
79 90 completions.extend(list(filter_completions(iter, self.text)))
80 91 else:
81 92 iter = iter_file_completions(self.text)
82 93 completions.extend(filter_completions(iter, self.text))
83 94 return completions
84 95
85 96
86 97 class PromptCmd(cmd.Cmd):
87 98
88 99 def __init__(self):
89 100 cmd.Cmd.__init__(self)
90 101 self.prompt = "bzr> "
91 102 try:
92 103 self.tree = WorkingTree.open_containing('.')[0]
93 104 except:
94 105 self.tree = None
95 106 self.set_title()
96 107 self.set_prompt()
97 108 self.identchars += '-'
98 109 ensure_config_dir_exists()
99 110 self.history_file = osutils.pathjoin(config_dir(), 'shell-history')
100 111 readline.set_completer_delims(string.whitespace)
101 112 if os.access(self.history_file, os.R_OK) and \
102 113 os.path.isfile(self.history_file):
103 114 readline.read_history_file(self.history_file)
104 115 self.cwd = os.getcwd()
105 116
106 117 def write_history(self):
107 118 readline.write_history_file(self.history_file)
108 119
109 120 def do_quit(self, args):
110 121 self.write_history()
111 122 raise StopIteration
112 123
113 124 def do_exit(self, args):
114 125 self.do_quit(args)
115 126
116 127 def do_EOF(self, args):
117 128 print
118 129 self.do_quit(args)
119 130
120 131 def postcmd(self, line, bar):
121 132 self.set_title()
122 133 self.set_prompt()
123 134
124 135 def set_prompt(self):
125 136 if self.tree is not None:
126 137 try:
127 138 prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
128 139 self.tree.relpath('.'))
129 140 prompt = " %s:%d/%s" % prompt_data
130 141 except:
131 142 prompt = ""
132 143 else:
133 144 prompt = ""
134 145 self.prompt = "bzr%s> " % prompt
135 146
136 147 def set_title(self, command=None):
137 148 try:
138 149 b = Branch.open_containing('.')[0]
139 150 version = "%s:%d" % (b.nick, b.revno())
140 151 except:
141 152 version = "[no version]"
142 153 if command is None:
143 154 command = ""
144 155 sys.stdout.write(terminal.term_title("bzr %s %s" % (command, version)))
145 156
146 157 def do_cd(self, line):
147 158 if line == "":
148 159 line = "~"
149 160 line = os.path.expanduser(line)
150 161 if os.path.isabs(line):
151 162 newcwd = line
152 163 else:
153 164 newcwd = self.cwd+'/'+line
154 165 newcwd = os.path.normpath(newcwd)
155 166 try:
156 167 os.chdir(newcwd)
157 168 self.cwd = newcwd
158 169 except Exception, e:
159 170 print e
160 171 try:
161 172 self.tree = WorkingTree.open_containing(".")[0]
162 173 except:
163 174 self.tree = None
164 175
165 176 def do_help(self, line):
166 177 self.default("help "+line)
167 178
168 179 def default(self, line):
169 180 args = shlex.split(line)
170 181 alias_args = get_alias(args[0])
171 182 if alias_args is not None:
172 183 args[0] = alias_args.pop(0)
173 184
174 185 commandname = args.pop(0)
175 186 for char in ('|', '<', '>'):
176 187 commandname = commandname.split(char)[0]
177 188 if commandname[-1] in ('|', '<', '>'):
178 189 commandname = commandname[:-1]
179 190 try:
180 191 if commandname in SHELL_BLACKLIST:
181 192 raise BlackListedCommand(commandname)
182 193 cmd_obj = get_cmd_object(commandname)
183 194 except (BlackListedCommand, BzrError):
184 195 return os.system(line)
185 196
186 197 try:
187 198 if too_complicated(line):
188 199 return os.system("bzr "+line)
189 200 else:
190 201 return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
191 202 except BzrError, e:
192 203 print e
193 204 except KeyboardInterrupt, e:
194 205 print "Interrupted"
195 206 except Exception, e:
196 207 # print "Unhandled error:\n%s" % errors.exception_str(e)
197 208 print "Unhandled error:\n%s" % (e)
198 209
199 210
200 211 def completenames(self, text, line, begidx, endidx):
201 212 return CompletionContext(text).get_completions()
202 213
203 214 def completedefault(self, text, line, begidx, endidx):
204 215 """Perform completion for native commands.
205 216
206 217 :param text: The text to complete
207 218 :type text: str
208 219 :param line: The entire line to complete
209 220 :type line: str
210 221 :param begidx: The start of the text in the line
211 222 :type begidx: int
212 223 :param endidx: The end of the text in the line
213 224 :type endidx: int
214 225 """
215 226 (cmd, args, foo) = self.parseline(line)
216 227 if cmd == "bzr":
217 228 cmd = None
218 229 return CompletionContext(text, command=cmd).get_completions()
219 230
220 231
221 232 def run_shell():
222 233 try:
223 234 prompt = PromptCmd()
224 235 try:
225 236 prompt.cmdloop()
226 237 finally:
227 238 prompt.write_history()
228 239 except StopIteration:
229 240 pass
230 241
231 242
232 243 def iter_opt_completions(command_obj):
233 244 for option_name, option in command_obj.options().items():
234 245 yield "--" + option_name
235 246 short_name = option.short_name()
236 247 if short_name:
237 248 yield "-" + short_name
238 249
239 250
240 251 def iter_file_completions(arg, only_dirs = False):
241 252 """Generate an iterator that iterates through filename completions.
242 253
243 254 :param arg: The filename fragment to match
244 255 :type arg: str
245 256 :param only_dirs: If true, match only directories
246 257 :type only_dirs: bool
247 258 """
248 259 cwd = os.getcwd()
249 260 if cwd != "/":
250 261 extras = [".", ".."]
251 262 else:
252 263 extras = []
253 264 (dir, file) = os.path.split(arg)
254 265 if dir != "":
255 266 listingdir = os.path.expanduser(dir)
256 267 else:
257 268 listingdir = cwd
258 269 for file in chain(os.listdir(listingdir), extras):
259 270 if dir != "":
260 271 userfile = dir+'/'+file
261 272 else:
262 273 userfile = file
263 274 if userfile.startswith(arg):
264 275 if os.path.isdir(listingdir+'/'+file):
265 276 userfile+='/'
266 277 yield userfile
267 278 elif not only_dirs:
268 279 yield userfile + ' '
269 280
270 281
271 282 def iter_dir_completions(arg):
272 283 """Generate an iterator that iterates through directory name completions.
273 284
274 285 :param arg: The directory name fragment to match
275 286 :type arg: str
276 287 """
277 288 return iter_file_completions(arg, True)
278 289
279 290
280 291 def iter_command_names(hidden=False):
281 292 for real_cmd_name, cmd_class in get_all_cmds():
282 293 if not hidden and cmd_class.hidden:
283 294 continue
284 295 for name in [real_cmd_name] + cmd_class.aliases:
285 296 # Don't complete on aliases that are prefixes of the canonical name
286 297 if name == real_cmd_name or not real_cmd_name.startswith(name):
287 298 yield name
288 299
289 300
290 301 def iter_executables(path):
291 302 dirname, partial = os.path.split(path)
292 303 for filename in os.listdir(dirname):
293 304 if not filename.startswith(partial):
294 305 continue
295 306 fullpath = os.path.join(dirname, filename)
296 307 mode=os.lstat(fullpath)[stat.ST_MODE]
297 308 if stat.S_ISREG(mode) and 0111 & mode:
298 309 yield fullpath + ' '
299 310
300 311
301 312 def filter_completions(iter, arg):
302 313 return (c for c in iter if c.startswith(arg))
303 314
304 315
305 316 def iter_munged_completions(iter, arg, text):
306 317 for completion in iter:
307 318 completion = str(completion)
308 319 if completion.startswith(arg):
309 320 yield completion[len(arg)-len(text):]+" "
310 321
311 322
312 323 def too_complicated(line):
313 324 for char in '|<>*?':
314 325 if char in line:
315 326 return True
316 327 return False
317 328
318 329
319 330 ### IPython mods start
320 331
321 332 def init_ipython(ip):
322 333 def bzr_completer(self,ev):
323 334 #print "bzr complete"
324 335 tup = ev.line.split(None,2)
325 336 if len(tup) > 2:
326 337 cmd = tup[1]
327 338 else:
328 339 cmd = None
329 340
330 341 return CompletionContext(ev.symbol, command = cmd).get_completions()
342 bzrlib.plugin.load_plugins()
331 343 ip.set_hook('complete_command', bzr_completer, str_key = 'bzr')
332
333
334
General Comments 0
You need to be logged in to leave comments. Login now