~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2008-05-12 02:46:09 UTC
  • Revision ID: aaron@aaronbentley.com-20080512024609-ku0i1fotlf8h16ry
update NEWS

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
 
32
from bzrlib.workingtree import WorkingTree
 
33
 
 
34
import terminal
 
35
 
26
36
 
27
37
SHELL_BLACKLIST = set(['rm', 'ls'])
28
38
COMPLETION_BLACKLIST = set(['shell'])
29
39
 
 
40
 
30
41
class BlackListedCommand(BzrError):
31
42
    def __init__(self, command):
32
43
        BzrError.__init__(self, "The command %s is blacklisted for shell use" %
33
44
                          command)
34
45
 
 
46
 
 
47
class CompletionContext(object):
 
48
    def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
 
49
        self.text = text
 
50
        self.command = command
 
51
        self.prev_opt = prev_opt
 
52
        self.arg_pos = None
 
53
 
 
54
    def get_completions(self):
 
55
        try:
 
56
            return self.get_completions_or_raise()
 
57
        except Exception, e:
 
58
            print e, type(e)
 
59
            return []
 
60
 
 
61
    def get_option_completions(self):
 
62
        try:
 
63
            command_obj = get_cmd_object(self.command)
 
64
        except BzrError:
 
65
            return []
 
66
        opts = [o+" " for o in iter_opt_completions(command_obj)]
 
67
        return list(filter_completions(opts, self.text))
 
68
 
 
69
    def get_completions_or_raise(self):
 
70
        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)
 
76
            return list(filter_completions(iter, self.text))
 
77
        if self.prev_opt is None:
 
78
            completions = self.get_option_completions()
 
79
            if self.command == "cd":
 
80
                iter = iter_dir_completions(self.text)
 
81
                completions.extend(list(filter_completions(iter, self.text)))
 
82
            else:
 
83
                iter = iter_file_completions(self.text)
 
84
                completions.extend(filter_completions(iter, self.text))
 
85
            return completions
 
86
 
 
87
 
35
88
class PromptCmd(cmd.Cmd):
 
89
 
36
90
    def __init__(self):
37
91
        cmd.Cmd.__init__(self)
38
92
        self.prompt = "bzr> "
39
93
        try:
40
 
            self.tree = arch.tree_root(".")
 
94
            self.tree = WorkingTree.open_containing('.')[0]
41
95
        except:
42
96
            self.tree = None
43
97
        self.set_title()
44
98
        self.set_prompt()
45
99
        self.identchars += '-'
46
 
        self.history_file = os.path.expanduser("~/.bazaar/shell-history")
 
100
        ensure_config_dir_exists()
 
101
        self.history_file = osutils.pathjoin(config_dir(), 'shell-history')
47
102
        readline.set_completer_delims(string.whitespace)
48
103
        if os.access(self.history_file, os.R_OK) and \
49
104
            os.path.isfile(self.history_file):
71
126
    def set_prompt(self):
72
127
        if self.tree is not None:
73
128
            try:
74
 
                prompt = pylon.alias_or_version(self.tree.tree_version, 
75
 
                                                self.tree, 
76
 
                                                full=False)
77
 
                if prompt is not None:
78
 
                    prompt = " " + prompt +":"+ pylon.tree_cwd(self.tree)
 
129
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
 
130
                               self.tree.relpath('.'))
 
131
                prompt = " %s:%d/%s" % prompt_data
79
132
            except:
80
133
                prompt = ""
81
134
        else:
84
137
 
85
138
    def set_title(self, command=None):
86
139
        try:
87
 
            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
88
 
                                             full=False)
 
140
            b = Branch.open_containing('.')[0]
 
141
            version = "%s:%d" % (b.nick, b.revno())
89
142
        except:
90
143
            version = "[no version]"
91
144
        if command is None:
107
160
        except Exception, e:
108
161
            print e
109
162
        try:
110
 
            self.tree = arch.tree_root(".")
 
163
            self.tree = WorkingTree.open_containing(".")[0]
111
164
        except:
112
165
            self.tree = None
113
166
 
115
168
        self.default("help "+line)
116
169
 
117
170
    def default(self, line):
118
 
        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
 
119
176
        commandname = args.pop(0)
120
177
        for char in ('|', '<', '>'):
121
178
            commandname = commandname.split(char)[0]
132
189
            if too_complicated(line):
133
190
                return os.system("bzr "+line)
134
191
            else:
