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
19
from itertools import chain
26
from bzrlib.branch import Branch
27
from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
28
from bzrlib.errors import BzrError
33
SHELL_BLACKLIST = set(['rm', 'ls'])
34
COMPLETION_BLACKLIST = set(['shell'])
37
class BlackListedCommand(BzrError):
38
def __init__(self, command):
39
BzrError.__init__(self, "The command %s is blacklisted for shell use" %
43
class CompletionContext(object):
44
def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
46
self.command = command
47
self.prev_opt = prev_opt
50
def get_completions(self):
52
return self.get_completions_or_raise()
57
def get_option_completions(self):
59
command_obj = get_cmd_object(self.command)
62
opts = [o+" " for o in iter_opt_completions(command_obj)]
63
return list(filter_completions(opts, self.text))
65
def get_completions_or_raise(self):
66
if self.command is None:
67
iter = (c+" " for c in iter_command_names() if
68
c not in COMPLETION_BLACKLIST)
69
return list(filter_completions(iter, self.text))
70
if self.prev_opt is None:
71
completions = self.get_option_completions()
72
if self.command == "cd":
73
iter = iter_dir_completions(self.text)
74
completions.extend(list(filter_completions(iter, self.text)))
76
iter = iter_file_completions(self.text)
77
completions.extend([f+" " for f in
78
filter_completions(iter, self.text)])
82
class PromptCmd(cmd.Cmd):
84
cmd.Cmd.__init__(self)
87
self.tree = WorkingTree.open_containing('.')[0]
92
self.identchars += '-'
93
self.history_file = os.path.expanduser("~/.bazaar/shell-history")
94
readline.set_completer_delims(string.whitespace)
95
if os.access(self.history_file, os.R_OK) and \
96
os.path.isfile(self.history_file):
97
readline.read_history_file(self.history_file)
98
self.cwd = os.getcwd()
100
def write_history(self):
101
readline.write_history_file(self.history_file)
103
def do_quit(self, args):
107
def do_exit(self, args):
110
def do_EOF(self, args):
114
def postcmd(self, line, bar):
118
def set_prompt(self):
119
if self.tree is not None:
121
prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
122
self.tree.branch.relpath('.'))
123
prompt = " %s:%d/%s" % prompt_data
128
self.prompt = "bzr%s> " % prompt
130
def set_title(self, command=None):
132
b = Branch.open_containing('.')[0]
133
version = "%s:%d" % (b.nick, b.revno())
135
version = "[no version]"
138
sys.stdout.write(terminal.term_title("bzr %s %s" % (command, version)))
140
def do_cd(self, line):
143
line = os.path.expanduser(line)
144
if os.path.isabs(line):
147
newcwd = self.cwd+'/'+line
148
newcwd = os.path.normpath(newcwd)
155
self.tree = WorkingTree.open_containing(".")[0]
159
def do_help(self, line):
160
self.default("help "+line)
162
def default(self, line):
163
args = shlex.split(line)
164
alias_args = get_alias(args[0])
165
if alias_args is not None:
166
args[0] = alias_args.pop(0)
168
commandname = args.pop(0)
169
for char in ('|', '<', '>'):
170
commandname = commandname.split(char)[0]
171
if commandname[-1] in ('|', '<', '>'):
172
commandname = commandname[:-1]
174
if commandname in SHELL_BLACKLIST:
175
raise BlackListedCommand(commandname)
176
cmd_obj = get_cmd_object(commandname)
177
except (BlackListedCommand, BzrError):
178
return os.system(line)
181
if too_complicated(line):
182
return os.system("bzr "+line)
184
return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
187
except KeyboardInterrupt, e:
190
# print "Unhandled error:\n%s" % errors.exception_str(e)
191
print "Unhandled error:\n%s" % (e)
194
def completenames(self, text, line, begidx, endidx):
195
return CompletionContext(text).get_completions()
197
def completedefault(self, text, line, begidx, endidx):
198
"""Perform completion for native commands.
200
:param text: The text to complete
202
:param line: The entire line to complete
204
:param begidx: The start of the text in the line
206
:param endidx: The end of the text in the line
209
(cmd, args, foo) = self.parseline(line)
212
return CompletionContext(text, command=cmd).get_completions()
221
prompt.write_history()
222
except StopIteration:
226
def iter_opt_completions(command_obj):
227
for option_name, option in command_obj.options().items():
228
yield "--" + option_name
229
short_name = option.short_name()
231
yield "-" + short_name
234
def iter_file_completions(arg, only_dirs = False):
235
"""Generate an iterator that iterates through filename completions.
237
:param arg: The filename fragment to match
239
:param only_dirs: If true, match only directories
240
:type only_dirs: bool
247
(dir, file) = os.path.split(arg)
249
listingdir = os.path.expanduser(dir)
252
for file in chain(os.listdir(listingdir), extras):
254
userfile = dir+'/'+file
257
if userfile.startswith(arg):
258
if os.path.isdir(listingdir+'/'+file):
265
def iter_dir_completions(arg):
266
"""Generate an iterator that iterates through directory name completions.
268
:param arg: The directory name fragment to match
271
return iter_file_completions(arg, True)
274
def iter_command_names(hidden=False):
275
for real_cmd_name, cmd_class in get_all_cmds():
276
if not hidden and cmd_class.hidden:
278
for name in [real_cmd_name] + cmd_class.aliases:
279
# Don't complete on aliases that are prefixes of the canonical name
280
if name == real_cmd_name or not real_cmd_name.startswith(name):
284
def filter_completions(iter, arg):
285
return (c for c in iter if c.startswith(arg))
288
def iter_munged_completions(iter, arg, text):
289
for completion in iter:
290
completion = str(completion)
291
if completion.startswith(arg):
292
yield completion[len(arg)-len(text):]+" "
295
def too_complicated(line):