/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/plugins/zsh_completion/zshcomp.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2019-10-13 17:31:55 UTC
  • mfrom: (7397.4.9 remove-unused)
  • Revision ID: breezy.the.bot@gmail.com-20191013173155-yoiokny4mknxb3um
Remove Tree.has_id.

Merged from https://code.launchpad.net/~jelmer/brz/remove-unused/+merge/373320

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
# Copyright (C) 2009, 2010 Canonical Ltd
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
18
 
 
19
from __future__ import absolute_import
 
20
 
 
21
from ... import (
 
22
    cmdline,
 
23
    commands,
 
24
    config,
 
25
    help_topics,
 
26
    option,
 
27
    plugin,
 
28
)
 
29
from ...sixish import (
 
30
    text_type,
 
31
    )
 
32
import breezy
 
33
import re
 
34
import sys
 
35
 
 
36
 
 
37
class ZshCodeGen(object):
 
38
    """Generate a zsh script for given completion data."""
 
39
 
 
40
    def __init__(self, data, function_name='_brz', debug=False):
 
41
        self.data = data
 
42
        self.function_name = function_name
 
43
        self.debug = debug
 
44
 
 
45
    def script(self):
 
46
        return ("""\
 
47
#compdef brz bzr
 
48
 
 
49
%(function_name)s ()
 
50
{
 
51
    local ret=1
 
52
    local -a args
 
53
    args+=(
 
54
%(global-options)s
 
55
    )
 
56
 
 
57
    _arguments $args[@] && ret=0
 
58
 
 
59
    return ret
 
60
}
 
61
 
 
62
%(function_name)s
 
63
""" % {
 
64
            'global-options': self.global_options(),
 
65
            'function_name': self.function_name})
 
66
 
 
67
    def global_options(self):
 
68
        lines = []
 
69
        for (long, short, help) in self.data.global_options:
 
70
            lines.append(
 
71
                '      \'(%s%s)%s[%s]\'' % (
 
72
                    (short + ' ') if short else '',
 
73
                    long,
 
74
                    long,
 
75
                    help))
 
76
 
 
77
        return "\n".join(lines)
 
78
 
 
79
 
 
80
class CompletionData(object):
 
81
 
 
82
    def __init__(self):
 
83
        self.plugins = {}
 
84
        self.global_options = []
 
85
        self.commands = []
 
86
 
 
87
    def all_command_aliases(self):
 
88
        for c in self.commands:
 
89
            for a in c.aliases:
 
90
                yield a
 
91
 
 
92
 
 
93
class CommandData(object):
 
94
 
 
95
    def __init__(self, name):
 
96
        self.name = name
 
97
        self.aliases = [name]
 
98
        self.plugin = None
 
99
        self.options = []
 
100
        self.fixed_words = None
 
101
 
 
102
 
 
103
class PluginData(object):
 
104
 
 
105
    def __init__(self, name, version=None):
 
106
        if version is None:
 
107
            try:
 
108
                version = breezy.plugin.plugins()[name].__version__
 
109
            except:
 
110
                version = 'unknown'
 
111
        self.name = name
 
112
        self.version = version
 
113
 
 
114
    def __str__(self):
 
115
        if self.version == 'unknown':
 
116
            return self.name
 
117
        return '%s %s' % (self.name, self.version)
 
118
 
 
119
 
 
120
class OptionData(object):
 
121
 
 
122
    def __init__(self, name):
 
123
        self.name = name
 
124
        self.registry_keys = None
 
125
        self.error_messages = []
 
126
 
 
127
    def __str__(self):
 
128
        return self.name
 
129
 
 
130
    def __cmp__(self, other):
 
131
        return cmp(self.name, other.name)
 
132
 
 
133
    def __lt__(self, other):
 
134
        return self.name < other.name
 
135
 
 
136
 
 
137
class DataCollector(object):
 
138
 
 
139
    def __init__(self, no_plugins=False, selected_plugins=None):
 
140
        self.data = CompletionData()
 
141
        self.user_aliases = {}
 
142
        if no_plugins:
 
143
            self.selected_plugins = set()
 
144
        elif selected_plugins is None:
 
145
            self.selected_plugins = None
 
146
        else:
 
147
            self.selected_plugins = {x.replace('-', '_')
 
148
                                     for x in selected_plugins}
 
149
 
 
150
    def collect(self):
 
151
        self.global_options()
 
152
        self.aliases()
 
153
        self.commands()
 
154
        return self.data
 
155
 
 
156
    def global_options(self):
 
157
        for name, item in option.Option.OPTIONS.items():
 
158
            self.data.global_options.append(
 
159
                ('--' + item.name,
 
160
                 '-' + item.short_name() if item.short_name() else None,
 
161
                 item.help.rstrip()))
 
162
 
 
163
    def aliases(self):
 
164
        for alias, expansion in config.GlobalConfig().get_aliases().items():
 
165
            for token in cmdline.split(expansion):
 
166
                if not token.startswith("-"):
 
167
                    self.user_aliases.setdefault(token, set()).add(alias)
 
168
                    break
 
169
 
 
170
    def commands(self):
 
171
        for name in sorted(commands.all_command_names()):
 
172
            self.command(name)
 
173
 
 
174
    def command(self, name):
 
