diff options
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | click/_bashcomplete.py | 13 | ||||
-rw-r--r-- | examples/bashcompletion/bashcompletion.py | 14 | ||||
-rw-r--r-- | tests/test_bashcomplete.py | 24 |
4 files changed, 43 insertions, 10 deletions
@@ -29,6 +29,8 @@ Version 7.0 - Fix bug that caused bashcompletion to give inproper completions on chained commands when a required option/argument was being completed. See #790. +- Allow autocompletion function to determine whether or not to return + completions that start with the incomplete argument. Version 6.8 ----------- diff --git a/click/_bashcomplete.py b/click/_bashcomplete.py index 61ddec2..99ffb4a 100644 --- a/click/_bashcomplete.py +++ b/click/_bashcomplete.py @@ -115,7 +115,7 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param): :return: all the possible user-specified completions for the param """ if isinstance(cmd_param.type, Choice): - return cmd_param.type.choices + return [c 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, @@ -148,6 +148,7 @@ def get_choices(cli, prog_name, args, incomplete): incomplete = '' choices = [] + user_choices = [] found_param = False if start_of_option(incomplete): # completions for partial options @@ -159,14 +160,14 @@ def get_choices(cli, prog_name, args, incomplete): # completion for option values from user supplied values for param in ctx.command.params: if is_incomplete_option(all_args, param): - choices.extend(get_user_autocompletions(ctx, all_args, incomplete, param)) + user_choices.extend(get_user_autocompletions(ctx, all_args, incomplete, param)) found_param = True break # completion for argument values from user supplied values if not found_param: for param in ctx.command.params: if is_incomplete_argument(ctx.params, param): - choices.extend(get_user_autocompletions(ctx, all_args, incomplete, param)) + user_choices.extend(get_user_autocompletions(ctx, all_args, incomplete, param)) # Stop looking for other completions only if this argument is required found_param = param.required break @@ -180,14 +181,18 @@ def get_choices(cli, prog_name, args, incomplete): while ctx.parent is not None: ctx = ctx.parent if isinstance(ctx.command, MultiCommand) and ctx.command.chain: - remaining_commands = set(ctx.command.list_commands(ctx))-set(ctx.protected_args) + remaining_commands = sorted(set(ctx.command.list_commands(ctx))-set(ctx.protected_args)) choices.extend(remaining_commands) + for item in user_choices: + yield item + for item in choices: if item.startswith(incomplete): yield item + def do_complete(cli, prog_name): cwords = split_arg_string(os.environ['COMP_WORDS']) cword = int(os.environ['COMP_CWORD']) diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py index 8aaf174..c483d79 100644 --- a/examples/bashcompletion/bashcompletion.py +++ b/examples/bashcompletion/bashcompletion.py @@ -1,12 +1,17 @@ import click import os + @click.group() def cli(): pass + def get_env_vars(ctx, args, incomplete): - return os.environ.keys() + for key in os.environ.keys(): + if incomplete in key: + yield key + @cli.command() @click.argument("envvar", type=click.STRING, autocompletion=get_env_vars) @@ -14,14 +19,19 @@ def cmd1(envvar): click.echo('Environment variable: %s' % envvar) click.echo('Value: %s' % os.environ[envvar]) + @click.group() def group(): pass + def list_users(ctx, args, incomplete): # Here you can generate completions dynamically users = ['bob', 'alice'] - return users + for user in users: + if user.startswith(incomplete): + yield user + @group.command() @click.argument("user", type=click.STRING, autocompletion=list_users) diff --git a/tests/test_bashcomplete.py b/tests/test_bashcomplete.py index 747c010..69448e4 100644 --- a/tests/test_bashcomplete.py +++ b/tests/test_bashcomplete.py @@ -86,13 +86,20 @@ def test_long_chain(): COLORS = ['red', 'green', 'blue'] def get_colors(ctx, args, incomplete): for c in COLORS: - yield c + if c.startswith(incomplete): + yield c + + def search_colors(ctx, args, incomplete): + for c in COLORS: + if incomplete in c: + yield c CSUB_OPT_CHOICES = ['foo', 'bar'] CSUB_CHOICES = ['bar', 'baz'] @bsub.command('csub') @click.option('--csub-opt', type=click.Choice(CSUB_OPT_CHOICES)) @click.option('--csub', type=click.Choice(CSUB_CHOICES)) + @click.option('--search-color', autocompletion=search_colors) @click.argument('color', autocompletion=get_colors) def csub(csub_opt, color): pass @@ -103,13 +110,14 @@ def test_long_chain(): 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'] + 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'] def test_chaining(): @@ -129,14 +137,22 @@ def test_chaining(): def bsub(bsub_opt, arg): pass + @cli.command('csub') + @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'] + 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'] + 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'] def test_argument_choice(): |