bashcomp.py :  » Development » Bazaar » bzr-2.2b3 » bzrlib » plugins » bash_completion » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Bazaar 
Bazaar » bzr 2.2b3 » bzrlib » plugins » bash_completion » bashcomp.py
#!/usr/bin/env python

# Copyright (C) 2009, 2010 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

from bzrlib import (
    cmdline,
    commands,
    config,
    help_topics,
    option,
    plugin,
)
import bzrlib
import re


class BashCodeGen(object):
    """Generate a bash script for given completion data."""

    def __init__(self, data, function_name='_bzr', debug=False):
        self.data = data
        self.function_name = function_name
        self.debug = debug

    def script(self):
        return ("""\
# Programmable completion for the Bazaar-NG bzr command under bash.
# Known to work with bash 2.05a as well as bash 4.1.2, and probably
# all versions in between as well.

# Based originally on the svn bash completition script.
# Customized by Sven Wilhelm/Icecrash.com
# Adjusted for automatic generation by Martin von Gagern

# Generated using the bash_completion plugin.
# See https://launchpad.net/bzr-bash-completion for details.

# Commands and options of bzr %(bzr_version)s

shopt -s progcomp
%(function)s
complete -F %(function_name)s -o default bzr
"""     % {
            "function_name": self.function_name,
            "function": self.function(),
            "bzr_version": self.bzr_version(),
        })

    def function(self):
        return ("""\
%(function_name)s ()
{
  local cur cmds cmdIdx cmd cmdOpts fixedWords i globalOpts
  local curOpt optEnums

  COMPREPLY=()
  cur=${COMP_WORDS[COMP_CWORD]}

  cmds='%(cmds)s'
  globalOpts='%(global_options)s'

  # do ordinary expansion if we are anywhere after a -- argument
  for ((i = 1; i < COMP_CWORD; ++i)); do
    [[ ${COMP_WORDS[i]} == "--" ]] && return 0
  done

  # find the command; it's the first word not starting in -
  cmd=
  for ((cmdIdx = 1; cmdIdx < ${#COMP_WORDS[@]}; ++cmdIdx)); do
    if [[ ${COMP_WORDS[cmdIdx]} != -* ]]; then
      cmd=${COMP_WORDS[cmdIdx]}
      break
    fi
  done

  # complete command name if we are not already past the command
  if [[ $COMP_CWORD -le cmdIdx ]]; then
    COMPREPLY=( $( compgen -W "$cmds $globalOpts" -- $cur ) )
    return 0
  fi

  # find the option for which we want to complete a value
  curOpt=
  if [[ $cur != -* ]] && [[ $COMP_CWORD -gt 1 ]]; then
    curOpt=${COMP_WORDS[COMP_CWORD - 1]}
    if [[ $curOpt == = ]]; then
      curOpt=${COMP_WORDS[COMP_CWORD - 2]}
    elif [[ $cur == : ]]; then
      cur=
      curOpt="$curOpt:"
    elif [[ $curOpt == : ]]; then
      curOpt=${COMP_WORDS[COMP_CWORD - 2]}:
    fi
  fi
%(debug)s
  cmdOpts=
  optEnums=
  fixedWords=
  case $cmd in
%(cases)s\
  *)
    cmdOpts='--help -h'
    ;;
  esac

  if [[ -z $fixedWords ]] && [[ -z $optEnums ]] && [[ $cur != -* ]]; then
    case $curOpt in
      tag:*)
        fixedWords="$(bzr tags 2>/dev/null | sed 's/  *[^ ]*$//')"
        ;;
    esac
  elif [[ $cur == = ]] && [[ -n $optEnums ]]; then
    # complete directly after "--option=", list all enum values
    COMPREPLY=( $optEnums )
    return 0
  else
    fixedWords="$cmdOpts $globalOpts $optEnums $fixedWords"
  fi

  if [[ -n $fixedWords ]]; then
    COMPREPLY=( $( compgen -W "$fixedWords" -- $cur ) )
  fi

  return 0
}
"""     % {
            "cmds": self.command_names(),
            "function_name": self.function_name,
            "cases": self.command_cases(),
            "global_options": self.global_options(),
            "debug": self.debug_output(),
        })

    def command_names(self):
        return " ".join(self.data.all_command_aliases())

    def debug_output(self):
        if not self.debug:
            return ''
        else:
            return (r"""
  # Debugging code enabled using the --debug command line switch.
  # Will dump some variables to the top portion of the terminal.
  echo -ne '\e[s\e[H'
  for (( i=0; i < ${#COMP_WORDS[@]}; ++i)); do
    echo "\$COMP_WORDS[$i]='${COMP_WORDS[i]}'"$'\e[K'
  done
  for i in COMP_CWORD COMP_LINE COMP_POINT COMP_TYPE COMP_KEY cur curOpt; do
    echo "\$${i}=\"${!i}\""$'\e[K'
  done
  echo -ne '---\e[K\e[u'
""")

    def bzr_version(self):
        bzr_version = bzrlib.version_string
        if not self.data.plugins:
            bzr_version += "."
        else:
            bzr_version += " and the following plugins:"
            for name, plugin in sorted(self.data.plugins.iteritems()):
                bzr_version += "\n# %s" % plugin
        return bzr_version

    def global_options(self):
        return " ".join(sorted(self.data.global_options))

    def command_cases(self):
        cases = ""
        for command in self.data.commands:
            cases += self.command_case(command)
        return cases

    def command_case(self, command):
        case = "\t%s)\n" % "|".join(command.aliases)
        if command.plugin:
            case += "\t\t# plugin \"%s\"\n" % command.plugin
        options = []
        enums = []
        for option in command.options:
            for message in option.error_messages:
                case += "\t\t# %s\n" % message
            if option.registry_keys:
                for key in option.registry_keys:
                    options.append("%s=%s" % (option, key))
                enums.append("%s) optEnums='%s' ;;" %
                             (option, ' '.join(option.registry_keys)))
            else:
                options.append(str(option))
        case += "\t\tcmdOpts='%s'\n" % " ".join(options)
        if command.fixed_words:
            fixed_words = command.fixed_words
            if isinstance(fixed_words, list):
                fixed_words = "'%s'" + ' '.join(fixed_words)
            case += "\t\tfixedWords=%s\n" % fixed_words
        if enums:
            case += "\t\tcase $curOpt in\n\t\t\t"
            case += "\n\t\t\t".join(enums)
            case += "\n\t\tesac\n"
        case += "\t\t;;\n"
        return case


