diff --git a/src/click/shell_completion.py b/src/click/shell_completion.py index 8f1564c49..a19c10277 100644 --- a/src/click/shell_completion.py +++ b/src/click/shell_completion.py @@ -297,6 +297,23 @@ def complete(self) -> str: """ args, incomplete = self.get_completion_args() completions = self.get_completions(args, incomplete) + + # When the incomplete value is in ``--option=value`` form, the + # shell will replace the entire token with the completion. + # Prefix each value with ``--option=`` so the result is + # ``--option=completed`` instead of just ``completed``. + if "=" in incomplete: + name, _, _ = incomplete.partition("=") + + if name.startswith("-"): + prefix = f"{name}=" + completions = [ + CompletionItem( + f"{prefix}{c.value}", c.type, c.help, **c._info + ) + for c in completions + ] + out = [self.format_completion(item) for item in completions] return "\n".join(out) diff --git a/tests/test_shell_completion.py b/tests/test_shell_completion.py index 20cff238f..30a4176dd 100644 --- a/tests/test_shell_completion.py +++ b/tests/test_shell_completion.py @@ -443,6 +443,47 @@ def test_zsh_full_complete_with_colons( assert result.output == expect +@pytest.mark.parametrize( + ("shell", "env", "expect"), + [ + ( + "bash", + {"COMP_WORDS": "cli --color=a", "COMP_CWORD": "1"}, + "plain,--color=auto\nplain,--color=always\n", + ), + ( + "bash", + {"COMP_WORDS": "cli --color=", "COMP_CWORD": "1"}, + "plain,--color=auto\nplain,--color=always\nplain,--color=never\n", + ), + ( + "zsh", + {"COMP_WORDS": "cli --color=a", "COMP_CWORD": "1"}, + "plain\n--color=auto\n_\nplain\n--color=always\n_\n", + ), + ( + "fish", + {"COMP_WORDS": "cli", "COMP_CWORD": "--color=a"}, + "plain,--color=auto\nplain,--color=always\n", + ), + ], +) +@pytest.mark.usefixtures("_patch_for_completion") +def test_option_eq_complete(runner, shell, env, expect): + """Completion for ``--option=value`` includes the option prefix. + + When a shell passes ``--color=a`` as the incomplete token, the + completions must be ``--color=auto`` etc. so the shell replaces + the entire token correctly. See :issue:`2847`. + """ + cli = Command( + "cli", params=[Option(["--color"], type=Choice(["auto", "always", "never"]))] + ) + env["_CLI_COMPLETE"] = f"{shell}_complete" + result = runner.invoke(cli, env=env) + assert result.output == expect + + @pytest.mark.usefixtures("_patch_for_completion") def test_context_settings(runner): def complete(ctx, param, incomplete):