~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2011-09-25 03:20:56 UTC
  • Revision ID: aaron@aaronbentley.com-20110925032056-o6c611su8gdueh10
Tags: release-2.4.1
Prepare for 2.4.1 release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2004, 2005 Aaron Bentley
2
 
# <aaron.bentley@utoronto.ca>
 
2
# <aaron@aaronbentley.com>
3
3
#
4
4
#    This program is free software; you can redistribute it and/or modify
5
5
#    it under the terms of the GNU General Public License as published by
14
14
#    You should have received a copy of the GNU General Public License
15
15
#    along with this program; if not, write to the Free Software
16
16
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
17
18
import cmd
18
 
import sys
 
19
from itertools import chain
19
20
import os
20
 
import terminal
21
 
import readline
 
21
try:
 
22
    import readline
 
23
except ImportError:
 
24
    _has_readline = False
 
25
else:
 
26
    _has_readline = True
 
27
import shlex
 
28
import stat
22
29
import string
23
 
from itertools import chain
 
30
import sys
 
31
 
 
32
from bzrlib import osutils, trace
 
33
from bzrlib.branch import Branch
 
34
from bzrlib.config import config_dir, ensure_config_dir_exists
 
35
from bzrlib.commands import get_cmd_object, all_command_names, get_alias
24
36
from bzrlib.errors import BzrError
25
 
from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
26
 
from bzrlib.branch import Branch
 
37
from bzrlib.workingtree import WorkingTree
 
38
 
 
39
import terminal
 
40
 
27
41
 
28
42
SHELL_BLACKLIST = set(['rm', 'ls'])
29
43
COMPLETION_BLACKLIST = set(['shell'])
30
44
 
 
45
 
31
46
class BlackListedCommand(BzrError):
32
47
    def __init__(self, command):
33
48
        BzrError.__init__(self, "The command %s is blacklisted for shell use" %
34
49
                          command)
35
50
 
 
51
 
36
52
class CompletionContext(object):
37
53
    def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
38
54
        self.text = text
57
73
 
58
74
    def get_completions_or_raise(self):
59
75
        if self.command is None:
60
 
            iter = (c+" " for c in iter_command_names() if
61
 
                    c not in COMPLETION_BLACKLIST)
 
76
            if '/' in self.text:
 
77
                iter = iter_executables(self.text)
 
78
            else:
 
79
                iter = (c+" " for c in iter_command_names() if
 
80
                        c not in COMPLETION_BLACKLIST)
62
81
            return list(filter_completions(iter, self.text))
63
82
        if self.prev_opt is None:
64
83
            completions = self.get_option_completions()
67
86
                completions.extend(list(filter_completions(iter, self.text)))
68
87
            else:
69
88
                iter = iter_file_completions(self.text)
70
 
                completions.extend([f+" " for f in 
71
 
                                    filter_completions(iter, self.text)])
72
 
            return completions 
 
89
                completions.extend(filter_completions(iter, self.text))
 
90
            return completions
73
91
 
74
92
 
75
93
class PromptCmd(cmd.Cmd):
 
94
 
76
95
    def __init__(self):
77
96
        cmd.Cmd.__init__(self)
78
97
        self.prompt = "bzr> "
83
102
        self.set_title()
84
103
        self.set_prompt()
85
104
        self.identchars += '-'
86
 
        self.history_file = os.path.expanduser("~/.bazaar/shell-history")
87
 
        readline.set_completer_delims(string.whitespace)
88
 
        if os.access(self.history_file, os.R_OK) and \
89
 
            os.path.isfile(self.history_file):
90
 
            readline.read_history_file(self.history_file)
 
105
        ensure_config_dir_exists()
 
106
        self.history_file = osutils.pathjoin(config_dir(), 'shell-history')
 
107
        whitespace = ''.join(c for c in string.whitespace if c < chr(127))
 
108
        if _has_readline:
 
109
            readline.set_completer_delims(whitespace)
 
110
            if os.access(self.history_file, os.R_OK) and \
 
111
                os.path.isfile(self.history_file):
 
112
                readline.read_history_file(self.history_file)
91
113
        self.cwd = os.getcwd()
92
114
 
93
115
    def write_history(self):
94
 
        readline.write_history_file(self.history_file)
 
116
        if _has_readline:
 
117
            readline.write_history_file(self.history_file)
95
118
 
96
119
    def do_quit(self, args):
97
120
        self.write_history()
111
134
    def set_prompt(self):
112
135
        if self.tree is not None:
113
136
            try:
114
 
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(), 
115
 
                               self.tree.branch.relpath('.'))
 
137
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
 
138
                               self.tree.relpath('.'))
116
139
                prompt = " %s:%d/%s" % prompt_data
117
140
            except:
118
141
                prompt = ""
153
176
        self.default("help "+line)
154
177
 
155
178
    def default(self, line):
