Repository: pallets/click

Issue:
Allow documentation in ZSH auto completion.
After #865 is merged we can start using the extended autocompletion benefits of ZSH such as offering documentation for arguments.


Discussion:

Resolving patch:
diff --git a/click/_bashcomplete.py b/click/_bashcomplete.py
--- a/click/_bashcomplete.py
+++ b/click/_bashcomplete.py
@@ -24,13 +24,28 @@
 
 COMPLETION_SCRIPT_ZSH = '''
 %(complete_func)s() {
-    emulate -L zsh
-    local IFS=$'\n'
-    local completions=( $( env COMP_WORDS="${words[*]}" \\
+    local -a completions
+    local -a completions_with_descriptions
+    local -a response
+    response=("${(@f)$( env COMP_WORDS=\"${words[*]}\" \\
                         COMP_CWORD=$((CURRENT-1)) \\
-                        %(autocomplete_var)s="complete" \\
-                        %(script_names)s ) )
-    compadd -M 'r:|=* l:|=* r:|=*' -a -- completions 
+                        %(autocomplete_var)s=\"complete_zsh\" \\
+                        %(script_names)s )}")
+
+    for key descr in ${(kv)response}; do
+      if [[ "$descr" == "_" ]]; then
+          completions+=("$key")
+      else
+          completions_with_descriptions+=("$key":"$descr")
+      fi
+    done
+
+    if [ -n "$completions_with_descriptions" ]; then
+        _describe '' completions_with_descriptions
+    fi
+    if [ -n "$completions" ]; then
+        compadd -M 'r:|=* l:|=* r:|=*' -a completions
+    fi
 }
 
 compdef %(complete_func)s %(script_names)s
@@ -131,21 +146,25 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param):
     :param cmd_param: command definition
     :return: all the possible user-specified completions for the param
     """
+    results = []
     if isinstance(cmd_param.type, Choice):
-        return [c for c in cmd_param.type.choices if c.startswith(incomplete)]
+        # Choices don't support descriptions.
+        results = [(c, None)
+                   for c in cmd_param.type.choices if c.startswith(incomplete)]
     elif cmd_param.autocompletion is not None:
-        return cmd_param.autocompletion(ctx=ctx,
-                                        args=args,
-                                        incomplete=incomplete)
-    else:
-        return []
+        dynamic_completions = cmd_param.autocompletion(ctx=ctx,
+                                                       args=args,
+                                                       incomplete=incomplete)
+        results = [c if isinstance(c, tuple) else (c, None)
+                   for c in dynamic_completions]
+    return results
 
 
 def add_subcommand_completions(ctx, incomplete, completions_out):
     # Add subcommand completions.
     if isinstance(ctx.command, MultiCommand):
         completions_out.extend(
-            [c for c in ctx.command.list_commands(ctx) if c.startswith(incomplete)])
+            [(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in ctx.command.list_commands(ctx) if c.startswith(incomplete)])
 
     # Walk up the context list and add any other completion possibilities from chained commands
     while ctx.parent is not None:
@@ -154,7 +173,7 @@ def add_subcommand_completions(ctx, incomplete, completions_out):
             remaining_commands = sorted(
                 set(ctx.command.list_commands(ctx)) - set(ctx.protected_args))
             completions_out.extend(
-                [c for c in remaining_commands if c.startswith(incomplete)])
+                [(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in remaining_commands if c.startswith(incomplete)])
 
 
 def get_choices(cli, prog_name, args, incomplete):
@@ -188,7 +207,7 @@ def get_choices(cli, prog_name, args, incomplete):
                 param_opts = [param_opt for param_opt in param.opts +
                               param.secondary_opts if param_opt not in all_args or param.multiple]
                 completions.extend(
-                    [c for c in param_opts if c.startswith(incomplete)])
+                    [(o, param.help) for o in param_opts if o.startswith(incomplete)])
         return completions
     # completion for option values from user supplied values
     for param in ctx.command.params:
@@ -208,7 +227,7 @@ def get_choices(cli, prog_name, args, incomplete):
     return completions
 
 
-def do_complete(cli, prog_name):
+def do_complete(cli, prog_name, include_descriptions):
     cwords = split_arg_string(os.environ['COMP_WORDS'])
     cword = int(os.environ['COMP_CWORD'])
     args = cwords[1:cword]
@@ -218,7 +237,10 @@ def do_complete(cli, prog_name):
         incomplete = ''
 
     for item in get_choices(cli, prog_name, args, incomplete):
-        echo(item)
+        echo(item[0])
+        if include_descriptions:
+            # ZSH has trouble dealing with empty array parameters when returned from commands, so use a well defined character '_' to indicate no description is present.
+            echo(item[1] if item[1] else '_')
 
     return True
 
@@ -228,6 +250,6 @@ def bashcomplete(cli, prog_name, complete_var, complete_instr):
         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)
+    elif complete_instr == 'complete' or complete_instr == 'complete_zsh':
+        return do_complete(cli, prog_name, complete_instr == 'complete_zsh')
     return False
diff --git a/click/core.py b/click/core.py
--- a/click/core.py
+++ b/click/core.py
@@ -850,6 +850,10 @@ def get_help(self, ctx):
         self.format_help(ctx, formatter)
         return formatter.getvalue().rstrip('\n')
 
+    def get_short_help_str(self, limit=45):
+        """Gets short help for the command or makes it by shortening the long help string."""
+        return self.short_help or self.help and make_default_short_help(self.help, limit) or ''
+
     def format_help(self, ctx, formatter):
         """Writes the help into the formatter if it exists.
 
@@ -1041,8 +1045,8 @@ def format_commands(self, ctx, formatter):
 
             rows = []
             for subcommand, cmd in commands:
-                help = cmd.short_help or cmd.help and make_default_short_help(cmd.help, limit)
-                rows.append((subcommand, help or ''))
+                help = cmd.get_short_help_str(limit)
+                rows.append((subcommand, help))
 
             if rows:
                 with formatter.section('Commands'):
diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py
--- a/examples/bashcompletion/bashcompletion.py
+++ b/examples/bashcompletion/bashcompletion.py
@@ -13,14 +13,14 @@ def get_env_vars(ctx, args, incomplete):
             yield key
 
 
-@cli.command()
+@cli.command(help='A command to print environment variables')
 @click.argument("envvar", type=click.STRING, autocompletion=get_env_vars)
 def cmd1(envvar):
     click.echo('Environment variable: %s' % envvar)
     click.echo('Value: %s' % os.environ[envvar])
 
 
-@click.group()
+@click.group(help='A group that holds a subcommand')
 def group():
     pass
 
@@ -33,7 +33,7 @@ def list_users(ctx, args, incomplete):
             yield user
 
 
-@group.command()
+@group.command(help='Choose a user')
 @click.argument("user", type=click.STRING, autocompletion=list_users)
 def subcmd(user):
     click.echo('Chosen user is %s' % user)


Test patch:
diff --git a/tests/test_bashcomplete.py b/tests/test_bashcomplete.py
--- a/tests/test_bashcomplete.py
+++ b/tests/test_bashcomplete.py
@@ -4,14 +4,23 @@
 from click._bashcomplete import get_choices
 
 
+def choices_without_help(cli, args, incomplete):
+    completions = get_choices(cli, 'dummy', args, incomplete)
+    return [c[0] for c in completions]
+
+
+def choices_with_help(cli, args, incomplete):
+    return list(get_choices(cli, 'dummy', args, incomplete))
+
+
 def test_single_command():
     @click.command()
     @click.option('--local-opt')
     def cli(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--local-opt']
-    assert list(get_choices(cli, 'lol', [], '')) == []
+    assert choices_without_help(cli, [], '-') == ['--local-opt']
+    assert choices_without_help(cli, [], '') == []
 
 
 def test_boolean_flag():
@@ -20,7 +29,7 @@ def test_boolean_flag():
     def cli(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--shout', '--no-shout']
+    assert choices_without_help(cli, [], '-') == ['--shout', '--no-shout']
 
 
 def test_multi_value_option():
@@ -34,10 +43,10 @@ def cli(local_opt):
     def sub(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--pos']
-    assert list(get_choices(cli, 'lol', ['--pos'], '')) == []
-    assert list(get_choices(cli, 'lol', ['--pos', '1.0'], '')) == []
-    assert list(get_choices(cli, 'lol', ['--pos', '1.0', '1.0'], '')) == ['sub']
+    assert choices_without_help(cli, [], '-') == ['--pos']
+    assert choices_without_help(cli, ['--pos'], '') == []
+    assert choices_without_help(cli, ['--pos', '1.0'], '') == []
+    assert choices_without_help(cli, ['--pos', '1.0', '1.0'], '') == ['sub']
 
 
 def test_multi_option():
@@ -46,8 +55,8 @@ def test_multi_option():
     def cli(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--message', '-m']
-    assert list(get_choices(cli, 'lol', ['-m'], '')) == []
+    assert choices_without_help(cli, [], '-') == ['--message', '-m']
+    assert choices_without_help(cli, ['-m'], '') == []
 
 
 def test_small_chain():
@@ -61,10 +70,10 @@ def cli(global_opt):
     def sub(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '')) == ['sub']
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--global-opt']
-    assert list(get_choices(cli, 'lol', ['sub'], '')) == []
-    assert list(get_choices(cli, 'lol', ['sub'], '-')) == ['--local-opt']
+    assert choices_without_help(cli, [], '') == ['sub']
+    assert choices_without_help(cli, [], '-') == ['--global-opt']
+    assert choices_without_help(cli, ['sub'], '') == []
+    assert choices_without_help(cli, ['sub'], '-') == ['--local-opt']
 
 
 def test_long_chain():
@@ -104,20 +113,20 @@ def search_colors(ctx, args, incomplete):
     def csub(csub_opt, color):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--cli-opt']
-    assert list(get_choices(cli, 'lol', [], '')) == ['asub']
-    assert list(get_choices(cli, 'lol', ['asub'], '-')) == ['--asub-opt']
-    assert list(get_choices(cli, 'lol', ['asub'], '')) == ['bsub']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub'], '-')) == ['--bsub-opt']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub'], '')) == ['csub']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub'], '-')) == ['--csub-opt', '--csub', '--search-color']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub', '--csub-opt'], '')) == CSUB_OPT_CHOICES
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub'], '--csub')) == ['--csub-opt', '--csub']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub', '--csub'], '')) == CSUB_CHOICES
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub', '--csub-opt'], 'f')) == ['foo']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub'], '')) == COLORS
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub'], 'b')) == ['blue']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub', '--search-color'], 'een')) == ['green']
+    assert choices_without_help(cli, [], '-') == ['--cli-opt']
+    assert choices_without_help(cli, [], '') == ['asub']
+    assert choices_without_help(cli, ['asub'], '-') == ['--asub-opt']
+    assert choices_without_help(cli, ['asub'], '') == ['bsub']
+    assert choices_without_help(cli, ['asub', 'bsub'], '-') == ['--bsub-opt']
+    assert choices_without_help(cli, ['asub', 'bsub'], '') == ['csub']
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub'], '-') == ['--csub-opt', '--csub', '--search-color']
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--csub-opt'], '') == CSUB_OPT_CHOICES
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub'], '--csub') == ['--csub-opt', '--csub']
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--csub'], '') == CSUB_CHOICES
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--csub-opt'], 'f') == ['foo']
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub'], '') == COLORS
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub'], 'b') == ['blue']
+    assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--search-color'], 'een') == ['green']
 
 
 def test_chaining():
@@ -126,33 +135,33 @@ def test_chaining():
     def cli(cli_opt):
         pass
 
-    @cli.command('asub')
+    @cli.command()
     @click.option('--asub-opt')
     def asub(asub_opt):
         pass
 
-    @cli.command('bsub')
+    @cli.command(help='bsub help')
     @click.option('--bsub-opt')
     @click.argument('arg', type=click.Choice(['arg1', 'arg2']), required=True)
     def bsub(bsub_opt, arg):
         pass
 
-    @cli.command('csub')
+    @cli.command()
     @click.option('--csub-opt')
     @click.argument('arg', type=click.Choice(['carg1', 'carg2']), required=False)
     def csub(csub_opt, arg):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--cli-opt']
-    assert list(get_choices(cli, 'lol', [], '')) == ['asub', 'bsub', 'csub']
-    assert list(get_choices(cli, 'lol', ['asub'], '-')) == ['--asub-opt']
-    assert list(get_choices(cli, 'lol', ['asub'], '')) == ['bsub', 'csub']
-    assert list(get_choices(cli, 'lol', ['bsub'], '')) == ['arg1', 'arg2']
-    assert list(get_choices(cli, 'lol', ['asub', '--asub-opt'], '')) == []
-    assert list(get_choices(cli, 'lol', ['asub', '--asub-opt', '5', 'bsub'], '-')) == ['--bsub-opt']
-    assert list(get_choices(cli, 'lol', ['asub', 'bsub'], '-')) == ['--bsub-opt']
-    assert list(get_choices(cli, 'lol', ['asub', 'csub'], '')) == ['carg1', 'carg2', 'bsub']
-    assert list(get_choices(cli, 'lol', ['asub', 'csub'], '-')) == ['--csub-opt']
+    assert choices_without_help(cli, [], '-') == ['--cli-opt']
+    assert choices_without_help(cli, [], '') == ['asub', 'bsub', 'csub']
+    assert choices_without_help(cli, ['asub'], '-') == ['--asub-opt']
+    assert choices_without_help(cli, ['asub'], '') == ['bsub', 'csub']
+    assert choices_without_help(cli, ['bsub'], '') == ['arg1', 'arg2']
+    assert choices_without_help(cli, ['asub', '--asub-opt'], '') == []
+    assert choices_without_help(cli, ['asub', '--asub-opt', '5', 'bsub'], '-') == ['--bsub-opt']
+    assert choices_without_help(cli, ['asub', 'bsub'], '-') == ['--bsub-opt']
+    assert choices_with_help(cli, ['asub'], 'b') == [('bsub', 'bsub help')]
+    assert choices_without_help(cli, ['asub', 'csub'], '-') == ['--csub-opt']
 
 
 def test_argument_choice():
@@ -163,36 +172,38 @@ def test_argument_choice():
     def cli():
         pass
 
-    assert list(get_choices(cli, 'lol', [], '')) == ['arg11', 'arg12']
-    assert list(get_choices(cli, 'lol', [], 'arg')) == ['arg11', 'arg12']
-    assert list(get_choices(cli, 'lol', ['arg11'], '')) == ['arg21', 'arg22']
-    assert list(get_choices(cli, 'lol', ['arg12', 'arg21'], '')) == ['arg', 'argument']
-    assert list(get_choices(cli, 'lol', ['arg12', 'arg21'], 'argu')) == ['argument']
+    assert choices_without_help(cli, [], '') == ['arg11', 'arg12']
+    assert choices_without_help(cli, [], 'arg') == ['arg11', 'arg12']
+    assert choices_without_help(cli, ['arg11'], '') == ['arg21', 'arg22']
+    assert choices_without_help(cli, ['arg12', 'arg21'], '') == ['arg', 'argument']
+    assert choices_without_help(cli, ['arg12', 'arg21'], 'argu') == ['argument']
 
 
 def test_option_choice():
     @click.command()
-    @click.option('--opt1', type=click.Choice(['opt11', 'opt12']))
+    @click.option('--opt1', type=click.Choice(['opt11', 'opt12']), help='opt1 help')
     @click.option('--opt2', type=click.Choice(['opt21', 'opt22']))
     @click.option('--opt3', type=click.Choice(['opt', 'option']))
     def cli():
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--opt1', '--opt2', '--opt3']
-    assert list(get_choices(cli, 'lol', [], '--opt')) == ['--opt1', '--opt2', '--opt3']
-    assert list(get_choices(cli, 'lol', [], '--opt1=')) == ['opt11', 'opt12']
-    assert list(get_choices(cli, 'lol', [], '--opt2=')) == ['opt21', 'opt22']
-    assert list(get_choices(cli, 'lol', ['--opt2'], '=')) == ['opt21', 'opt22']
-    assert list(get_choices(cli, 'lol', ['--opt2', '='], 'opt')) == ['opt21', 'opt22']
-    assert list(get_choices(cli, 'lol', ['--opt1'], '')) == ['opt11', 'opt12']
-    assert list(get_choices(cli, 'lol', ['--opt2'], '')) == ['opt21', 'opt22']
-    assert list(get_choices(cli, 'lol', ['--opt1', 'opt11', '--opt2'], '')) == ['opt21', 'opt22']
-    assert list(get_choices(cli, 'lol', ['--opt2', 'opt21'], '-')) == ['--opt1', '--opt3']
-    assert list(get_choices(cli, 'lol', ['--opt1', 'opt11'], '-')) == ['--opt2', '--opt3']
-    assert list(get_choices(cli, 'lol', ['--opt1'], 'opt')) == ['opt11', 'opt12']
-    assert list(get_choices(cli, 'lol', ['--opt3'], 'opti')) == ['option']
-
-    assert list(get_choices(cli, 'lol', ['--opt1', 'invalid_opt'], '-')) == ['--opt2', '--opt3']
+    assert choices_with_help(cli, [], '-') == [('--opt1', 'opt1 help'),
+                                               ('--opt2', None),
+                                               ('--opt3', None)]
+    assert choices_without_help(cli, [], '--opt') == ['--opt1', '--opt2', '--opt3']
+    assert choices_without_help(cli, [], '--opt1=') == ['opt11', 'opt12']
+    assert choices_without_help(cli, [], '--opt2=') == ['opt21', 'opt22']
+    assert choices_without_help(cli, ['--opt2'], '=') == ['opt21', 'opt22']
+    assert choices_without_help(cli, ['--opt2', '='], 'opt') == ['opt21', 'opt22']
+    assert choices_without_help(cli, ['--opt1'], '') == ['opt11', 'opt12']
+    assert choices_without_help(cli, ['--opt2'], '') == ['opt21', 'opt22']
+    assert choices_without_help(cli, ['--opt1', 'opt11', '--opt2'], '') == ['opt21', 'opt22']
+    assert choices_without_help(cli, ['--opt2', 'opt21'], '-') == ['--opt1', '--opt3']
+    assert choices_without_help(cli, ['--opt1', 'opt11'], '-') == ['--opt2', '--opt3']
+    assert choices_without_help(cli, ['--opt1'], 'opt') == ['opt11', 'opt12']
+    assert choices_without_help(cli, ['--opt3'], 'opti') == ['option']
+
+    assert choices_without_help(cli, ['--opt1', 'invalid_opt'], '-') == ['--opt2', '--opt3']
 
 
 def test_option_and_arg_choice():
@@ -203,10 +214,10 @@ def test_option_and_arg_choice():
     def cli():
         pass
 
-    assert list(get_choices(cli, 'lol', ['--opt1'], '')) == ['opt11', 'opt12']
-    assert list(get_choices(cli, 'lol', [''], '--opt1=')) == ['opt11', 'opt12']
-    assert list(get_choices(cli, 'lol', [], '')) == ['arg11', 'arg12']
-    assert list(get_choices(cli, 'lol', ['--opt2'], '')) == ['opt21', 'opt22']
+    assert choices_without_help(cli, ['--opt1'], '') == ['opt11', 'opt12']
+    assert choices_without_help(cli, [''], '--opt1=') == ['opt11', 'opt12']
+    assert choices_without_help(cli, [], '') == ['arg11', 'arg12']
+    assert choices_without_help(cli, ['--opt2'], '') == ['opt21', 'opt22']
 
 
 def test_boolean_flag_choice():
@@ -216,8 +227,8 @@ def test_boolean_flag_choice():
     def cli(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', [], '-')) == ['--shout', '--no-shout']
-    assert list(get_choices(cli, 'lol', ['--shout'], '')) == ['arg1', 'arg2']
+    assert choices_without_help(cli, [], '-') == ['--shout', '--no-shout']
+    assert choices_without_help(cli, ['--shout'], '') == ['arg1', 'arg2']
 
 
 def test_multi_value_option_choice():
@@ -227,10 +238,10 @@ def test_multi_value_option_choice():
     def cli(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', ['--pos'], '')) == ['pos1', 'pos2']
-    assert list(get_choices(cli, 'lol', ['--pos', 'pos1'], '')) == ['pos1', 'pos2']
-    assert list(get_choices(cli, 'lol', ['--pos', 'pos1', 'pos2'], '')) == ['arg1', 'arg2']
-    assert list(get_choices(cli, 'lol', ['--pos', 'pos1', 'pos2', 'arg1'], '')) == []
+    assert choices_without_help(cli, ['--pos'], '') == ['pos1', 'pos2']
+    assert choices_without_help(cli, ['--pos', 'pos1'], '') == ['pos1', 'pos2']
+    assert choices_without_help(cli, ['--pos', 'pos1', 'pos2'], '') == ['arg1', 'arg2']
+    assert choices_without_help(cli, ['--pos', 'pos1', 'pos2', 'arg1'], '') == []
 
 
 def test_multi_option_choice():
@@ -240,9 +251,9 @@ def test_multi_option_choice():
     def cli(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', ['-m'], '')) == ['m1', 'm2']
-    assert list(get_choices(cli, 'lol', ['-m', 'm1', '-m'], '')) == ['m1', 'm2']
-    assert list(get_choices(cli, 'lol', ['-m', 'm1'], '')) == ['arg1', 'arg2']
+    assert choices_without_help(cli, ['-m'], '') == ['m1', 'm2']
+    assert choices_without_help(cli, ['-m', 'm1', '-m'], '') == ['m1', 'm2']
+    assert choices_without_help(cli, ['-m', 'm1'], '') == ['arg1', 'arg2']
 
 
 def test_variadic_argument_choice():
@@ -251,7 +262,7 @@ def test_variadic_argument_choice():
     def cli(local_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', ['src1', 'src2'], '')) == ['src1', 'src2']
+    assert choices_without_help(cli, ['src1', 'src2'], '') == ['src1', 'src2']
 
 
 def test_long_chain_choice():
@@ -259,28 +270,28 @@ def test_long_chain_choice():
     def cli():
         pass
 
-    @cli.group('sub')
+    @cli.group()
     @click.option('--sub-opt', type=click.Choice(['subopt1', 'subopt2']))
     @click.argument('sub-arg', required=False, type=click.Choice(['subarg1', 'subarg2']))
     def sub(sub_opt):
         pass
 
-    @sub.command('bsub')
+    @sub.command(short_help='bsub help')
     @click.option('--bsub-opt', type=click.Choice(['bsubopt1', 'bsubopt2']))
     @click.argument('bsub-arg1', required=False, type=click.Choice(['bsubarg1', 'bsubarg2']))
     @click.argument('bbsub-arg2', required=False, type=click.Choice(['bbsubarg1', 'bbsubarg2']))
     def bsub(bsub_opt):
         pass
 
-    assert list(get_choices(cli, 'lol', ['sub'], '')) == ['subarg1', 'subarg2', 'bsub']
-    assert list(get_choices(cli, 'lol', ['sub', '--sub-opt'], '')) == ['subopt1', 'subopt2']
-    assert list(get_choices(cli, 'lol', ['sub', '--sub-opt', 'subopt1'], '')) == \
+    assert choices_with_help(cli, ['sub'], '') == [('subarg1', None), ('subarg2', None), ('bsub', 'bsub help')]
+    assert choices_without_help(cli, ['sub', '--sub-opt'], '') == ['subopt1', 'subopt2']
+    assert choices_without_help(cli, ['sub', '--sub-opt', 'subopt1'], '') == \
         ['subarg1', 'subarg2', 'bsub']
-    assert list(get_choices(cli, 'lol',
-        ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub'], '-')) == ['--bsub-opt']
-    assert list(get_choices(cli, 'lol',
-        ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt'], '')) == \
+    assert choices_without_help(cli,
+        ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub'], '-') == ['--bsub-opt']
+    assert choices_without_help(cli,
+        ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt'], '') == \
         ['bsubopt1', 'bsubopt2']
-    assert list(get_choices(cli, 'lol',
+    assert choices_without_help(cli,
         ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt', 'bsubopt1', 'bsubarg1'],
-                            '')) == ['bbsubarg1', 'bbsubarg2']
+                            '') == ['bbsubarg1', 'bbsubarg2']