1
# Copyright (C) 2004, 2005 Aaron Bentley
2
# <aaron.bentley@utoronto.ca>
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.
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.
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
23
from itertools import chain
24
from bzrlib.errors import BzrError
25
from bzrlib.commands import get_cmd_object, get_all_cmds
27
SHELL_BLACKLIST = set(['rm', 'ls'])
28
COMPLETION_BLACKLIST = set(['shell'])
30
class BlackListedCommand(BzrError):
31
def __init__(self, command):
32
BzrError.__init__(self, "The command %s is blacklisted for shell use" %
35
class CompletionContext(object):
36
def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
38
self.command = command
39
self.prev_opt = prev_opt
42
def get_completions(self):
44
iter = (c for c in iter_command_names() if
45
c not in COMPLETION_BLACKLIST)
47
iter = list(filter_completions(iter, text))
53
class PromptCmd(cmd.Cmd):
55
cmd.Cmd.__init__(self)
58
self.tree = arch.tree_root(".")
63
self.identchars += '-'
64
self.history_file = os.path.expanduser("~/.bazaar/shell-history")
65
readline.set_completer_delims(string.whitespace)
66
if os.access(self.history_file, os.R_OK) and \
67
os.path.isfile(self.history_file):
68
readline.read_history_file(self.history_file)
69
self.cwd = os.getcwd()
71
def write_history(self):
72
readline.write_history_file(self.history_file)
74
def do_quit(self, args):
78
def do_exit(self, args):
81
def do_EOF(self, args):
85
def postcmd(self, line, bar):
90
if self.tree is not None:
92
prompt = pylon.alias_or_version(self.tree.tree_version,
95
if prompt is not None:
96
prompt = " " + prompt +":"+ pylon.tree_cwd(self.tree)
101
self.prompt = "bzr%s> " % prompt
103
def set_title(self, command=None):
105
version = pylon.alias_or_version(self.tree.tree_version, self.tree,
108
version = "[no version]"
111
sys.stdout.write(terminal.term_title("bzr %s %s" % (command, version)))
113
def do_cd(self, line):
116
line = os.path.expanduser(line)
117
if os.path.isabs(line):
120
newcwd = self.cwd+'/'+line
121
newcwd = os.path.normpath(newcwd)
128
self.tree = arch.tree_root(".")
132
def do_help(self, line):
133
self.default("help "+line)
135
def default(self, line):
137
commandname = args.pop(0)
138
for char in ('|', '<', '>'):
139
commandname = commandname.split(char)[0]
140
if commandname[-1] in ('|', '<', '>'):
141
commandname = commandname[:-1]
143
if commandname in SHELL_BLACKLIST:
144
raise BlackListedCommand(commandname)
145
cmd_obj = get_cmd_object(commandname)
146
except (BlackListedCommand, BzrError):
147
return os.system(line)
150
if too_complicated(line):
151
return os.system("bzr "+line)
153
return (cmd_obj.run_argv(args) or 0)
156
except KeyboardInterrupt, e:
159
# print "Unhandled error:\n%s" % errors.exception_str(e)
160
print "Unhandled error:\n%s" % (e)
163
def completenames(self, text, line, begidx, endidx):
164
from bzrlib.trace import mutter
166
iter = (c for c in iter_command_names() if
167
c not in COMPLETION_BLACKLIST)
170
arg = line.split()[-1]
173
iter = filter_completions(iter, arg)
178
def completedefault(self, text, line, begidx, endidx):
179
"""Perform completion for native commands.
181
:param text: The text to complete
183
:param line: The entire line to complete
185
:param begidx: The start of the text in the line
187
:param endidx: The end of the text in the line
190
(cmd, args, foo) = self.parseline(line)
193
return self.completenames(text, line, begidx, endidx)
197
command_obj = get_cmd_object(cmd)
201
if command_obj is not None:
202
opts = list(iter_opt_completions(command_obj))
203
files = list(iter_file_completions(text))
204
return list(filter_completions(opts+files, text))
207
arg = args.split()[-1]
210
iter = iter_dir_completions(text)
211
iter = filter_completions(iter, text)
214
arg = args.split()[-1]
215
iter = iter_file_completions(arg)
216
return list(iter_munged_completions(iter, arg, text))
218
return self.completenames(text, line, begidx, endidx)
228
prompt.write_history()
229
except StopIteration:
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()
237
yield "-" + short_name
239
def iter_file_completions(arg, only_dirs = False):
240
"""Generate an iterator that iterates through filename completions.
242
:param arg: The filename fragment to match
244
:param only_dirs: If true, match only directories
245
:type only_dirs: bool
252
(dir, file) = os.path.split(arg)
254
listingdir = os.path.expanduser(dir)
257
for file in chain(os.listdir(listingdir), extras):
259
userfile = dir+'/'+file
262
if userfile.startswith(arg):
263
if os.path.isdir(listingdir+'/'+file):
270
def iter_dir_completions(arg):
271
"""Generate an iterator that iterates through directory name completions.
273
:param arg: The directory name fragment to match
276
return iter_file_completions(arg, True)
278
def iter_command_names(hidden=False):
279
for real_cmd_name, cmd_class in get_all_cmds():
280
if not hidden and cmd_class.hidden:
282
for name in [real_cmd_name] + cmd_class.aliases:
283
# Don't complete on aliases that are prefixes of the canonical name
284
if name == real_cmd_name or not real_cmd_name.startswith(name):
287
def filter_completions(iter, arg):
288
return (c for c in iter if c.startswith(arg))
290
def iter_munged_completions(iter, arg, text):
291
for completion in iter:
292
completion = str(completion)
293
if completion.startswith(arg):
294
yield completion[len(arg)-len(text):]+" "
296
def too_complicated(line):
297
for char in '|<>"\"*?':