~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2005-10-27 04:45:06 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20051027044506-45f616c07537a1da
Got the shell basics working properly

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
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
31
23
from bzrlib.errors import BzrError
32
 
from bzrlib.workingtree import WorkingTree
33
 
 
34
 
import terminal
35
 
 
36
 
 
37
 
SHELL_BLACKLIST = set(['rm', 'ls'])
38
 
COMPLETION_BLACKLIST = set(['shell'])
39
 
 
40
 
 
41
 
class BlackListedCommand(BzrError):
42
 
    def __init__(self, command):
43
 
        BzrError.__init__(self, "The command %s is blacklisted for shell use" %
44
 
                          command)
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
 
 
 
24
from bzrlib.commands import get_cmd_object
87
25
 
88
26
class PromptCmd(cmd.Cmd):
89
 
 
90
27
    def __init__(self):
91
28
        cmd.Cmd.__init__(self)
92
29
        self.prompt = "bzr> "
93
30
        try:
94
 
            self.tree = WorkingTree.open_containing('.')[0]
 
31
            self.tree = arch.tree_root(".")
95
32
        except:
96
33
            self.tree = None
97
34
        self.set_title()
98
35
        self.set_prompt()
99
36
        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)
 
37
        self.history_file = os.path.expanduser("~/.bazaar/shell-history")
 
38
        readline.set_completer_delims(string.whitespace)
104
39
        if os.access(self.history_file, os.R_OK) and \
105
40
            os.path.isfile(self.history_file):
106
41
            readline.read_history_file(self.history_file)
111
46
 
112
47
    def do_quit(self, args):
113
48
        self.write_history()
114
 
        raise StopIteration
 
49
        sys.exit(0)
115
50
 
116
51
    def do_exit(self, args):
117
52
        self.do_quit(args)
127
62
    def set_prompt(self):
128
63
        if self.tree is not None:
129
64
            try:
130
 
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
131
 
                               self.tree.relpath('.'))
132
 
                prompt = " %s:%d/%s" % prompt_data
 
65
                prompt = pylon.alias_or_version(self.tree.tree_version, 
 
66
                                                self.tree, 
 
67
                                                full=False)
 
68
                if prompt is not None:
 
69
                    prompt = " " + prompt +":"+ pylon.tree_cwd(self.tree)
133
70
            except:
134
71
                prompt = ""
135
72
        else:
138
75
 
139
76
    def set_title(self, command=None):
140
77
        try:
141
 
            b = Branch.open_containing('.')[0]
142
 
            version = "%s:%d" % (b.nick, b.revno())
 
78
            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
 
79
                                             full=False)
143
80
        except:
144
81
            version = "[no version]"
145
82
        if command is None:
161
98
        except Exception, e:
162
99
            print e
163
100
        try:
164
 
            self.tree = WorkingTree.open_containing(".")[0]
 
101
            self.tree = arch.tree_root(".")
165
102
        except:
166
103
            self.tree = None
167
104
 
168
105
    def do_help(self, line):
169
 
        self.default("help "+line)
 
106
        Help()(line)
170
107
 
171
108
    def default(self, line):
172
 
        try:
173
 
            args = shlex.split(line)
174
 
        except ValueError, e:
175
 
            print 'Parse error:', e
176
 
            return
177
 
 
178
 
        alias_args = get_alias(args[0])
179
 
        if alias_args is not None:
180
 
            args[0] = alias_args.pop(0)
181
 
 
 
109
        args = line.split()
182
110
        commandname = args.pop(0)
183
 
        for char in ('|', '<', '>'):
184
 
            commandname = commandname.split(char)[0]
185
 
        if commandname[-1] in ('|', '<', '>'):
186
 
            commandname = commandname[:-1]
187
111
        try:
188
 
            if commandname in SHELL_BLACKLIST:
189
 
                raise BlackListedCommand(commandname)
190
112
            cmd_obj = get_cmd_object(commandname)
191
 
        except (BlackListedCommand, BzrError):
 
113
        except BzrError:
192
114
            return os.system(line)
193
115
 
 
116
 
194
117
        try:
195
 
            is_qbzr = cmd_obj.__module__.startswith('bzrlib.plugins.qbzr.')
196
 
            if too_complicated(line) or is_qbzr:
197
 
                return os.system("bzr "+line)
198
 
            else:
199
 
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
 
118
            return (cmd_obj.run_argv(args) or 0)
200
119
        except BzrError, e:
201
120
            print e
202
121
        except KeyboardInterrupt, e:
207
126
 
208
127
 
209
128
    def completenames(self, text, line, begidx, endidx):
210
 
        return CompletionContext(text).get_completions()
 
129
        completions = []
 
130
        iter = iter_command_names(self.fake_aba)
 
131
        try:
 
132
            if len(line) > 0:
 
133
                arg = line.split()[-1]
 
134
            else:
 
135
                arg = ""
 
136
            iter = cmdutil.iter_munged_completions(iter, arg, text)
 
137
        except Exception, e:
 
138
            print e
 
139
        return list(iter)
211
140
 
212
141
    def completedefault(self, text, line, begidx, endidx):
213
142
        """Perform completion for native commands.
214
 
 
 
143
        
215
144
        :param text: The text to complete
216
145
        :type text: str
217
146
        :param line: The entire line to complete
221
150
        :param endidx: The end of the text in the line
222
151
        :type endidx: int
223
152
        """
