~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2007-12-22 02:01:03 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20071222020103-ggjszok7n974e1l2
Update branches, multi-pull to new APIs, create trees

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
25
import sys
 
26
 
 
27
from bzrlib.branch import Branch
 
28
from bzrlib.commands import get_cmd_object, get_all_cmds, get_alias
23
29
from bzrlib.errors import BzrError
24
 
from bzrlib.commands import get_cmd_object
 
30
from bzrlib.workingtree import WorkingTree
 
31
 
 
32
import terminal
 
33
 
 
34
 
 
35
SHELL_BLACKLIST = set(['rm', 'ls'])
 
36
COMPLETION_BLACKLIST = set(['shell'])
 
37
 
 
38
 
 
39
class BlackListedCommand(BzrError):
 
40
    def __init__(self, command):
 
41
        BzrError.__init__(self, "The command %s is blacklisted for shell use" %
 
42
                          command)
 
43
 
 
44
 
 
45
class CompletionContext(object):
 
46
    def __init__(self, text, command=None, prev_opt=None, arg_pos=None):
 
47
        self.text = text
 
48
        self.command = command
 
49
        self.prev_opt = prev_opt
 
50
        self.arg_pos = None
 
51
 
 
52
    def get_completions(self):
 
53
        try:
 
54
            return self.get_completions_or_raise()
 
55
        except Exception, e:
 
56
            print e, type(e)
 
57
            return []
 
58
 
 
59
    def get_option_completions(self):
 
60
        try:
 
61
            command_obj = get_cmd_object(self.command)
 
62
        except BzrError:
 
63
            return []
 
64
        opts = [o+" " for o in iter_opt_completions(command_obj)]
 
65
        return list(filter_completions(opts, self.text))
 
66
 
 
67
    def get_completions_or_raise(self):
 
68
        if self.command is None:
 
69
            if '/' in self.text:
 
70
                iter = iter_executables(self.text)
 
71
            else:
 
72
                iter = (c+" " for c in iter_command_names() if
 
73
                        c not in COMPLETION_BLACKLIST)
 
74
            return list(filter_completions(iter, self.text))
 
75
        if self.prev_opt is None:
 
76
            completions = self.get_option_completions()
 
77
            if self.command == "cd":
 
78
                iter = iter_dir_completions(self.text)
 
79
                completions.extend(list(filter_completions(iter, self.text)))
 
80
            else:
 
81
                iter = iter_file_completions(self.text)
 
82
                completions.extend(filter_completions(iter, self.text))
 
83
            return completions
 
84
 
25
85
 
26
86
class PromptCmd(cmd.Cmd):
27
87
    def __init__(self):
28
88
        cmd.Cmd.__init__(self)
29
89
        self.prompt = "bzr> "
30
90
        try:
31
 
            self.tree = arch.tree_root(".")
 
91
            self.tree = WorkingTree.open_containing('.')[0]
32
92
        except:
33
93
            self.tree = None
34
94
        self.set_title()
46
106
 
47
107
    def do_quit(self, args):
48
108
        self.write_history()
49
 
        sys.exit(0)
 
109
        raise StopIteration
50
110
 
51
111
    def do_exit(self, args):
52
112
        self.do_quit(args)
62
122
    def set_prompt(self):
63
123
        if self.tree is not None:
64
124
            try:
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)
 
125
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
 
126
                               self.tree.relpath('.'))
 
127
                prompt = " %s:%d/%s" % prompt_data
70
128
            except:
71
129
                prompt = ""
72
130
        else:
75
133
 
76
134
    def set_title(self, command=None):
77
135
        try:
78
 
            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
79
 
                                             full=False)
 
136
            b = Branch.open_containing('.')[0]
 
137
            version = "%s:%d" % (b.nick, b.revno())
80
138
        except:
81
139
            version = "[no version]"
82
140
        if command is None:
98
156
        except Exception, e:
99
157
            print e
100
158
        try:
101
 
            self.tree = arch.tree_root(".")
 
159
            self.tree = WorkingTree.open_containing(".")[0]
102
160
        except:
103
161
            self.tree = None
104
162
 
105
163
    def do_help(self, line):
106
 
        Help()(line)
 
164
        self.default("help "+line)
107
165
 
108
166
    def default(self, line):
109
 
        args = line.split()
 
167
        args = shlex.split(line)
 
168
        alias_args = get_alias(args[0])
 
169
        if alias_args is not None:
 
170
            args[0] = alias_args.pop(0)
 
171
 
110
172
        commandname = args.pop(0)
 
173
        for char in ('|', '<', '>'):
 
