Skip to content

Commit

Permalink
Fix bashautocompletion of arguments on chained commands
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicholas Wiles committed May 2, 2017
1 parent 5f2ff4f commit 92c8053
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 5 deletions.
19 changes: 14 additions & 5 deletions click/_bashcomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,18 @@ def resolve_ctx(cli, prog_name, args):
:return: the final context/command parsed
"""
ctx = cli.make_context(prog_name, args, resilient_parsing=True)
while ctx.protected_args + ctx.args and isinstance(ctx.command, MultiCommand):
a = ctx.protected_args + ctx.args
cmd = ctx.command.get_command(ctx, a[0])
args_remaining = ctx.protected_args + ctx.args
while ctx is not None and args_remaining:
if isinstance(ctx.command, MultiCommand):
cmd = ctx.command.get_command(ctx, args_remaining[0])
if cmd is None:
return None
ctx = cmd.make_context(a[0], a[1:], parent=ctx, resilient_parsing=True)
return ctx
ctx = cmd.make_context(args_remaining[0], args_remaining[1:], parent=ctx, resilient_parsing=True)
args_remaining = ctx.protected_args + ctx.args
else:
ctx = ctx.parent

return ctx

def start_of_option(param_str):
"""
Expand Down Expand Up @@ -164,6 +168,11 @@ def get_choices(cli, prog_name, args, incomplete):
# completion for any subcommands
choices.extend(ctx.command.list_commands(ctx))

if not start_of_option(incomplete) and ctx.parent is not None and isinstance(ctx.parent.command, MultiCommand) and ctx.parent.command.chain:
# completion for chained commands
remaining_comands = set(ctx.parent.command.list_commands(ctx.parent))-set(ctx.parent.protected_args)
choices.extend(remaining_comands)

for item in choices:
if item.startswith(incomplete):
yield item
Expand Down
26 changes: 26 additions & 0 deletions tests/test_bashcomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,32 @@ def csub(csub_opt, color):
assert list(get_choices(cli, 'lol', ['asub', 'bsub', 'csub'], 'b')) == ['blue']


def test_chaining():
@click.group('cli', chain=True)
@click.option('--cli-opt')
def cli(cli_opt):
pass

@cli.command('asub')
@click.option('--asub-opt')
def asub(asub_opt):
pass

@cli.command('bsub')
@click.option('--bsub-opt')
@click.argument('arg', type=click.Choice(['arg1', 'arg2']))
def bsub(bsub_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'], '-')) == ['--asub-opt']
assert list(get_choices(cli, 'lol', ['asub'], '')) == ['bsub']
assert list(get_choices(cli, 'lol', ['bsub'], '')) == ['arg1', 'arg2', 'asub']
assert list(get_choices(cli, 'lol', ['asub', '--asub-opt', '5', 'bsub'], '-')) == ['--bsub-opt']
assert list(get_choices(cli, 'lol', ['asub', 'bsub'], '-')) == ['--bsub-opt']


def test_argument_choice():
@click.command()
@click.argument('arg1', required=False, type=click.Choice(['arg11', 'arg12']))
Expand Down

0 comments on commit 92c8053

Please sign in to comment.