~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2011-04-12 04:20:08 UTC
  • mfrom: (749.1.10 2.3)
  • Revision ID: aaron@aaronbentley.com-20110412042008-a961qe7jm3uok502
Add zap --store.

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
21
import readline
 
22
import shlex
 
23
import stat
22
24
import string
23
 
from itertools import chain
 
25
import sys
 
26
 
 
27
from bzrlib import osutils, trace
 
28
from bzrlib.branch import Branch
 
29
from bzrlib.config import config_dir, ensure_config_dir_exists
 
30
from bzrlib.commands import get_cmd_object, all_command_names, get_alias
24
31
from bzrlib.errors import BzrError
25
 
from bzrlib.commands import get_cmd_object, get_all_cmds
26
 
from bzrlib.branch import Branch
 
32
from bzrlib.workingtree import WorkingTree
 
33
 
 
34
import terminal
 
35
 
27
36
 
28
37
SHELL_BLACKLIST = set(['rm', 'ls'])
29
38
COMPLETION_BLACKLIST = set(['shell'])
30
39
 
 
40
 
31
41
class BlackListedCommand(BzrError):
32
42
    def __init__(self, command):
33
43
        BzrError.__init__(self, "The command %s is blacklisted for shell use" %
34
44
                          command)
35
45
 
 
46
 
36
47
class CompletionContext(object):
37
48
    def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
38
49
        self.text = text
57
68
 
58
69
    def get_completions_or_raise(self):
59
70
        if self.command is None:
60
 
            iter = (c+" " for c in iter_command_names() if
61
 
                    c not in COMPLETION_BLACKLIST)
 
71
            if '/' in self.text:
 
72
                iter = iter_executables(self.text)
 
73
            else:
 
74
                iter = (c+" " for c in iter_command_names() if
 
75
                        c not in COMPLETION_BLACKLIST)
62
76
            return list(filter_completions(iter, self.text))
63
77
        if self.prev_opt is None:
64
78
            completions = self.get_option_completions()
67
81
                completions.extend(list(filter_completions(iter, self.text)))
68
82
            else:
69
83
                iter = iter_file_completions(self.text)
70
 
                completions.extend([f+" " for f in 
71
 
                                    filter_completions(iter, self.text)])
72
 
            return completions 
 
84
                completions.extend(filter_completions(iter, self.text))
 
85
            return completions
73
86
 
74
87
 
75
88
class PromptCmd(cmd.Cmd):
 
89
 
76
90
    def __init__(self):
77
91
        cmd.Cmd.__init__(self)
78
92
        self.prompt = "bzr> "
83
97
        self.set_title()
84
98
        self.set_prompt()
85
99
        self.identchars += '-'
86
 
        self.history_file = os.path.expanduser("~/.bazaar/shell-history")
87
 
        readline.set_completer_delims(string.whitespace)
 
100
        ensure_config_dir_exists()
 
101
        self.history_file = osutils.pathjoin(config_dir(), 'shell-history')
 
102
        whitespace = ''.join(c for c in string.whitespace if c < chr(127))
 
103
        readline.set_completer_delims(whitespace)
88
104
        if os.access(self.history_file, os.R_OK) and \
89
105
            os.path.isfile(self.history_file):
90
106
            readline.read_history_file(self.history_file)
111
127
    def set_prompt(self):
112
128
        if self.tree is not None:
113
129
            try:
114
 
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(), 
115
 
                               self.tree.branch.relpath('.'))
 
130
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
 
131
                               self.tree.relpath('.'))
116
132
                prompt = " %s:%d/%s" % prompt_data
117
133
            except:
118
134
                prompt = ""
153
169
        self.default("help "+line)
154
170
 
155
171
    def default(self, line):
156
 
        args = line.split()
 
172
        try:
 
173
            args = shlex.split(line)
 
174
        except ValueError, e:
 
175
            print 'Parse error:', e
 
176
            return
 
177
 
 
178
        alias_args = get_alias(args[0])
 
179
        if alias_args is not None:
 
180
            args[0] = alias_args.pop(0)
 
181
 
157
182
        commandname = args.pop(0)
158
183
        for char in ('|', '<', '>'):
159
184
            commandname = commandname.split(char)[0]
167
192
            return os.system(line)
168
193
 
169
194
        try:
170
 
            if too_complicated(line):
 