class CompletionData(object):

    def __init__(self):
        self.plugins = {}
        self.global_options = set()
        self.commands = []

    def all_command_aliases(self):
        for c in self.commands:
            for a in c.aliases:
                yield a


class CommandData(object):

    def __init__(self, name):
        self.name = name
        self.aliases = [name]
        self.plugin = None
        self.options = []
        self.fixed_words = None


class PluginData(object):

    def __init__(self, name, version=None):
        if version is None:
            version = bzrlib.plugin.plugins()[name].__version__
        self.name = name
        self.version = version

    def __str__(self):
        if self.version == 'unknown':
            return self.name
        return '%s %s' % (self.name, self.version)


class OptionData(object):

    def __init__(self, name):
        self.name = name
        self.registry_keys = None
        self.error_messages = []

    def __str__(self):
        return self.name

    def __cmp__(self, other):
        return cmp(self.name, other.name)


class DataCollector(object):

    def __init__(self, no_plugins=False, selected_plugins=None):
        self.data = CompletionData()
        self.user_aliases = {}
        if no_plugins:
            self.selected_plugins = set()
        elif selected_plugins is None:
            self.selected_plugins = None
        else:
            self.selected_plugins = set([x.replace('-', '_')
                                         for x in selected_plugins])

    def collect(self):
        self.global_options()
        self.aliases()
        self.commands()
        return self.data

    def global_options(self):
        re_switch = re.compile(r'\n(--[A-Za-z0-9-_]+)(?:, (-\S))?\s')
        help_text = help_topics.topic_registry.get_detail('global-options')
        for long, short in re_switch.findall(help_text):
            self.data.global_options.add(long)
            if short:
                self.data.global_options.add(short)

    def aliases(self):
        for alias, expansion in config.GlobalConfig().get_aliases().iteritems():
            for token in cmdline.split(expansion):
                if not token.startswith("-"):
                    self.user_aliases.setdefault(token, set()).add(alias)
                    break

    def commands(self):
        for name in sorted(commands.all_command_names()):
            self.command(name)

    def command(self, name):
        cmd = commands.get_cmd_object(name)
        cmd_data = CommandData(name)

        plugin_name = cmd.plugin_name()
        if plugin_name is not None:
            if (self.selected_plugins is not None and
                plugin not in self.selected_plugins):
                return None
            plugin_data = self.data.plugins.get(plugin_name)
            if plugin_data is None:
                plugin_data = PluginData(plugin_name)
                self.data.plugins[plugin_name] = plugin_data
            cmd_data.plugin = plugin_data
        self.data.commands.append(cmd_data)

        # Find all aliases to the command; both cmd-defined and user-defined.
        # We assume a user won't override one command with a different one,
        # but will choose completely new names or add options to existing
        # ones while maintaining the actual command name unchanged.
        cmd_data.aliases.extend(cmd.aliases)
        cmd_data.aliases.extend(sorted([useralias
            for cmdalias in cmd_data.aliases
            if cmdalias in self.user_aliases
            for useralias in self.user_aliases[cmdalias]
            if useralias not in cmd_data.aliases]))

        opts = cmd.options()
        for optname, opt in sorted(opts.iteritems()):
            cmd_data.options.extend(self.option(opt))

        if 'help' == name or 'help' in cmd.aliases:
            cmd_data.fixed_words = ('"$cmds %s"' %
                " ".join(sorted(help_topics.topic_registry.keys())))

        return cmd_data

    def option(self, opt):
        optswitches = {}
        parser = option.get_optparser({opt.name: opt})
        parser = self.wrap_parser(optswitches, parser)
        optswitches.clear()
        opt.add_option(parser, opt.short_name())
        if isinstance(opt, option.RegistryOption) and opt.enum_switch:
            enum_switch = '--%s' % opt.name
            enum_data = optswitches.get(enum_switch)
            if enum_data:
                try:
                    enum_data.registry_keys = opt.registry.keys()
                except ImportError, e:
                    enum_data.error_messages.append(
                        "ERROR getting registry keys for '--%s': %s"
                        % (opt.name, str(e).split('\n')[0]))
        return sorted(optswitches.values())

    def wrap_container(self, optswitches, parser):
        def tweaked_add_option(*opts, **attrs):
            for name in opts:
                optswitches[name] = OptionData(name)
        parser.add_option = tweaked_add_option
        return parser

    def wrap_parser(self, optswitches, parser):
        orig_add_option_group = parser.add_option_group
        def tweaked_add_option_group(*opts, **attrs):
            return self.wrap_container(optswitches,
                orig_add_option_group(*opts, **attrs))
        parser.add_option_group = tweaked_add_option_group
        return self.wrap_container(optswitches, parser)


