diff --git a/click_repl/__init__.py b/click_repl/__init__.py index eaa74ad..d41ffa7 100644 --- a/click_repl/__init__.py +++ b/click_repl/__init__.py @@ -26,7 +26,7 @@ class ClickExit(RuntimeError): text_type = str # noqa -__version__ = "0.1.6" +__version__ = "0.1.8" _internal_commands = dict() @@ -83,8 +83,9 @@ def _help_internal(): class ClickCompleter(Completer): - def __init__(self, cli, styles=None): + def __init__(self, cli, styles=None, ctx=None): self.cli = cli + self.ctx = ctx self.styles = styles if styles is not None else { 'command': '', 'argument': '', @@ -113,14 +114,18 @@ def get_completions(self, document, complete_event=None): # command, so give all relevant completions for this context. incomplete = "" - ctx = click._bashcomplete.resolve_ctx(self.cli, "", args) + ctx = click._bashcomplete.resolve_ctx(self.cli, "", list(args)) if ctx is None: return choices = [] + param_choices = [] + param_called = False + autocomplete_ctx = self.ctx or ctx for param in ctx.command.params: if isinstance(param, click.Option): - for options in (param.opts, param.secondary_opts): + possible_options = (param.opts, param.secondary_opts) + for options in possible_options: for o in options: choices.append( Completion( @@ -128,11 +133,33 @@ def get_completions(self, document, complete_event=None): style=self.styles['option'] ) ) + # We want to make sure if this parameter was called + if o in args[param.nargs * -1:]: + param_called = True + if param_called and hasattr(param, "autocompletion") and param.autocompletion is not None: + for autocomplete in param.autocompletion(autocomplete_ctx, args, incomplete): + if isinstance(autocomplete, tuple): + param_choices.append(Completion( + text_type(autocomplete[0]), -len(incomplete), + display_meta=autocomplete[1] + )) + else: + param_choices.append(Completion(text_type(autocomplete), -len(incomplete))) + elif isinstance(param, click.Argument): if isinstance(param.type, click.Choice): for choice in param.type.choices: choices.append(Completion(text_type(choice), -len(incomplete), style=self.styles['argument'])) + if hasattr(param, "autocompletion") and param.autocompletion is not None: + for autocomplete in param.autocompletion(autocomplete_ctx, args, incomplete): + if isinstance(autocomplete, tuple): + choices.append(Completion( + text_type(autocomplete[0]), -len(incomplete), + display_meta=autocomplete[1] + )) + else: + choices.append(Completion(text_type(autocomplete), -len(incomplete))) if isinstance(ctx.command, click.MultiCommand): for name in ctx.command.list_commands(ctx): @@ -146,12 +173,16 @@ def get_completions(self, document, complete_event=None): ) ) + # If we are inside a parameter that was called, we want to show only + # relevant choices + if param_called: + choices = param_choices for item in choices: if item.text.startswith(incomplete): yield item -def bootstrap_prompt(prompt_kwargs, group, styles=None): +def bootstrap_prompt(prompt_kwargs, group, styles=None, ctx=None): """ Bootstrap prompt_toolkit kwargs or use user defined values. @@ -162,7 +193,7 @@ def bootstrap_prompt(prompt_kwargs, group, styles=None): defaults = { "history": InMemoryHistory(), - "completer": ClickCompleter(group, styles), + "completer": ClickCompleter(group, styles, ctx=ctx), "message": u"> ", } @@ -219,7 +250,7 @@ def repl( # noqa: C901 available_commands = group_ctx.command.commands available_commands.pop(repl_command_name, None) - prompt_kwargs = bootstrap_prompt(prompt_kwargs, group, styles) + prompt_kwargs = bootstrap_prompt(prompt_kwargs, group, styles, ctx=group_ctx) if isatty: @@ -308,4 +339,3 @@ def handle_internal_commands(command): target = _get_registered_target(command[1:], default=None) if target: return target() -