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