~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to shell.py

  • Committer: Aaron Bentley
  • Date: 2008-02-13 04:35:59 UTC
  • Revision ID: aaron@aaronbentley.com-20080213043559-rk1rlfx4nb9pfe1l
Handle missing patch, fix tabs

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 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
23
31
from bzrlib.errors import BzrError
24
 
from bzrlib.commands import get_cmd_object
 
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
 
25
87
 
26
88
class PromptCmd(cmd.Cmd):
 
89
 
27
90
    def __init__(self):
28
91
        cmd.Cmd.__init__(self)
29
92
        self.prompt = "bzr> "
30
93
        try:
31
 
            self.tree = arch.tree_root(".")
 
94
            self.tree = WorkingTree.open_containing('.')[0]
32
95
        except:
33
96
            self.tree = None
34
97
        self.set_title()
35
98
        self.set_prompt()
36
99
        self.identchars += '-'
37
 
        self.history_file = os.path.expanduser("~/.bazaar/shell-history")
 
100
        ensure_config_dir_exists()
 
101
        self.history_file = osutils.pathjoin(config_dir(), 'shell-history')
38
102
        readline.set_completer_delims(string.whitespace)
39
103
        if os.access(self.history_file, os.R_OK) and \
40
104
            os.path.isfile(self.history_file):
46
110
 
47
111
    def do_quit(self, args):
48
112
        self.write_history()
49
 
        sys.exit(0)
 
113
        raise StopIteration
50
114
 
51
115
    def do_exit(self, args):
52
116
        self.do_quit(args)
62
126
    def set_prompt(self):
63
127
        if self.tree is not None:
64
128
            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)
 
129
                prompt_data = (self.tree.branch.nick, self.tree.branch.revno(),
 
130
                               self.tree.relpath('.'))
 
131
                prompt = " %s:%d/%s" % prompt_data
70
132
            except:
71
133
                prompt = ""
72
134
        else:
75
137
 
76
138
    def set_title(self, command=None):
77
139
        try:
78
 
            version = pylon.alias_or_version(self.tree.tree_version, self.tree, 
79
 
                                             full=False)
 
140
            b = Branch.open_containing('.')[0]
 
141
            version = "%s:%d" % (b.nick, b.revno())
80
142
        except:
81
143
            version = "[no version]"
82
144
        if command is None:
98
160
        except Exception, e:
99
161
            print e
100
162
        try:
101
 
            self.tree = arch.tree_root(".")
 
163
            self.tree = WorkingTree.open_containing(".")[0]
102
164
        except:
103
165
            self.tree = None
104
166
 
105
167
    def do_help(self, line):
106
 
        Help()(line)
 
168
        self.default("help "+line)
107
169
 
108
170
    def default(self, line):
109
 
        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
 
110
176
        commandname = args.pop(0)
 
177
        for char in ('|', '<', '>'):
 
178
            commandname = commandname.split(char)[0]
 
179
        if commandname[-1] in ('|', '<', '>'):
 
180
            commandname = commandname[:-1]
111
181
        try:
 
182
            if commandname in SHELL_BLACKLIST:
 
183
                raise BlackListedCommand(commandname)
112
184
            cmd_obj = get_cmd_object(commandname)
113
 
        except BzrError:
 
185
        except (BlackListedCommand, BzrError):
114
186
            return os.system(line)
115
187
 
116
 
 
117
188
        try:
118
 
            return (cmd_obj.run_argv(args) or 0)
 
189
            if too_complicated(line):
 
190
                return os.system("bzr "+line)
 
191
            else:
 
192
                return (cmd_obj.run_argv_aliases(args, alias_args) or 0)
119
193
        except BzrError, e:
120
194
            print e
121
195
        except KeyboardInterrupt, e:
126
200
 
127
201
 
128
202
    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)
 
203
        return CompletionContext(text).get_completions()
140
204
 
141
205
    def completedefault(self, text, line, begidx, endidx):
142
206
        """Perform completion for native commands.
143
 
        
 
207
 
144
208
        :param text: The text to complete
145
209
        :type text: str
146
210
        :param line: The entire line to complete
150
214
        :param endidx: The end of the text in the line
151
215
        :type endidx: int
152
216
        """
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
 
217
        (cmd, args, foo) = self.parseline(line)
 
218
        if cmd == "bzr":
 
219
            cmd = None
 
220
        return CompletionContext(text, command=cmd).get_completions()
 
221
 
189
222
 
190
223
def run_shell():
191
 
    prompt = PromptCmd()
192
224
    try:
193
 
        prompt.cmdloop()
194
 
    finally:
195
 
        prompt.write_history()
 
225
        prompt = PromptCmd()
 
226
        try:
 
227
            prompt.cmdloop()
 
228
        finally:
 
229
            prompt.write_history()
 
230
    except StopIteration:
 
231
        pass
 
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
 
196
241
 
197
242
def iter_file_completions(arg, only_dirs = False):
198
243
    """Generate an iterator that iterates through filename completions.
212
257
        listingdir = os.path.expanduser(dir)
213
258
    else:
214
259
        listingdir = cwd
215
 
    for file in iter_combine([os.listdir(listingdir), extras]):
 
260
    for file in chain(os.listdir(listingdir), extras):
216
261
        if dir != "":
217
262
            userfile = dir+'/'+file
218
263
        else:
222
267
                userfile+='/'
223
268
                yield userfile
224
269
            elif not only_dirs:
225
 
                yield userfile
 
270
                yield userfile + ' '
226
271
 
227
272
 
228
273
def iter_dir_completions(arg):
232
277
    :type arg: str
233
278
    """
234
279
    return iter_file_completions(arg, True)
 
280
 
 
281
 
 
282
def iter_command_names(hidden=False):
 
283
    for real_cmd_name, cmd_class in get_all_cmds():
 
284
        if not hidden and cmd_class.hidden:
 
285
            continue
 
286
        for name in [real_cmd_name] + cmd_class.aliases:
 
287
            # Don't complete on aliases that are prefixes of the canonical name
 
288
            if name == real_cmd_name or not real_cmd_name.startswith(name):
 
289
                yield name
 
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
 
 
307
def iter_munged_completions(iter, arg, text):
 
308
    for completion in iter:
 
309
        completion = str(completion)
 
310
        if completion.startswith(arg):
 
311
            yield completion[len(arg)-len(text):]+" "
 
312
 
 
313
 
 
314
def too_complicated(line):
 
315
    for char in '|<>*?':
 
316
        if char in line:
 
317
            return True
 
318
    return False