~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2005-10-27 21:13:56 UTC
  • mfrom: (256)
  • mto: (147.4.14)
  • mto: This revision was merged to the branch mainline in revision 324.
  • Revision ID: abentley@panoramicfeedback.com-20051027211356-0d4699554bcfbc7b
MergeĀ fromĀ mainline

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005 Aaron Bentley
 
2
# <aaron.bentley@utoronto.ca>
 
3
#
 
4
#    This program is free software; you can redistribute it and/or modify
 
5
#    it under the terms of the GNU General Public License as published by
 
6
#    the Free Software Foundation; either version 2 of the License, or
 
7
#    (at your option) any later version.
 
8
#
 
9
#    This program is distributed in the hope that it will be useful,
 
10
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
#    GNU General Public License for more details.
 
13
#
 
14
#    You should have received a copy of the GNU General Public License
 
15
#    along with this program; if not, write to the Free Software
 
16
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
import cmd
 
18
import sys
 
19
import os
 
20
import terminal
 
21
import readline
 
22
import string
 
23
from itertools import chain
 
24
from bzrlib.errors import BzrError
 
25
from bzrlib.commands import get_cmd_object, get_all_cmds
 
26
 
 
27
SHELL_BLACKLIST = set(['rm', 'ls'])
 
28
 
 
29
class BlackListedCommand(BzrError):
 
30
    def __init__(self, command):
 
31
        BzrError.__init__(self, "The command %s is blacklisted for shell use" %
 
32
                          command)
 
33
 
 
34
class PromptCmd(cmd.Cmd):
 
35
    def __init__(self):
 
36
        cmd.Cmd.__init__(self)
 
37
        self.prompt = "bzr> "
 
38
        try:
 
39
            self.tree = arch.tree_root(".")
 
40
        except:
 
41
            self.tree = None
 
42
        self.set_title()
 
43
        self.set_prompt()
 
44
        self.identchars += '-'
 
45
        self.history_file = os.path.expanduser("~/.bazaar/shell-history")
 
46
        readline.set_completer_delims(string.whitespace)
 
47
        if os.access(self.history_file, os.R_OK) and \
 
48
            os.path.isfile(self.history_file):
 
49
            readline.read_history_file(self.history_file)
 
50
        self.cwd = os.getcwd()
 
51
 
 
52
    def write_history(self):
 
53
        readline.write_history_file(self.history_file)
 
54
 
 
55
    def do_quit(self, args):
 
56
        self.write_history()
 
57
        raise StopIteration
 
58
 
 
59
    def do_exit(self, args):
 
60
        self.do_quit(args)
 
61
 
 
62
    def do_EOF(self, args):
 
63
        print
 
64
        self.do_quit(args)
 
65
 
 
66
    def postcmd(self, line, bar):
 
67
        self.set_title()
 
68
        self.set_prompt()
 
69
 
 
70
    def set_prompt(self):
 
71
        if self.tree is not None:
 
72
            try:
 
73
                prompt = pylon.alias_or_version(self.tree.tree_version, 
 
74
                                                self.tree, 
 
75
                                                full=False)
 
76
                if prompt is not None:
 
77
                    prompt = " " + prompt +":"+ pylon.tree_cwd(self.tree)
 
78
            except:
 
79
                prompt = ""
 
80
        else:
 
81
            prompt = ""
 
82
        self.prompt = "bzr%s> " % prompt
 
83
 
 
84
    def set_title(self, command=None):
 
85
        try:
 
86
            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
 
87
                                             full=False)
 
88
        except:
 
89
            version = "[no version]"
 
90
        if command is None:
 
91
            command = ""
 
92
        sys.stdout.write(terminal.term_title("bzr %s %s" % (command, version)))
 
93
 
 
94
    def do_cd(self, line):
 
95
        if line == "":
 
96
            line = "~"
 
97
        line = os.path.expanduser(line)
 
98
        if os.path.isabs(line):
 
99
            newcwd = line
 
100
        else:
 
101
            newcwd = self.cwd+'/'+line
 
102
        newcwd = os.path.normpath(newcwd)
 
103
        try:
 
104
            os.chdir(newcwd)
 
105
            self.cwd = newcwd
 
106
        except Exception, e:
 
107
            print e
 
108
        try:
 
109
            self.tree = arch.tree_root(".")
 
110
        except:
 
111
            self.tree = None
 
112
 
 
113
    def do_help(self, line):
 
114
        self.default("help "+line)
 
115
 
 
116
    def default(self, line):
 
117
        args = line.split()
 
118
        commandname = args.pop(0)
 
119
        for char in ('|', '<', '>'):
 
120
            commandname = commandname.split(char)[0]
 
121
        if commandname[-1] in ('|', '<', '>'):
 
122
            commandname = commandname[:-1]
 
123
        try:
 
124
            if commandname in SHELL_BLACKLIST:
 
125
                raise BlackListedCommand(commandname)
 
126
            cmd_obj = get_cmd_object(commandname)
 
127
        except (BlackListedCommand, BzrError):
 
128
            return os.system(line)
 
129
 
 
130
        try:
 
131
            if too_complicated(line):
 
132
                return os.system("bzr "+line)
 
133
            else:
 
134
                return (cmd_obj.run_argv(args) or 0)
 
