~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2006-03-22 15:19:16 UTC
  • Revision ID: abentley@panoramicfeedback.com-20060322151916-75711de1522d1f68
Tagged BZRTOOLS commands to reduce confusion

Show diffs side-by-side

added added

removed removed

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