175
        cmd = commands.get_cmd_object(name)
 
176
        cmd_data = CommandData(name)
 
177
 
 
178
        plugin_name = cmd.plugin_name()
 
179
        if plugin_name is not None:
 
180
            if (self.selected_plugins is not None and
 
181
                    plugin not in self.selected_plugins):
 
182
                return None
 
183
            plugin_data = self.data.plugins.get(plugin_name)
 
184
            if plugin_data is None:
 
185
                plugin_data = PluginData(plugin_name)
 
186
                self.data.plugins[plugin_name] = plugin_data
 
187
            cmd_data.plugin = plugin_data
 
188
        self.data.commands.append(cmd_data)
 
189
 
 
190
        # Find all aliases to the command; both cmd-defined and user-defined.
 
191
        # We assume a user won't override one command with a different one,
 
192
        # but will choose completely new names or add options to existing
 
193
        # ones while maintaining the actual command name unchanged.
 
194
        cmd_data.aliases.extend(cmd.aliases)
 
195
        cmd_data.aliases.extend(sorted([useralias
 
196
                                        for cmdalias in cmd_data.aliases
 
197
                                        if cmdalias in self.user_aliases
 
198
                                        for useralias in self.user_aliases[cmdalias]
 
199
                                        if useralias not in cmd_data.aliases]))
 
200
 
 
201
        opts = cmd.options()
 
202
        for optname, opt in sorted(opts.items()):
 
203
            cmd_data.options.extend(self.option(opt))
 
204
 
 
205
        if 'help' == name or 'help' in cmd.aliases:
 
206
            cmd_data.fixed_words = ('($cmds %s)' %
 
207
                                    " ".join(sorted(help_topics.topic_registry.keys())))
 
208
 
 
209
        return cmd_data
 
210
 
 
211
    def option(self, opt):
 
212
        optswitches = {}
 
213
        parser = option.get_optparser([opt])
 
214
        parser = self.wrap_parser(optswitches, parser)
 
215
        optswitches.clear()
 
216
        opt.add_option(parser, opt.short_name())
 
217
        if isinstance(opt, option.RegistryOption) and opt.enum_switch:
 
218
            enum_switch = '--%s' % opt.name
 
219
            enum_data = optswitches.get(enum_switch)
 
220
            if enum_data:
 
221
                try:
 
222
                    enum_data.registry_keys = opt.registry.keys()
 
223
                except ImportError as e:
 
224
                    enum_data.error_messages.append(
 
225
                        "ERROR getting registry keys for '--%s': %s"
 
226
                        % (opt.name, str(e).split('\n')[0]))
 
227
        return sorted(optswitches.values())
 
228
 
 
229
    def wrap_container(self, optswitches, parser):
 
230
        def tweaked_add_option(*opts, **attrs):
 
231
            for name in opts:
 
232
                optswitches[name] = OptionData(name)
 
233
        parser.add_option = tweaked_add_option
 
234
        return parser
 
235
 
 
236
    def wrap_parser(self, optswitches, parser):
 
237
        orig_add_option_group = parser.add_option_group
 
238
 
 
239
        def tweaked_add_option_group(*opts, **attrs):
 
240
            return self.wrap_container(optswitches,
 
241
                                       orig_add_option_group(*opts, **attrs))
 
242
        parser.add_option_group = tweaked_add_option_group
 
243
        return self.wrap_container(optswitches, parser)
 
244
 
 
245
 
 
246
def zsh_completion_function(out, function_name="_brz",
 
247
                            debug=False,
 
248
                            no_plugins=False, selected_plugins=None):
 
249
    dc = DataCollector(no_plugins=no_plugins,
 
250
                       selected_plugins=selected_plugins)
 
251
    data = dc.collect()
 
252
    cg = ZshCodeGen(data, function_name=function_name, debug=debug)
 
253
    res = cg.script()
 
254
    out.write(res)
 
255
 
 
256
 
 
257
class cmd_zsh_completion(commands.Command):
 
258
    __doc__ = """Generate a shell function for zsh command line completion.
 
259
 
 
260
    This command generates a shell function which can be used by zsh to
 
261
    automatically complete the currently typed command when the user presses
 
262
    the completion key (usually tab).
 
263
 
 
264
    Commonly used like this:
 
265
        eval "`brz zsh -completion`"
 
266
    """
 
267
 
 
268
    takes_options = [
 
269
        option.Option("function-name", short_name="f", type=text_type, argname="name",
 
270
                      help="Name of the generated function (default: _brz)"),
 
271
        option.Option("debug", type=None, hidden=True,
 
272
                      help="Enable shell code useful for debugging"),
 
273
        option.ListOption("plugin", type=text_type, argname="name",
 
274
                          # param_name="selected_plugins", # doesn't work, bug #387117
 
275
                          help="Enable completions for the selected plugin"
 
276
                          + " (default: all plugins)"),
 
277
        ]
 
278
 
 
279
    def run(self, **kwargs):
 
280
        if 'plugin' in kwargs:
 
281
            # work around bug #387117 which prevents us from using param_name
 
282
            if len(kwargs['plugin']) > 0:
 
283
                kwargs['selected_plugins'] = kwargs['plugin']
 
284
            del kwargs['plugin']
 
285
        zsh_completion_function(sys.stdout, **kwargs)