195
            is_qbzr = cmd_obj.__module__.startswith('bzrlib.plugins.qbzr.')
 
196
            if too_complicated(line) or is_qbzr:
171
197
                return os.system("bzr "+line)
172
198
            else:
173
 
                return (cmd_obj.run_argv(args) or 0)
 
199
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
174
200
        except BzrError, e:
 
201
            trace.log_exception_quietly()
175
202
            print e
176
203
        except KeyboardInterrupt, e:
177
204
            print "Interrupted"
178
205
        except Exception, e:
179
 
#            print "Unhandled error:\n%s" % errors.exception_str(e)
 
206
            trace.log_exception_quietly()
180
207
            print "Unhandled error:\n%s" % (e)
181
208
 
182
209
 
185
212
 
186
213
    def completedefault(self, text, line, begidx, endidx):
187
214
        """Perform completion for native commands.
188
 
        
 
215
 
189
216
        :param text: The text to complete
190
217
        :type text: str
191
218
        :param line: The entire line to complete
200
227
            cmd = None
201
228
        return CompletionContext(text, command=cmd).get_completions()
202
229
 
203
 
def run_shell():
 
230
 
 
231
def run_shell(directory=None):
204
232
    try:
 
233
        if not directory is None:
 
234
            os.chdir(directory)
205
235
        prompt = PromptCmd()
206
 
        try:
207
 
            prompt.cmdloop()
208
 
        finally:
209
 
            prompt.write_history()
 
236
        while True:
 
237
            try:
 
238
                try:
 
239
                    prompt.cmdloop()
 
240
                except KeyboardInterrupt:
 
241
                    print
 
242
            finally:
 
243
                prompt.write_history()
210
244
    except StopIteration:
211
245
        pass
212
246
 
 
247
 
213
248
def iter_opt_completions(command_obj):
214
249
    for option_name, option in command_obj.options().items():
215
250
        yield "--" + option_name
217
252
        if short_name:
218
253
            yield "-" + short_name
219
254
 
 
255
 
220
256
def iter_file_completions(arg, only_dirs = False):
221
257
    """Generate an iterator that iterates through filename completions.
222
258
 
245
281
                userfile+='/'
246
282
                yield userfile
247
283
            elif not only_dirs:
248
 
                yield userfile
 
284
                yield userfile + ' '
249
285
 
250
286
 
251
287
def iter_dir_completions(arg):
256
292
    """
257
293
    return iter_file_completions(arg, True)
258
294
 
 
295
 
259
296
def iter_command_names(hidden=False):
260
 
    for real_cmd_name, cmd_class in get_all_cmds():
261
 
        if not hidden and cmd_class.hidden:
 
297
    for real_cmd_name in all_command_names():
 
298
        cmd_obj = get_cmd_object(real_cmd_name)
 
299
        if not hidden and cmd_obj.hidden:
262
300
            continue
263
 
        for name in [real_cmd_name] + cmd_class.aliases:
 
301
        for name in [real_cmd_name] + cmd_obj.aliases:
264
302
            # Don't complete on aliases that are prefixes of the canonical name
265
303
            if name == real_cmd_name or not real_cmd_name.startswith(name):
266
304
                yield name
267
305
 
 
306
 
 
307
def iter_executables(path):
 
308
    dirname, partial = os.path.split(path)
 
309
    for filename in os.listdir(dirname):
 
310
        if not filename.startswith(partial):
 
311
            continue
 
312
        fullpath = os.path.join(dirname, filename)
 
313
        mode=os.lstat(fullpath)[stat.ST_MODE]
 
314
        if stat.S_ISREG(mode) and 0111 & mode:
 
315
            yield fullpath + ' '
 
316
 
 
317
 
268
318
def filter_completions(iter, arg):
269
319
    return (c for c in iter if c.startswith(arg))
270
320
 
 
321
 
271
322
def iter_munged_completions(iter, arg, text):
272
323
    for completion in iter:
273
324
        completion = str(completion)
274
325
        if completion.startswith(arg):
275
326
            yield completion[len(arg)-len(text):]+" "
276
327
 
 
328
 
277
329
def too_complicated(line):
278
 
    for char in '|<>"\"*?':
 
330
    for char in '|<>*?':
279
331
        if char in line:
280
332
            return True
281
333
    return False