def bash_completion_function(out, function_name="_bzr", function_only=False,
                             debug=False,
                             no_plugins=False, selected_plugins=None):
    dc = DataCollector(no_plugins=no_plugins, selected_plugins=selected_plugins)
    data = dc.collect()
    cg = BashCodeGen(data, function_name=function_name, debug=debug)
    if function_only:
        res = cg.function()
    else:
        res = cg.script()
    out.write(res)


class cmd_bash_completion(commands.Command):
    __doc__ = """Generate a shell function for bash command line completion.

    This command generates a shell function which can be used by bash to
    automatically complete the currently typed command when the user presses
    the completion key (usually tab).
    
    Commonly used like this:
        eval "`bzr bash-completion`"
    """

    takes_options = [
        option.Option("function-name", short_name="f", type=str, argname="name",
               help="Name of the generated function (default: _bzr)"),
        option.Option("function-only", short_name="o", type=None,
               help="Generate only the shell function, don't enable it"),
        option.Option("debug", type=None, hidden=True,
               help="Enable shell code useful for debugging"),
        option.ListOption("plugin", type=str, argname="name",
                # param_name="selected_plugins", # doesn't work, bug #387117
                help="Enable completions for the selected plugin"
                + " (default: all plugins)"),
        ]

    def run(self, **kwargs):
        import sys
        from bashcomp import bash_completion_function
        if 'plugin' in kwargs:
            # work around bug #387117 which prevents us from using param_name
            if len(kwargs['plugin']) > 0:
                kwargs['selected_plugins'] = kwargs['plugin']
            del kwargs['plugin']
        bash_completion_function(sys.stdout, **kwargs)


if __name__ == '__main__':

    import sys
    import locale
    import optparse

    def plugin_callback(option, opt, value, parser):
        values = parser.values.selected_plugins
        if value == '-':
            del values[:]
        else:
            values.append(value)

    parser = optparse.OptionParser(usage="%prog [-f NAME] [-o]")
    parser.add_option("--function-name", "-f", metavar="NAME",
                      help="Name of the generated function (default: _bzr)")
    parser.add_option("--function-only", "-o", action="store_true",
                      help="Generate only the shell function, don't enable it")
    parser.add_option("--debug", action="store_true",
                      help=optparse.SUPPRESS_HELP)
    parser.add_option("--no-plugins", action="store_true",
                      help="Don't load any bzr plugins")
    parser.add_option("--plugin", metavar="NAME", type="string",
                      dest="selected_plugins", default=[],
                      action="callback", callback=plugin_callback,
                      help="Enable completions for the selected plugin"
                      + " (default: all plugins)")
    (opts, args) = parser.parse_args()
    if args:
        parser.error("script does not take positional arguments")
    kwargs = dict()
    for name, value in opts.__dict__.iteritems():
        if value is not None:
            kwargs[name] = value

    locale.setlocale(locale.LC_ALL, '')
    if not kwargs.get('no_plugins', False):
        plugin.load_plugins()
    commands.install_bzr_command_hooks()
    bash_completion_function(sys.stdout, **kwargs)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.