224
 
        (cmd, args, foo) = self.parseline(line)
225
 
        if cmd == "bzr":
226
 
            cmd = None
227
 
        return CompletionContext(text, command=cmd).get_completions()
228
 
 
229
 
 
230
 
def run_shell(directory=None):
 
153
        try:
 
154
            (cmd, args, foo) = self.parseline(line)
 
155
            command_obj=find_command(cmd)
 
156
            if command_obj is not None:
 
157
                return command_obj.complete(args.split(), text)
 
158
            elif not self.fake_aba.is_command(cmd) and \
 
159
                cmdutil.is_tla_command(cmd):
 
160
                iter = cmdutil.iter_supported_switches(cmd)
 
161
                if len(args) > 0:
 
162
                    arg = args.split()[-1]
 
163
                else:
 
164
                    arg = ""
 
165
                if arg.startswith("-"):
 
166
                    return list(cmdutil.iter_munged_completions(iter, arg, 
 
167
                                                                text))
 
168
                else:
 
169
                    return list(cmdutil.iter_munged_completions(
 
170
                        cmdutil.iter_file_completions(arg), arg, text))
 
171
 
 
172
 
 
173
            elif cmd == "cd":
 
174
                if len(args) > 0:
 
175
                    arg = args.split()[-1]
 
176
                else:
 
177
                    arg = ""
 
178
                iter = cmdutil.iter_dir_completions(arg)
 
179
                iter = cmdutil.iter_munged_completions(iter, arg, text)
 
180
                return list(iter)
 
181
            elif len(args)>0:
 
182
                arg = args.split()[-1]
 
183
                iter = cmdutil.iter_file_completions(arg)
 
184
                return list(cmdutil.iter_munged_completions(iter, arg, text))
 
185
            else:
 
186
                return self.completenames(text, line, begidx, endidx)
 
187
        except Exception, e:
 
188
            print e
 
189
 
 
190
def run_shell():
 
191
    prompt = PromptCmd()
231
192
    try:
232
 
        if not directory is None:
233
 
            os.chdir(directory)
234
 
        prompt = PromptCmd()
235
 
        while True:
236
 
            try:
237
 
                try:
238
 
                    prompt.cmdloop()
239
 
                except KeyboardInterrupt:
240
 
                    print
241
 
            finally:
242
 
                prompt.write_history()
243
 
    except StopIteration:
244
 
        pass
245
 
 
246
 
 
247
 
def iter_opt_completions(command_obj):
248
 
    for option_name, option in command_obj.options().items():
249
 
        yield "--" + option_name
250
 
        short_name = option.short_name()
251
 
        if short_name:
252
 
            yield "-" + short_name
253
 
 
 
193
        prompt.cmdloop()
 
194
    finally:
 
195
        prompt.write_history()
254
196
 
255
197
def iter_file_completions(arg, only_dirs = False):
256
198
    """Generate an iterator that iterates through filename completions.
270
212
        listingdir = os.path.expanduser(dir)
271
213
    else:
272
214
        listingdir = cwd
273
 
    for file in chain(os.listdir(listingdir), extras):
 
215
    for file in iter_combine([os.listdir(listingdir), extras]):
274
216
        if dir != "":
275
217
            userfile = dir+'/'+file
276
218
        else:
280
222
                userfile+='/'
281
223
                yield userfile
282
224
            elif not only_dirs:
283
 
                yield userfile + ' '
 
225
                yield userfile
284
226
 
285
227
 
286
228
def iter_dir_completions(arg):
290
232
    :type arg: str
291
233
    """
292
234
    return iter_file_completions(arg, True)
293
 
 
294
 
 
295
 
def iter_command_names(hidden=False):
296
 
    for real_cmd_name in all_command_names():
297
 
        cmd_obj = get_cmd_object(real_cmd_name)
298
 
        if not hidden and cmd_obj.hidden:
299
 
            continue
300
 
        for name in [real_cmd_name] + cmd_obj.aliases:
301
 
            # Don't complete on aliases that are prefixes of the canonical name
302
 
            if name == real_cmd_name or not real_cmd_name.startswith(name):
303
 
                yield name
304
 
 
305
 
 
306
 
def iter_executables(path):
307
 
    dirname, partial = os.path.split(path)
308
 
    for filename in os.listdir(dirname):
309
 
        if not filename.startswith(partial):
310
 
            continue
311
 
        fullpath = os.path.join(dirname, filename)
312
 
        mode=os.lstat(fullpath)[stat.ST_MODE]
313
 
        if stat.S_ISREG(mode) and 0111 & mode:
314
 
            yield fullpath + ' '
315
 
 
316
 
 
317
 
def filter_completions(iter, arg):
318
 
    return (c for c in iter if c.startswith(arg))
319
 
 
320
 
 
321
 
def iter_munged_completions(iter, arg, text):
322
 
    for completion in iter:
323
 
        completion = str(completion)
324
 
        if completion.startswith(arg):
325
 
            yield completion[len(arg)-len(text):]+" "
326
 
 
327
 
 
328
 
def too_complicated(line):
329
 
    for char in '|<>*?':
330
 
        if char in line:
331
 
            return True
332
 
    return False