Commit 677ddcbb authored by Joseph Kocherhans's avatar Joseph Kocherhans
Browse files

Fixed #10752. Added more advanced bash completion. Thanks, Arthur Koziel.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11526 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent a53601c6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -241,6 +241,7 @@ answer newbie questions, and generally made Django that much better:
    Igor Kolar <ike@email.si>
    Gasper Koren
    Martin Kosír <martin@martinkosir.net>
    Arthur Koziel <http://arthurkoziel.com>
    Meir Kriheli <http://mksoft.co.il/>
    Bruce Kroeze <http://coderseye.com/>
    krzysiek.pawlik@silvermedia.pl
+77 −0
Original line number Diff line number Diff line
@@ -261,6 +261,82 @@ class ManagementUtility(object):
            sys.exit(1)
        return klass

    def autocomplete(self):
        """
        Output completion suggestions for BASH.

        The output of this function is passed to BASH's `COMREPLY` variable and
        treated as completion suggestions. `COMREPLY` expects a space
        separated string as the result.

        The `COMP_WORDS` and `COMP_CWORD` BASH environment variables are used
        to get information about the cli input. Please refer to the BASH
        man-page for more information about this variables.

        Subcommand options are saved as pairs. A pair consists of
        the long option string (e.g. '--exclude') and a boolean
        value indicating if the option requires arguments. When printing to
        stdout, a equal sign is appended to options which require arguments.

        Note: If debugging this function, it is recommended to write the debug
        output in a separate file. Otherwise the debug output will be treated
        and formatted as potential completion suggestions.
        """
        # Don't complete if user hasn't sourced bash_completion file.
        if not os.environ.has_key('DJANGO_AUTO_COMPLETE'):
            return

        cwords = os.environ['COMP_WORDS'].split()[1:]
        cword = int(os.environ['COMP_CWORD'])

        try:
            curr = cwords[cword-1]
        except IndexError:
            curr = ''

        subcommands = get_commands().keys() + ['help']
        options = [('--help', None)]

        # subcommand
        if cword == 1:
            print ' '.join(filter(lambda x: x.startswith(curr), subcommands))
        # subcommand options
        # special case: the 'help' subcommand has no options
        elif cwords[0] in subcommands and cwords[0] != 'help':
            subcommand_cls = self.fetch_command(cwords[0])
            # special case: 'runfcgi' stores additional options as
            # 'key=value' pairs
            if cwords[0] == 'runfcgi':
                from django.core.servers.fastcgi import FASTCGI_OPTIONS
                options += [(k, 1) for k in FASTCGI_OPTIONS]
            # special case: add the names of installed apps to options
            elif cwords[0] in ('dumpdata', 'reset', 'sql', 'sqlall',
                               'sqlclear', 'sqlcustom', 'sqlindexes',
                               'sqlreset', 'sqlsequencereset', 'test'):
                try:
                    from django.conf import settings
                    # Get the last part of the dotted path as the app name.
                    options += [(a.split('.')[-1], 0) for a in settings.INSTALLED_APPS]
                except ImportError:
                    # Fail silently if DJANGO_SETTINGS_MODULE isn't set. The
                    # user will find out once they execute the command.
                    pass
            options += [(s_opt.get_opt_string(), s_opt.nargs) for s_opt in
                        subcommand_cls.option_list]
            # filter out previously specified options from available options
            prev_opts = [x.split('=')[0] for x in cwords[1:cword-1]]
            options = filter(lambda (x, v): x not in prev_opts, options)

            # filter options by current input
            options = [(k, v) for k, v in options if k.startswith(curr)]
            for option in options:
                opt_label = option[0]
                # append '=' to options which require args
                if option[1]:
                    opt_label += '='
                print opt_label
        sys.exit(1)

    def execute(self):
        """
        Given the command-line arguments, this figures out which subcommand is
@@ -272,6 +348,7 @@ class ManagementUtility(object):
        parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
                                 version=get_version(),
                                 option_list=BaseCommand.option_list)
        self.autocomplete()
        try:
            options, args = parser.parse_args(self.argv)
            handle_default_options(options)
+4 −130
Original line number Diff line number Diff line
@@ -31,136 +31,10 @@
#
# To uninstall, just remove the line from your .bash_profile and .bashrc.

# Enable extended pattern matching operators.
shopt -s extglob

_django_completion()
{
    local cur prev opts actions action_shell_opts action_runfcgi_opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Standalone options
    opts="--help --settings --pythonpath --noinput --noreload --format --indent --verbosity --adminmedia --version --locale --domain"
    # Actions
    actions="createcachetable createsuperuser compilemessages \
             dbshell diffsettings dumpdata flush inspectdb loaddata \
             makemessages reset runfcgi runserver shell sql sqlall sqlclear \
             sqlcustom sqlflush sqlindexes sqlreset sqlsequencereset startapp \
             startproject syncdb test validate"
    # Action's options
    action_shell_opts="--plain"
    action_runfcgi_opts="host port socket method maxspare minspare maxchildren daemonize pidfile workdir"

    if [[ # django-admin.py, django-admin, ./manage, manage.py
          ( ${COMP_CWORD} -eq 1 &&
            ( ${COMP_WORDS[0]} == django-admin.py ||
              ${COMP_WORDS[0]} == django-admin ||
              ${COMP_WORDS[0]} == ./manage.py ||
              ${COMP_WORDS[0]} == manage.py ) )
          ||
          # python manage.py, /some/path/python manage.py (if manage.py exists)
          ( ${COMP_CWORD} -eq 2 &&
            ( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
            ( $( basename -- ${COMP_WORDS[1]} ) == manage.py) &&
            ( -r ${COMP_WORDS[1]} ) ) 
          ||
          ( ${COMP_CWORD} -eq 2 &&
            ( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
            ( $( basename -- ${COMP_WORDS[1]} ) == django-admin.py) &&
            ( -r ${COMP_WORDS[1]} ) ) 
          ||
          ( ${COMP_CWORD} -eq 2 &&
            ( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
            ( $( basename -- ${COMP_WORDS[1]} ) == django-admin) &&
            ( -r ${COMP_WORDS[1]} ) ) ]] ; then

        case ${cur} in
            -*)
                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
                action=$COMPREPLY
                return 0
                ;;
            *)
                COMPREPLY=( $(compgen -W "${actions}" -- ${cur}) )
                action=$COMPREPLY
                return 0
                ;;
        esac
    else
        case ${prev} in
            dumpdata|reset| \
            sql|sqlall|sqlclear|sqlcustom|sqlindexes| \
            sqlreset|sqlsequencereset|test)
                # App completion
                settings=""
                # If settings.py in the PWD, use that
                if [ -e settings.py ] ; then
                    settings="$PWD/settings.py"
                else
                    # Use the ENV variable if it is set
                    if [ $DJANGO_SETTINGS_MODULE ] ; then
                        settings=$DJANGO_SETTINGS_MODULE
                    fi
                fi
                # Couldn't find settings so return nothing
                if [ -z $settings ] ; then
                    COMPREPLY=()
                # Otherwise inspect settings.py file
                else
                    apps=`sed -n "/INSTALLED_APPS = (/,/)/p" $settings | \
                          grep -v "django.contrib" | 
                          sed -n "s/^[ ]*'\(.*\.\)*\(.*\)'.*$/\2 /pg" | \
                          tr -d "\n"`
                    COMPREPLY=( $(compgen -W "${apps}" -- ${cur}) )
                fi
                return 0
                ;;

            createcachetable|cleanup|compilemessages|dbshell| \
            diffsettings|inspectdb|makemessages| \
            runserver|startapp|startproject|syncdb| \
            validate)
                COMPREPLY=()
                return 0
                ;;
            shell)
                COMPREPLY=( $(compgen -W "$action_shell_opts" -- ${cur}) )
                return 0
                ;;
            runfcgi)
                COMPREPLY=( $(compgen -W "$action_runfcgi_opts" -- ${cur}) )
                return 0
                ;;
            host*|port*|socket*|method*|maxspare*|minspare*|maxchildren*|daemonize*|pidfile*|workdir*)
                if [ "$action"  == "runfcgi" ] ; then
                    COMPREPLY=( $(compgen -W "$action_runfcgi_opts" -- ${cur}) )
                    return 0
                fi
                return 0
                ;;
            *)
                #COMPREPLY=( $(compgen -W "auth core" -- ${cur}) )
                COMPREPLY=()
                return 0
                ;;
        esac
    fi
    COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \
                   COMP_CWORD=$COMP_CWORD \
	               DJANGO_AUTO_COMPLETE=1 $1 ) )
}

complete -F _django_completion django-admin.py manage.py django-admin

# Support for multiple interpreters.
unset pythons
if command -v whereis &>/dev/null; then
    python_interpreters=$(whereis python | cut -d " " -f 2-) 
    for python in $python_interpreters; do
        pythons="${pythons} $(basename -- $python)"
    done
    pythons=$(echo $pythons | tr " " "\n" | sort -u | tr "\n" " ")
else
    pythons=python    
fi

complete -F _django_completion -o default $pythons
complete -F _django_completion -o default django-admin.py manage.py