Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore completions for partially typed hidden commands and options. (Fixes #1058) #1061

Merged
merged 1 commit into from
Sep 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Unreleased
- Use Python sorting order for ZSH completions. (`#1047`_, `#1059`_)
- Document that parameter names are lowercased by default. (`#1055`_)
- Subcommands that are named by the function now automatically have the underscore replaced with a dash. If you register a function named ``my_command`` it becomes ``my-command`` in the command line interface.
- Hide hidden commands and options from completion. (`#1058`_, `#1061`_)

.. _#202: https://github.com/pallets/click/issues/202
.. _#323: https://github.com/pallets/click/issues/323
Expand Down Expand Up @@ -193,7 +194,9 @@ Unreleased
.. _#1027: https://github.com/pallets/click/pull/1027
.. _#1047: https://github.com/pallets/click/pull/1047
.. _#1055: https://github.com/pallets/click/pull/1055
.. _#1058: https://github.com/pallets/click/pull/1058
.. _#1059: https://github.com/pallets/click/pull/1059
.. _#1061: https://github.com/pallets/click/pull/1061


Version 6.7
Expand Down
32 changes: 22 additions & 10 deletions click/_bashcomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
%(complete_func)setup() {
local COMPLETION_OPTIONS=""
local BASH_VERSION_ARR=(${BASH_VERSION//./ })
if [ ${BASH_VERSION_ARR[0]} -ge 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ];then
# Only BASH version 4.4 and later have the nosort option.
if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then
COMPLETION_OPTIONS="-o nosort"
fi

Expand Down Expand Up @@ -176,7 +177,7 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param):
if isinstance(cmd_param.type, Choice):
# Choices don't support descriptions.
results = [(c, None)
for c in cmd_param.type.choices if c.startswith(incomplete)]
for c in cmd_param.type.choices if str(c).startswith(incomplete)]
elif cmd_param.autocompletion is not None:
dynamic_completions = cmd_param.autocompletion(ctx=ctx,
args=args,
Expand All @@ -186,20 +187,32 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param):
return results


def get_visible_commands_starting_with(ctx, starts_with):
"""
:param ctx: context associated with the parsed command
:starts_with: string that visible commands must start with.
:return: all visible (not hidden) commands that start with starts_with.
"""
for c in ctx.command.list_commands(ctx):
if c.startswith(starts_with):
command = ctx.command.get_command(ctx, c)
if not command.hidden:
yield command


def add_subcommand_completions(ctx, incomplete, completions_out):
# Add subcommand completions.
if isinstance(ctx.command, MultiCommand):
completions_out.extend(
[(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in ctx.command.list_commands(ctx) if c.startswith(incomplete)])
[(c.name, c.get_short_help_str()) for c in get_visible_commands_starting_with(ctx, incomplete)])

# Walk up the context list and add any other completion possibilities from chained commands
while ctx.parent is not None:
ctx = ctx.parent
if isinstance(ctx.command, MultiCommand) and ctx.command.chain:
remaining_commands = sorted(
set(ctx.command.list_commands(ctx)) - set(ctx.protected_args))
completions_out.extend(
[(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in remaining_commands if c.startswith(incomplete)])
remaining_commands = [c for c in get_visible_commands_starting_with(ctx, incomplete)
if c.name not in ctx.protected_args]
completions_out.extend([(c.name, c.get_short_help_str()) for c in remaining_commands])


def get_choices(cli, prog_name, args, incomplete):
Expand Down Expand Up @@ -229,11 +242,10 @@ def get_choices(cli, prog_name, args, incomplete):
if start_of_option(incomplete):
# completions for partial options
for param in ctx.command.params:
if isinstance(param, Option):
if isinstance(param, Option) and not param.hidden:
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(
[(o, param.help) for o in param_opts if o.startswith(incomplete)])
completions.extend([(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:
Expand Down
36 changes: 36 additions & 0 deletions tests/test_bashcomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,39 @@ def esub():
assert choices_without_help(cli, ['sub'], 'c') == ['csub']
assert choices_without_help(cli, ['sub', 'csub'], '') == ['dsub', 'esub']
assert choices_without_help(cli, ['sub', 'csub', 'dsub'], '') == ['esub']


def test_hidden():
@click.group()
@click.option('--name', hidden=True)
@click.option('--choices', type=click.Choice([1, 2]), hidden=True)
def cli(name):
pass

@cli.group(hidden=True)
def hgroup():
pass

@hgroup.group()
def hgroupsub():
pass

@cli.command()
def asub():
pass

@cli.command(hidden=True)
@click.option('--hname')
def hsub():
pass

assert choices_without_help(cli, [], '--n') == []
assert choices_without_help(cli, [], '--c') == []
# If the user exactly types out the hidden param, complete its options.
assert choices_without_help(cli, ['--choices'], '') == [1, 2]
assert choices_without_help(cli, [], '') == ['asub']
assert choices_without_help(cli, [], '') == ['asub']
assert choices_without_help(cli, [], 'h') == []
# If the user exactly types out the hidden command, complete its subcommands.
assert choices_without_help(cli, ['hgroup'], '') == ['hgroupsub']
assert choices_without_help(cli, ['hsub'], '--h') == ['--hname']