174
            commandname = commandname.split(char)[0]
 
175
        if commandname[-1] in ('|', '<', '>'):
 
176
            commandname = commandname[:-1]
111
177
        try:
 
178
            if commandname in SHELL_BLACKLIST:
 
179
                raise BlackListedCommand(commandname)
112
180
            cmd_obj = get_cmd_object(commandname)
113
 
        except BzrError:
 
181
        except (BlackListedCommand, BzrError):
114
182
            return os.system(line)
115
183
 
116
 
 
117
184
        try:
118
 
            return (cmd_obj.run_argv(args) or 0)
 
185
            if too_complicated(line):
 
186
                return os.system("bzr "+line)
 
187
            else:
 
188
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
119
189
        except BzrError, e:
120
190
            print e
121
191
        except KeyboardInterrupt, e:
126
196
 
127
197
 
128
198
    def completenames(self, text, line, begidx, endidx):
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)
 
199
        return CompletionContext(text).get_completions()
140
200
 
141
201
    def completedefault(self, text, line, begidx, endidx):
142
202
        """Perform completion for native commands.
143
 
        
 
203
 
144
204
        :param text: The text to complete
145
205
        :type text: str
146
206
        :param line: The entire line to complete
150
210
        :param endidx: The end of the text in the line
151
211
        :type endidx: int
152
212
        """
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
 
213
        (cmd, args, foo) = self.parseline(line)
 
214
        if cmd == "bzr":
 
215
            cmd = None
 
216
        return CompletionContext(text, command=cmd).get_completions()
 
217
 
189
218
 
190
219
def run_shell():
191
 
    prompt = PromptCmd()
192
220
    try:
193
 
        prompt.cmdloop()
194
 
    finally:
195
 
        prompt.write_history()
 
221
        prompt = PromptCmd()
 
222
        try:
 
223
            prompt.cmdloop()
 
224
        finally:
 
225
            prompt.write_history()
 
226
    except StopIteration:
 
227
        pass
 
228
 
 
229
 
 
230
def iter_opt_completions(command_obj):
 
231
    for option_name, option in command_obj.options().items():
 
232
        yield "--" + option_name
 
233
        short_name = option.short_name()
 
234
        if short_name:
 
235
            yield "-" + short_name
 
236
 
196
237
 
197
238
def iter_file_completions(arg, only_dirs = False):
198
239
    """Generate an iterator that iterates through filename completions.
212
253
        listingdir = os.path.expanduser(dir)
213
254
    else:
214
255
        listingdir = cwd
215
 
    for file in iter_combine([os.listdir(listingdir), extras]):
 
256
    for file in chain(os.listdir(listingdir), extras):
216
257
        if dir != "":
217
258
            userfile = dir+'/'+file
218
259
        else:
222
263
                userfile+='/'
223
264
                yield userfile
224
265
            elif not only_dirs:
225
 
                yield userfile
 
266
                yield userfile + ' '
226
267
 
227
268
 
228
269
def iter_dir_completions(arg):
232
273
    :type arg: str
233
274
    """
234
275
    return iter_file_completions(arg, True)
 
276
 
 
277
 
 
278
def iter_command_names(hidden=False):
 
279
    for real_cmd_name, cmd_class in get_all_cmds():
 
280
        if not hidden and cmd_class.hidden:
 
281
            continue
 
282
        for name in [real_cmd_name] + cmd_class.aliases:
 
283
            # Don't complete on aliases that are prefixes of the canonical name
 
284
            if name == real_cmd_name or not real_cmd_name.startswith(name):
 
285
                yield name
 
286
 
 
287
 
 
288
def iter_executables(path):
 
289
    dirname, partial = os.path.split(path)
 
290
    for filename in os.listdir(dirname):
 
291
        if not filename.startswith(partial):
 
292
            continue
 
293
        fullpath = os.path.join(dirname, filename)
 
294
        mode=os.lstat(fullpath)[stat.ST_MODE]
 
295
        if stat.S_ISREG(mode) and 0111 & mode:
 
296
            yield fullpath + ' '
 
297
 
 
298
 
 
299
def filter_completions(iter, arg):
 
300
    return (c for c in iter if c.startswith(arg))
 
301
 
 
302
 
 
303
def iter_munged_completions(iter, arg, text):
 
304
    for completion in iter:
 
305
        completion = str(completion)
 
306
        if completion.startswith(arg):
 
307
            yield completion[len(arg)-len(text):]+" "
 
308
 
 
309
 
 
310
def too_complicated(line):
 
311
    for char in '|<>*?':
 
312
        if char in line:
 
313
            return True
 
314
    return False