Skip to content

Commit 752c5e4

Browse files
authored
Merge pull request #1061 from stopthatcow/feature/1058
Ignore completions for partially typed hidden commands and options. (Fixes #1058)
2 parents df0f81d + ecf4ce6 commit 752c5e4

File tree

3 files changed

+61
-10
lines changed

3 files changed

+61
-10
lines changed

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Unreleased
8484
- Use Python sorting order for ZSH completions. (`#1047`_, `#1059`_)
8585
- Document that parameter names are lowercased by default. (`#1055`_)
8686
- 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.
87+
- Hide hidden commands and options from completion. (`#1058`_, `#1061`_)
8788

8889
.. _#202: https://github.com/pallets/click/issues/202
8990
.. _#323: https://github.com/pallets/click/issues/323
@@ -193,7 +194,9 @@ Unreleased
193194
.. _#1027: https://github.com/pallets/click/pull/1027
194195
.. _#1047: https://github.com/pallets/click/pull/1047
195196
.. _#1055: https://github.com/pallets/click/pull/1055
197+
.. _#1058: https://github.com/pallets/click/pull/1058
196198
.. _#1059: https://github.com/pallets/click/pull/1059
199+
.. _#1061: https://github.com/pallets/click/pull/1061
197200

198201

199202
Version 6.7

click/_bashcomplete.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
%(complete_func)setup() {
2424
local COMPLETION_OPTIONS=""
2525
local BASH_VERSION_ARR=(${BASH_VERSION//./ })
26-
if [ ${BASH_VERSION_ARR[0]} -ge 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ];then
26+
# Only BASH version 4.4 and later have the nosort option.
27+
if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then
2728
COMPLETION_OPTIONS="-o nosort"
2829
fi
2930
@@ -176,7 +177,7 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param):
176177
if isinstance(cmd_param.type, Choice):
177178
# Choices don't support descriptions.
178179
results = [(c, None)
179-
for c in cmd_param.type.choices if c.startswith(incomplete)]
180+
for c in cmd_param.type.choices if str(c).startswith(incomplete)]
180181
elif cmd_param.autocompletion is not None:
181182
dynamic_completions = cmd_param.autocompletion(ctx=ctx,
182183
args=args,
@@ -186,20 +187,32 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param):
186187
return results
187188

188189

190+
def get_visible_commands_starting_with(ctx, starts_with):
191+
"""
192+
:param ctx: context associated with the parsed command
193+
:starts_with: string that visible commands must start with.
194+
:return: all visible (not hidden) commands that start with starts_with.
195+
"""
196+
for c in ctx.command.list_commands(ctx):
197+
if c.startswith(starts_with):
198+
command = ctx.command.get_command(ctx, c)
199+
if not command.hidden:
200+
yield command
201+
202+
189203
def add_subcommand_completions(ctx, incomplete, completions_out):
190204
# Add subcommand completions.
191205
if isinstance(ctx.command, MultiCommand):
192206
completions_out.extend(
193-
[(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in ctx.command.list_commands(ctx) if c.startswith(incomplete)])
207+
[(c.name, c.get_short_help_str()) for c in get_visible_commands_starting_with(ctx, incomplete)])
194208

195209
# Walk up the context list and add any other completion possibilities from chained commands
196210
while ctx.parent is not None:
197211
ctx = ctx.parent
198212
if isinstance(ctx.command, MultiCommand) and ctx.command.chain:
199-
remaining_commands = sorted(
200-
set(ctx.command.list_commands(ctx)) - set(ctx.protected_args))
201-
completions_out.extend(
202-
[(c, ctx.command.get_command(ctx, c).get_short_help_str()) for c in remaining_commands if c.startswith(incomplete)])
213+
remaining_commands = [c for c in get_visible_commands_starting_with(ctx, incomplete)
214+
if c.name not in ctx.protected_args]
215+
completions_out.extend([(c.name, c.get_short_help_str()) for c in remaining_commands])
203216

204217

205218
def get_choices(cli, prog_name, args, incomplete):
@@ -229,11 +242,10 @@ def get_choices(cli, prog_name, args, incomplete):
229242
if start_of_option(incomplete):
230243
# completions for partial options
231244
for param in ctx.command.params:
232-
if isinstance(param, Option):
245+
if isinstance(param, Option) and not param.hidden:
233246
param_opts = [param_opt for param_opt in param.opts +
234247
param.secondary_opts if param_opt not in all_args or param.multiple]
235-
completions.extend(
236-
[(o, param.help) for o in param_opts if o.startswith(incomplete)])
248+
completions.extend([(o, param.help) for o in param_opts if o.startswith(incomplete)])
237249
return completions
238250
# completion for option values from user supplied values
239251
for param in ctx.command.params:

tests/test_bashcomplete.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,39 @@ def esub():
371371
assert choices_without_help(cli, ['sub'], 'c') == ['csub']
372372
assert choices_without_help(cli, ['sub', 'csub'], '') == ['dsub', 'esub']
373373
assert choices_without_help(cli, ['sub', 'csub', 'dsub'], '') == ['esub']
374+
375+
376+
def test_hidden():
377+
@click.group()
378+
@click.option('--name', hidden=True)
379+
@click.option('--choices', type=click.Choice([1, 2]), hidden=True)
380+
def cli(name):
381+
pass
382+
383+
@cli.group(hidden=True)
384+
def hgroup():
385+
pass
386+
387+
@hgroup.group()
388+
def hgroupsub():
389+
pass
390+
391+
@cli.command()
392+
def asub():
393+
pass
394+
395+
@cli.command(hidden=True)
396+
@click.option('--hname')
397+
def hsub():
398+
pass
399+
400+
assert choices_without_help(cli, [], '--n') == []
401+
assert choices_without_help(cli, [], '--c') == []
402+
# If the user exactly types out the hidden param, complete its options.
403+
assert choices_without_help(cli, ['--choices'], '') == [1, 2]
404+
assert choices_without_help(cli, [], '') == ['asub']
405+
assert choices_without_help(cli, [], '') == ['asub']
406+
assert choices_without_help(cli, [], 'h') == []
407+
# If the user exactly types out the hidden command, complete its subcommands.
408+
assert choices_without_help(cli, ['hgroup'], '') == ['hgroupsub']
409+
assert choices_without_help(cli, ['hsub'], '--h') == ['--hname']

0 commit comments

Comments
 (0)