Repository: pallets/click

Issue:
Autocomplete support for zsh
Bash autocompletion works well, but I would really like zsh to work as well



Discussion:
Related https://github.com/mitsuhiko/click/issues/241.

Has zsh implementation: https://github.com/kislyuk/argcomplete

I have to release a `tmuxp.bash`, `tmuxp.zsh` and `tmuxp.tcsh` to get autocomplete working with argcomplete. The user has to source the correct one depending on their `$SHELL`, https://github.com/tony/tmuxp/tree/master/pkg

See also aws-cli's advanced completion: https://github.com/aws/aws-cli/tree/develop/bin 

> The user has to source the correct one depending on their `$SHELL`

if their `$SHELL` is zsh and you install globally, you can just put `_tmuxp` into `$fpath`, which by default contains `/usr/{,local/}share/zsh/site-functions`.

As an alternative to implementing native zsh completion, you can also use zsh's [bash completion compatibility mode](http://stackoverflow.com/questions/3249432/i-have-a-bash-tab-completion-script-is-there-a-simple-way-to-use-it-from-zsh).  To use it, add

```
autoload bashcompinit
bashcompinit
eval "$(_FOO_BAR_COMPLETE=source foo-bar)"
```

to your `.zshrc`.  The last line assumes your click script is called `foo-bar`; see the [click docs](http://click.pocoo.org/5/bashcomplete/) for more info.

@bb4242 thanks for the tipp, that works well.

@bb4242's answer should be added to the docs at: http://click.pocoo.org/5/bashcomplete/

Resolving patch:
diff --git a/click/_bashcomplete.py b/click/_bashcomplete.py
--- a/click/_bashcomplete.py
+++ b/click/_bashcomplete.py
@@ -10,7 +10,7 @@
 
 WORDBREAK = '='
 
-COMPLETION_SCRIPT = '''
+COMPLETION_SCRIPT_BASH = '''
 %(complete_func)s() {
     local IFS=$'\n'
     COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\
@@ -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,
@@ -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)


Test patch:
diff --git a/tests/test_compat.py b/tests/test_compat.py
--- a/tests/test_compat.py
+++ b/tests/test_compat.py
@@ -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