156
 
        args = line.split()
 
179
        try:
 
180
            args = shlex.split(line)
 
181
        except ValueError, e:
 
182
            print 'Parse error:', e
 
183
            return
 
184
 
157
185
        alias_args = get_alias(args[0])
158
186
        if alias_args is not None:
159
187
            args[0] = alias_args.pop(0)
160
 
            
 
188
 
161
189
        commandname = args.pop(0)
162
190
        for char in ('|', '<', '>'):
163
191
            commandname = commandname.split(char)[0]
171
199
            return os.system(line)
172
200
 
173
201
        try:
174
 
            if too_complicated(line):
 
202
            is_qbzr = cmd_obj.__module__.startswith('bzrlib.plugins.qbzr.')
 
203
            if too_complicated(line) or is_qbzr:
175
204
                return os.system("bzr "+line)
176
205
            else:
177
206
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
178
207
        except BzrError, e:
 
208
            trace.log_exception_quietly()
179
209
            print e
180
210
        except KeyboardInterrupt, e:
181
211
            print "Interrupted"
182
212
        except Exception, e:
183
 
#            print "Unhandled error:\n%s" % errors.exception_str(e)
 
213
            trace.log_exception_quietly()
184
214
            print "Unhandled error:\n%s" % (e)
185
215
 
186
216
 
189
219
 
190
220
    def completedefault(self, text, line, begidx, endidx):
191
221
        """Perform completion for native commands.
192
 
        
 
222
 
193
223
        :param text: The text to complete
194
224
        :type text: str
195
225
        :param line: The entire line to complete
204
234
            cmd = None
205
235
        return CompletionContext(text, command=cmd).get_completions()
206
236
 
207
 
def run_shell():
 
237
 
 
238
def run_shell(directory=None):
208
239
    try:
 
240
        if not directory is None:
 
241
            os.chdir(directory)
209
242
        prompt = PromptCmd()
210
 
        try:
211
 
            prompt.cmdloop()
212
 
        finally:
213
 
            prompt.write_history()
 
243
        while True:
 
244
            try:
 
245
                try:
 
246
                    prompt.cmdloop()
 
247
                except KeyboardInterrupt:
 
248
                    print
 
249
            finally:
 
250
                prompt.write_history()
214
251
    except StopIteration:
215
252
        pass
216
253
 
 
254
 
217
255
def iter_opt_completions(command_obj):
218
256
    for option_name, option in command_obj.options().items():
219
257
        yield "--" + option_name
221
259
        if short_name:
222
260
            yield "-" + short_name
223
261
 
 
262
 
224
263
def iter_file_completions(arg, only_dirs = False):
225
264
    """Generate an iterator that iterates through filename completions.
226
265
 
249
288
                userfile+='/'
250
289
                yield userfile
251
290
            elif not only_dirs:
252
 
                yield userfile
 
291
                yield userfile + ' '
253
292
 
254
293
 
255
294
def iter_dir_completions(arg):
260
299
    """
261
300
    return iter_file_completions(arg, True)
262
301
 
 
302
 
263
303
def iter_command_names(hidden=False):
264
 
    for real_cmd_name, cmd_class in get_all_cmds():
265
 
        if not hidden and cmd_class.hidden:
 
304
    for real_cmd_name in all_command_names():
 
305
        cmd_obj = get_cmd_object(real_cmd_name)
 
306
        if not hidden and cmd_obj.hidden:
266
307
            continue
267
 
        for name in [real_cmd_name] + cmd_class.aliases:
 
308
        for name in [real_cmd_name] + cmd_obj.aliases:
268
309
            # Don't complete on aliases that are prefixes of the canonical name
269
310
            if name == real_cmd_name or not real_cmd_name.startswith(name):
270
311
                yield name
271
312
 
 
313
 
 
314
def iter_executables(path):
 
315
    dirname, partial = os.path.split(path)
 
316
    for filename in os.listdir(dirname):
 
317
        if not filename.startswith(partial):
 
318
            continue
 
319
        fullpath = os.path.join(dirname, filename)
 
320
        mode=os.lstat(fullpath)[stat.ST_MODE]
 
321
        if stat.S_ISREG(mode) and 0111 & mode:
 
322
            yield fullpath + ' '
 
323
 
 
324
 
272
325
def filter_completions(iter, arg):
273
326
    return (c for c in iter if c.startswith(arg))
274
327
 
 
328
 
275
329
def iter_munged_completions(iter, arg, text):
276
330
    for completion in iter:
277
331
        completion = str(completion)
278
332
        if completion.startswith(arg):
279
333
            yield completion[len(arg)-len(text):]+" "
280
334
 
 
335
 
281
336
def too_complicated(line):
282
 
    for char in '|<>"\"*?':
 
337
    for char in '|<>*?':
283
338
        if char in line:
284
339
            return True
285
340
    return False