135
 
                return (cmd_obj.run_argv(args) or 0)
 
192
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
136
193
        except BzrError, e:
137
194
            print e
138
195
        except KeyboardInterrupt, e:
143
200
 
144
201
 
145
202
    def completenames(self, text, line, begidx, endidx):
146
 
        completions = []
147
 
        iter = (c for c in iter_command_names() if
148
 
                c not in COMPLETION_BLACKLIST)
149
 
        try:
150
 
            if len(line) > 0:
151
 
                arg = line.split()[-1]
152
 
            else:
153
 
                arg = ""
154
 
            iter = list(iter_munged_completions(iter, arg, text))
155
 
        except Exception, e:
156
 
            print e, type(e)
157
 
        return list(iter)
 
203
        return CompletionContext(text).get_completions()
158
204
 
159
205
    def completedefault(self, text, line, begidx, endidx):
160
206
        """Perform completion for native commands.
161
 
        
 
207
 
162
208
        :param text: The text to complete
163
209
        :type text: str
164
210
        :param line: The entire line to complete
170
216
        """
171
217
        (cmd, args, foo) = self.parseline(line)
172
218
        if cmd == "bzr":
173
 
            try:
174
 
                return self.completenames(text, line, begidx, endidx)
175
 
            except Exception, e:
176
 
                print e
177
 
        try:
178
 
            command_obj = get_cmd_object(cmd)
179
 
        except BzrError:
180
 
            command_obj = None
181
 
        try:
182
 
            if command_obj is not None:
183
 
                opts = []
184
 
                for option_name, option in command_obj.options().items():
185
 
                    opts.append("--" + option_name)
186
 
                    short_name = option.short_name()
187
 
                    if short_name:
188
 
                        opts.append("-" + short_name)
189
 
                q = list(iter_munged_completions(opts, args, text))
190
 
                return list(iter_munged_completions(opts, args, text))
191
 
            elif cmd == "cd":
192
 
                if len(args) > 0:
193
 
                    arg = args.split()[-1]
194
 
                else:
195
 
                    arg = ""
196
 
                iter = iter_dir_completions(arg)
197
 
                iter = iter_munged_completions(iter, arg, text)
198
 
                return list(iter)
199
 
            elif len(args)>0:
200
 
                arg = args.split()[-1]
201
 
                iter = iter_file_completions(arg)
202
 
                return list(iter_munged_completions(iter, arg, text))
203
 
            else:
204
 
                return self.completenames(text, line, begidx, endidx)
205
 
        except Exception, e:
206
 
            print e
 
219
            cmd = None
 
220
        return CompletionContext(text, command=cmd).get_completions()
 
221
 
207
222
 
208
223
def run_shell():
209
224
    try:
215
230
    except StopIteration:
216
231
        pass
217
232
 
 
233
 
 
234
def iter_opt_completions(command_obj):
 
235
    for option_name, option in command_obj.options().items():
 
236
        yield "--" + option_name
 
237
        short_name = option.short_name()
 
238
        if short_name:
 
239
            yield "-" + short_name
 
240
 
 
241
 
218
242
def iter_file_completions(arg, only_dirs = False):
219
243
    """Generate an iterator that iterates through filename completions.
220
244
 
243
267
                userfile+='/'
244
268
                yield userfile
245
269
            elif not only_dirs:
246
 
                yield userfile
 
270
                yield userfile + ' '
247
271
 
248
272
 
249
273
def iter_dir_completions(arg):
254
278
    """
255
279
    return iter_file_completions(arg, True)
256
280
 
 
281
 
257
282
def iter_command_names(hidden=False):
258
283
    for real_cmd_name, cmd_class in get_all_cmds():
259
284
        if not hidden and cmd_class.hidden:
263
288
            if name == real_cmd_name or not real_cmd_name.startswith(name):
264
289
                yield name
265
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
 
 
303
def filter_completions(iter, arg):
 
304
    return (c for c in iter if c.startswith(arg))
 
305
 
 
306
 
266
307
def iter_munged_completions(iter, arg, text):
267
308
    for completion in iter:
268
309
        completion = str(completion)
269
310
        if completion.startswith(arg):
270
311
            yield completion[len(arg)-len(text):]+" "
271
312
 
 
313
 
272
314
def too_complicated(line):
273
 
    for char in '|<>"\"':
 
315
    for char in '|<>*?':
274
316
        if char in line:
275
317
            return True
276
318
    return False