~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2008-11-03 18:43:48 UTC
  • Revision ID: aaron@aaronbentley.com-20081103184348-cbh1llu232l8n3ds
Tags: release-1.9.0
Fix release date

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
 
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, get_all_cmds, 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")
 
100
        ensure_config_dir_exists()
 
101
        self.history_file = osutils.pathjoin(config_dir(), 'shell-history')
87
102
        readline.set_completer_delims(string.whitespace)
88
103
        if os.access(self.history_file, os.R_OK) and \
89
104
            os.path.isfile(self.history_file):
111
126
    def set_prompt(self):
112
127
        if self.tree is not None:
113
128
            try:
114
 
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(), 
115
 
                               self.tree.branch.relpath('.'))
 
129
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
 
130
                               self.tree.relpath('.'))
116
131
                prompt = " %s:%d/%s" % prompt_data
117
132
            except:
118
133
                prompt = ""
153
168
        self.default("help "+line)
154
169
 
155
170
    def default(self, line):
156
 
        args = line.split()
 
171
        args = shlex.split(line)
 
172
        alias_args = get_alias(args[0])
 
173
        if alias_args is not None:
 
174
            args[0] = alias_args.pop(0)
 
175
 
157
176
        commandname = args.pop(0)
158
177
        for char in ('|', '<', '>'):
159
178
            commandname = commandname.split(char)[0]
170
189
            if too_complicated(line):
171
190
                return os.system("bzr "+line)
172
191
            else:
173
 
                return (cmd_obj.run_argv(args) or 0)
 
192
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
174
193
        except BzrError, e:
175
194
            print e
176
195
        except KeyboardInterrupt, e:
185
204
 
186
205
    def completedefault(self, text, line, begidx, endidx):
187
206
        """Perform completion for native commands.
188
 
        
 
207
 
189
208
        :param text: The text to complete
190
209
        :type text: str
191
210
        :param line: The entire line to complete
200
219
            cmd = None
201
220
        return CompletionContext(text, command=cmd).get_completions()
202
221
 
 
222
 
203
223
def run_shell():
204
224
    try:
205
225
        prompt = PromptCmd()
210
230
    except StopIteration:
211
231
        pass
212
232
 
 
233
 
213
234
def iter_opt_completions(command_obj):
214
235
    for option_name, option in command_obj.options().items():
215
236
        yield "--" + option_name
217
238
        if short_name:
218
239
            yield "-" + short_name
219
240
 
 
241
 
220
242
def iter_file_completions(arg, only_dirs = False):
221
243
    """Generate an iterator that iterates through filename completions.
222
244
 
245
267
                userfile+='/'
246
268
                yield userfile
247
269
            elif not only_dirs:
248
 
                yield userfile
 
270
                yield userfile + ' '
249
271
 
250
272
 
251
273
def iter_dir_completions(arg):
256
278
    """
257
279
    return iter_file_completions(arg, True)
258
280
 
 
281
 
259
282
def iter_command_names(hidden=False):
260
283
    for real_cmd_name, cmd_class in get_all_cmds():
261
284
        if not hidden and cmd_class.hidden:
265
288
            if name == real_cmd_name or not real_cmd_name.startswith(name):
266
289
                yield name
267
290
 
 
291
 
 
292
def iter_executables(path):
 
293
    dirname, partial = os.path.split(path)
 
294
    for filename in os.listdir(dirname):
 
295
        if not filename.startswith(partial):
 
296
            continue
 
297
        fullpath = os.path.join(dirname, filename)
 
298
        mode=os.lstat(fullpath)[stat.ST_MODE]
 
299
        if stat.S_ISREG(mode) and 0111 & mode:
 
300
            yield fullpath + ' '
 
301
 
 
302
 
268
303
def filter_completions(iter, arg):
269
304
    return (c for c in iter if c.startswith(arg))
270
305
 
 
306
 
271
307
def iter_munged_completions(iter, arg, text):
272
308
    for completion in iter:
273
309
        completion = str(completion)
274
310
        if completion.startswith(arg):
275
311
            yield completion[len(arg)-len(text):]+" "
276
312
 
 
313
 
277
314
def too_complicated(line):
278
 
    for char in '|<>"\"*?':
 
315
    for char in '|<>*?':
279
316
        if char in line:
280
317
            return True
281
318
    return False