~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2006-06-14 18:09:53 UTC
  • mto: This revision was merged to the branch mainline in revision 395.
  • Revision ID: abentley@panoramicfeedback.com-20060614180953-4671478534fd8823
Update NEWS

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
21
 
try:
22
 
    import readline
23
 
except ImportError:
24
 
    _has_readline = False
25
 
else:
26
 
    _has_readline = True
27
 
import shlex
28
 
import stat
 
20
import terminal
 
21
import readline
29
22
import string
30
 
import sys
31
 
 
32
 
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
33
26
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
36
 
from bzrlib.errors import BzrError
37
 
from bzrlib.workingtree import WorkingTree
38
 
 
39
 
import terminal
40
 
 
41
27
 
42
28
SHELL_BLACKLIST = set(['rm', 'ls'])
43
29
COMPLETION_BLACKLIST = set(['shell'])
44
30
 
45
 
 
46
31
class BlackListedCommand(BzrError):
47
32
    def __init__(self, command):
48
33
        BzrError.__init__(self, "The command %s is blacklisted for shell use" %
49
34
                          command)
50
35
 
51
 
 
52
36
class CompletionContext(object):
53
37
    def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
54
38
        self.text = text
73
57
 
74
58
    def get_completions_or_raise(self):
75
59
        if self.command is None:
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)
 
60
            iter = (c+" " for c in iter_command_names() if
 
61
                    c not in COMPLETION_BLACKLIST)
81
62
            return list(filter_completions(iter, self.text))
82
63
        if self.prev_opt is None:
83
64
            completions = self.get_option_completions()
86
67
                completions.extend(list(filter_completions(iter, self.text)))
87
68
            else:
88
69
                iter = iter_file_completions(self.text)
89
 
                completions.extend(filter_completions(iter, self.text))
90
 
            return completions
 
70
                completions.extend([f+" " for f in 
 
71
                                    filter_completions(iter, self.text)])
 
72
            return completions 
91
73
 
92
74
 
93
75
class PromptCmd(cmd.Cmd):
94
 
 
95
76
    def __init__(self):
96
77
        cmd.Cmd.__init__(self)
97
78
        self.prompt = "bzr> "
102
83
        self.set_title()
103
84
        self.set_prompt()
104
85
        self.identchars += '-'
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)
 
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)
113
91
        self.cwd = os.getcwd()
114
92
 
115
93
    def write_history(self):
116
 
        if _has_readline:
117
 
            readline.write_history_file(self.history_file)
 
94
        readline.write_history_file(self.history_file)
118
95
 
119
96
    def do_quit(self, args):
120
97
        self.write_history()
134
111
    def set_prompt(self):
135
112
        if self.tree is not None:
136
113
            try:
137
 
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
138
 
                               self.tree.relpath('.'))
 
114
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(), 
 
115
                               self.tree.branch.relpath('.'))
139
116
                prompt = " %s:%d/%s" % prompt_data
140
117
            except:
141
118
                prompt = ""
176
153
        self.default("help "+line)
177
154
 
178
155
    def default(self, line):
179
 
        try:
180
 
            args = shlex.split(line)
181
 
        except ValueError, e:
182
 
            print 'Parse error:', e
183
 
            return
184
 
 
 
156
        args = line.split()
185
157
        alias_args = get_alias(args[0])
186
158
        if alias_args is not None:
187
159
            args[0] = alias_args.pop(0)
188
 
 
 
160
            
189
161
        commandname = args.pop(0)
190
162
        for char in ('|', '<', '>'):
191
163
            commandname = commandname.split(char)[0]
199
171
            return os.system(line)
200
172
 
201
173
        try:
202
 
            is_qbzr = cmd_obj.__module__.startswith('bzrlib.plugins.qbzr.')
203
 
            if too_complicated(line) or is_qbzr:
 
174
            if too_complicated(line):
204
175
                return os.system("bzr "+line)
205
176
            else:
206
177
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
207
178
        except BzrError, e:
208
 
            trace.log_exception_quietly()
209
179
            print e
210
180
        except KeyboardInterrupt, e:
211
181
            print "Interrupted"
212
182
        except Exception, e:
213
 
            trace.log_exception_quietly()
 
183
#            print "Unhandled error:\n%s" % errors.exception_str(e)
214
184
            print "Unhandled error:\n%s" % (e)
215
185
 
216
186
 
219
189
 
220
190
    def completedefault(self, text, line, begidx, endidx):
221
191
        """Perform completion for native commands.
222
 
 
 
192
        
223
193
        :param text: The text to complete
224
194
        :type text: str
225
195
        :param line: The entire line to complete
234
204
            cmd = None
235
205
        return CompletionContext(text, command=cmd).get_completions()
236
206
 
237
 
 
238
 
def run_shell(directory=None):
 
207
def run_shell():
239
208
    try:
240
 
        if not directory is None:
241
 
            os.chdir(directory)
242
209
        prompt = PromptCmd()
243
 
        while True:
244
 
            try:
245
 
                try:
246
 
                    prompt.cmdloop()
247
 
                except KeyboardInterrupt:
248
 
                    print
249
 
            finally:
250
 
                prompt.write_history()
 
210
        try:
 
211
            prompt.cmdloop()
 
212
        finally:
 
213
            prompt.write_history()
251
214
    except StopIteration:
252
215
        pass
253
216
 
254
 
 
255
217
def iter_opt_completions(command_obj):
256
218
    for option_name, option in command_obj.options().items():
257
219
        yield "--" + option_name
259
221
        if short_name:
260
222
            yield "-" + short_name
261
223
 
262
 
 
263
224
def iter_file_completions(arg, only_dirs = False):
264
225
    """Generate an iterator that iterates through filename completions.
265
226
 
288
249
                userfile+='/'
289
250
                yield userfile
290
251
            elif not only_dirs:
291
 
                yield userfile + ' '
 
252
                yield userfile
292
253
 
293
254
 
294
255
def iter_dir_completions(arg):
299
260
    """
300
261
    return iter_file_completions(arg, True)
301
262
 
302
 
 
303
263
def iter_command_names(hidden=False):
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:
 
264
    for real_cmd_name, cmd_class in get_all_cmds():
 
265
        if not hidden and cmd_class.hidden:
307
266
            continue
308
 
        for name in [real_cmd_name] + cmd_obj.aliases:
 
267
        for name in [real_cmd_name] + cmd_class.aliases:
309
268
            # Don't complete on aliases that are prefixes of the canonical name
310
269
            if name == real_cmd_name or not real_cmd_name.startswith(name):
311
270
                yield name
312
271
 
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
 
 
325
272
def filter_completions(iter, arg):
326
273
    return (c for c in iter if c.startswith(arg))
327
274
 
328
 
 
329
275
def iter_munged_completions(iter, arg, text):
330
276
    for completion in iter:
331
277
        completion = str(completion)
332
278
        if completion.startswith(arg):
333
279
            yield completion[len(arg)-len(text):]+" "
334
280
 
335
 
 
336
281
def too_complicated(line):
337
 
    for char in '|<>*?':
 
282
    for char in '|<>"\"*?':
338
283
        if char in line:
339
284
            return True
340
285
    return False