~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Max Bowsher
  • Date: 2009-11-13 00:43:08 UTC
  • mto: This revision was merged to the branch mainline in revision 739.
  • Revision ID: maxb@f2s.com-20091113004308-4dcwu1kwt3lhf1kp
Having discovered that bzr-builddeb import_dsc.py is a horrid copy-paste job of bzrtools upstream_import.py, restructure the change to minimize divergence from it.

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, all_command_names, get_alias
24
31
from bzrlib.errors import BzrError
25
 
from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
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
 
157
178
        alias_args = get_alias(args[0])
158
179
        if alias_args is not None:
159
180
            args[0] = alias_args.pop(0)
160
 
            
 
181
 
161
182
        commandname = args.pop(0)
162
183
        for char in ('|', '<', '>'):
163
184
            commandname = commandname.split(char)[0]
189
210
 
190
211
    def completedefault(self, text, line, begidx, endidx):
191
212
        """Perform completion for native commands.
192
 
        
 
213
 
193
214
        :param text: The text to complete
194
215
        :type text: str
195
216
        :param line: The entire line to complete
204
225
            cmd = None
205
226
        return CompletionContext(text, command=cmd).get_completions()
206
227
 
 
228
 
207
229
def run_shell():
208
230
    try:
209
231
        prompt = PromptCmd()
210
 
        try:
211
 
            prompt.cmdloop()
212
 
        finally:
213
 
            prompt.write_history()
 
232
        while True:
 
233
            try:
 
234
                try:
 
235
                    prompt.cmdloop()
 
236
                except KeyboardInterrupt:
 
237
                    print
 
238
            finally:
 
239
                prompt.write_history()
214
240
    except StopIteration:
215
241
        pass
216
242
 
 
243
 
217
244
def iter_opt_completions(command_obj):
218
245
    for option_name, option in command_obj.options().items():
219
246
        yield "--" + option_name
221
248
        if short_name:
222
249
            yield "-" + short_name
223
250
 
 
251
 
224
252
def iter_file_completions(arg, only_dirs = False):
225
253
    """Generate an iterator that iterates through filename completions.
226
254
 
249
277
                userfile+='/'
250
278
                yield userfile
251
279
            elif not only_dirs:
252
 
                yield userfile
 
280
                yield userfile + ' '
253
281
 
254
282
 
255
283
def iter_dir_completions(arg):
260
288
    """
261
289
    return iter_file_completions(arg, True)
262
290
 
 
291
 
263
292
def iter_command_names(hidden=False):
264
 
    for real_cmd_name, cmd_class in get_all_cmds():
265
 
        if not hidden and cmd_class.hidden:
 
293
    for real_cmd_name in all_command_names():
 
294
        cmd_obj = get_cmd_object(real_cmd_name)
 
295
        if not hidden and cmd_obj.hidden:
266
296
            continue
267
 
        for name in [real_cmd_name] + cmd_class.aliases:
 
297
        for name in [real_cmd_name] + cmd_obj.aliases:
268
298
            # Don't complete on aliases that are prefixes of the canonical name
269
299
            if name == real_cmd_name or not real_cmd_name.startswith(name):
270
300
                yield name
271
301
 
 
302
 
 
303
def iter_executables(path):
 
304
    dirname, partial = os.path.split(path)
 
305
    for filename in os.listdir(dirname):
 
306
        if not filename.startswith(partial):
 
307
            continue
 
308
        fullpath = os.path.join(dirname, filename)
 
309
        mode=os.lstat(fullpath)[stat.ST_MODE]
 
310
        if stat.S_ISREG(mode) and 0111 & mode:
 
311
            yield fullpath + ' '
 
312
 
 
313
 
272
314
def filter_completions(iter, arg):
273
315
    return (c for c in iter if c.startswith(arg))
274
316
 
 
317
 
275
318
def iter_munged_completions(iter, arg, text):
276
319
    for completion in iter:
277
320
        completion = str(completion)
278
321
        if completion.startswith(arg):
279
322
            yield completion[len(arg)-len(text):]+" "
280
323
 
 
324
 
281
325
def too_complicated(line):
282
 
    for char in '|<>"\"*?':
 
326
    for char in '|<>*?':
283
327
        if char in line:
284
328
            return True
285
329
    return False