summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES2
-rw-r--r--click/_bashcomplete.py13
-rw-r--r--examples/bashcompletion/bashcompletion.py14
-rw-r--r--tests/test_bashcomplete.py24
4 files changed, 43 insertions, 10 deletions
diff --git a/CHANGES b/CHANGES
index 58acf13..456eaf8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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():