Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native ZSH completion script #865

Merged
merged 4 commits into from May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Expand Up @@ -33,6 +33,7 @@ Version 7.0
See #790.
- Allow autocompletion function to determine whether or not to return
completions that start with the incomplete argument.
- Add native ZSH autocompletion support See #323.
- Subcommands that are named by the function now automatically have the
underscore replaced with a dash. So if you register a function named
`my_command` it becomes `my-command` in the command line interface.
Expand Down
28 changes: 22 additions & 6 deletions click/_bashcomplete.py
Expand Up @@ -10,7 +10,7 @@

WORDBREAK = '='

COMPLETION_SCRIPT = '''
COMPLETION_SCRIPT_BASH = '''
%(complete_func)s() {
local IFS=$'\n'
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\
Expand All @@ -19,15 +19,30 @@
return 0
}

complete -F %(complete_func)s -o default %(script_names)s
complete -F %(complete_func)s %(script_names)s
'''

COMPLETION_SCRIPT_ZSH = '''
%(complete_func)s() {
emulate -L zsh
local IFS=$'\n'
local completions=( $( env COMP_WORDS="${words[*]}" \\
COMP_CWORD=$((CURRENT-1)) \\
%(autocomplete_var)s="complete" \\
%(script_names)s ) )
compadd -M 'r:|=* l:|=* r:|=*' -a -- completions
}

compdef %(complete_func)s %(script_names)s
'''

_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]')


def get_completion_script(prog_name, complete_var):
def get_completion_script(prog_name, complete_var, shell):
cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_'))
return (COMPLETION_SCRIPT % {
script = COMPLETION_SCRIPT_ZSH if shell == 'zsh' else COMPLETION_SCRIPT_BASH
return (script % {
'complete_func': '_%s_completion' % cf_name,
'script_names': prog_name,
'autocomplete_var': complete_var,
Expand Down Expand Up @@ -209,8 +224,9 @@ def do_complete(cli, prog_name):


def bashcomplete(cli, prog_name, complete_var, complete_instr):
if complete_instr == 'source':
echo(get_completion_script(prog_name, complete_var))
if complete_instr.startswith('source'):
shell = 'zsh' if complete_instr == 'source_zsh' else 'bash'
echo(get_completion_script(prog_name, complete_var, shell))
return True
elif complete_instr == 'complete':
return do_complete(cli, prog_name)
Expand Down
19 changes: 9 additions & 10 deletions docs/bashcomplete.rst
Expand Up @@ -75,7 +75,11 @@ is what you would need to put into your ``.bashrc``::

eval "$(_FOO_BAR_COMPLETE=source foo-bar)"

From this point onwards, your script will have Bash completion enabled.
For zsh users add this to your ``.zshrc``::

eval "$(_FOO_BAR_COMPLETE=source_zsh foo-bar)"

From this point onwards, your script will have autocompletion enabled.

Activation Script
-----------------
Expand All @@ -90,17 +94,12 @@ This can be easily accomplished::

_FOO_BAR_COMPLETE=source foo-bar > foo-bar-complete.sh

And then you would put this into your bashrc instead::
For zsh:

. /path/to/foo-bar-complete.sh
_FOO_BAR_COMPLETE=source_zsh foo-bar > foo-bar-complete.sh

Zsh Compatibility
-----------------
And then you would put this into your .bashrc or .zshrc instead::

To enable Bash completion in Zsh, add the following lines to your .zshrc:
. /path/to/foo-bar-complete.sh

autoload bashcompinit
bashcompinit

See https://github.com/pallets/click/issues/323 for more information on
this issue.
2 changes: 1 addition & 1 deletion tests/test_compat.py
Expand Up @@ -20,6 +20,6 @@ def cli(foo):

def test_bash_func_name():
from click._bashcomplete import get_completion_script
script = get_completion_script('foo-bar baz_blah', '_COMPLETE_VAR').strip()
script = get_completion_script('foo-bar baz_blah', '_COMPLETE_VAR', 'bash').strip()
assert script.startswith('_foo_barbaz_blah_completion()')
assert '_COMPLETE_VAR=complete $1' in script