135
        except BzrError, e:
 
136
            print e
 
137
        except KeyboardInterrupt, e:
 
138
            print "Interrupted"
 
139
        except Exception, e:
 
140
#            print "Unhandled error:\n%s" % errors.exception_str(e)
 
141
            print "Unhandled error:\n%s" % (e)
 
142
 
 
143
 
 
144
    def completenames(self, text, line, begidx, endidx):
 
145
        completions = []
 
146
        iter = iter_command_names()
 
147
        try:
 
148
            if len(line) > 0:
 
149
                arg = line.split()[-1]
 
150
            else:
 
151
                arg = ""
 
152
            iter = list(iter_munged_completions(iter, arg, text))
 
153
        except Exception, e:
 
154
            print e, type(e)
 
155
        return list(iter)
 
156
 
 
157
    def completedefault(self, text, line, begidx, endidx):
 
158
        """Perform completion for native commands.
 
159
        
 
160
        :param text: The text to complete
 
161
        :type text: str
 
162
        :param line: The entire line to complete
 
163
        :type line: str
 
164
        :param begidx: The start of the text in the line
 
165
        :type begidx: int
 
166
        :param endidx: The end of the text in the line
 
167
        :type endidx: int
 
168
        """
 
169
        (cmd, args, foo) = self.parseline(line)
 
170
        if cmd == "bzr":
 
171
            try:
 
172
                return self.completenames(text, line, begidx, endidx)
 
173
            except Exception, e:
 
174
                print e
 
175
        try:
 
176
            command_obj = get_cmd_object(cmd)
 
177
        except BzrError:
 
178
            command_obj = None
 
179
        try:
 
180
            if command_obj is not None:
 
181
                opts = []
 
182
                for option_name, option in command_obj.options().items():
 
183
                    opts.append("--" + option_name)
 
184
                    short_name = option.short_name()
 
185
                    if short_name:
 
186
                        opts.append("-" + short_name)
 
187
                q = list(iter_munged_completions(opts, args, text))
 
188
                return list(iter_munged_completions(opts, args, text))
 
189
            elif cmd == "cd":
 
190
                if len(args) > 0:
 
191
                    arg = args.split()[-1]
 
192
                else:
 
193
                    arg = ""
 
194
                iter = iter_dir_completions(arg)
 
195
                iter = iter_munged_completions(iter, arg, text)
 
196
                return list(iter)
 
197
            elif len(args)>0:
 
198
                arg = args.split()[-1]
 
199
                iter = iter_file_completions(arg)
 
200
                return list(iter_munged_completions(iter, arg, text))
 
201
            else:
 
202
                return self.completenames(text, line, begidx, endidx)
 
203
        except Exception, e:
 
204
            print e
 
205
 
 
206
def run_shell():
 
207
    try:
 
208
        prompt = PromptCmd()
 
209
        try:
 
210
            prompt.cmdloop()
 
211
        finally:
 
212
            prompt.write_history()
 
213
    except StopIteration:
 
214
        pass
 
215
 
 
216
def iter_file_completions(arg, only_dirs = False):
 
217
    """Generate an iterator that iterates through filename completions.
 
218
 
 
219
    :param arg: The filename fragment to match
 
220
    :type arg: str
 
221
    :param only_dirs: If true, match only directories
 
222
    :type only_dirs: bool
 
223
    """
 
224
    cwd = os.getcwd()
 
225
    if cwd != "/":
 
226
        extras = [".", ".."]
 
227
    else:
 
228
        extras = []
 
229
    (dir, file) = os.path.split(arg)
 
230
    if dir != "":
 
231
        listingdir = os.path.expanduser(dir)
 
232
    else:
 
233
        listingdir = cwd
 
234
    for file in chain(os.listdir(listingdir), extras):
 
235
        if dir != "":
 
236
            userfile = dir+'/'+file
 
237
        else:
 
238
            userfile = file
 
239
        if userfile.startswith(arg):
 
240
            if os.path.isdir(listingdir+'/'+file):
 
241
                userfile+='/'
 
242
                yield userfile
 
243
            elif not only_dirs:
 
244
                yield userfile
 
245
 
 
246
 
 
247
def iter_dir_completions(arg):
 
248
    """Generate an iterator that iterates through directory name completions.
 
249
 
 
250
    :param arg: The directory name fragment to match
 
251
    :type arg: str
 
252
    """
 
253
    return iter_file_completions(arg, True)
 
254
 
 
255
def iter_command_names(hidden=False):
 
256
    for real_cmd_name, cmd_class in get_all_cmds():
 
257
        if not hidden and cmd_class.hidden:
 
258
            continue
 
259
        for name in [real_cmd_name] + cmd_class.aliases:
 
260
            # Don't complete on aliases that are prefixes of the canonical name
 
261
            if name == real_cmd_name or not real_cmd_name.startswith(name):
 
262
                yield name
 
263
 
 
264
def iter_munged_completions(iter, arg, text):
 
265
    for completion in iter:
 
266
        completion = str(completion)
 
267
        if completion.startswith(arg):
 
268
            yield completion[len(arg)-len(text):]+" "
 
269
 
 
270
def too_complicated(line):
 
271
    for char in '|<>"\"':
 
272
        if char in line:
 
273
            return True
 
274
    return False