From 7931f75e7d74823a6cc25e0382c681988035679a Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 10 Dec 2023 10:35:43 +1100 Subject: [PATCH] Fix zsh global install (#466) * Revert "Use -first- instead of -default- when installing zsh global completer" This reverts commit 95da43507eaed949bc63539052a4c8d40d8bb98a. * Fix global installation for zsh * Use -Uz when loading zsh function --- .../bash_completion.d/_python-argcomplete | 19 ++++++++++++------ test/test.py | 20 +++++++++++++++---- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/argcomplete/bash_completion.d/_python-argcomplete b/argcomplete/bash_completion.d/_python-argcomplete index 1a8d6fc..b29d9cd 100644 --- a/argcomplete/bash_completion.d/_python-argcomplete +++ b/argcomplete/bash_completion.d/_python-argcomplete @@ -1,10 +1,15 @@ -#compdef -P * +#compdef -default- # Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. # Note: both the leading underscore in the name of this file and the first line (compdef) are required by zsh +# In zsh, this file is autoloaded and used as the default completer (_default). +# There are many other special contexts we don't want to override +# (as would be the case with `#compdef -P *`). +# https://zsh.sourceforge.io/Doc/Release/Completion-System.html + # Copy of __expand_tilde_by_ref from bash-completion # ZSH implementation added __python_argcomplete_expand_tilde_by_ref () { @@ -115,6 +120,7 @@ __python_argcomplete_which() { } _python_argcomplete_global() { + if [[ -n "${ZSH_VERSION-}" ]]; then # Store result of a regex match in the # BASH_REMATCH variable rather than MATCH @@ -235,9 +241,10 @@ _python_argcomplete_global() { if [[ -z "${ZSH_VERSION-}" ]]; then complete -o default -o bashdefault -D -F _python_argcomplete_global else - autoload is-at-least - # Set a hook for argcomplete to be called first if no other completers are defined for the given command. - # "compdef _python_argcomplete_global -P *" overrides other special contexts that we want to preserve: - # https://zsh.sourceforge.io/Doc/Release/Completion-System.html - compdef _python_argcomplete_global -first- + # -Uz is recommended for the use of functions supplied with the zsh distribution. + # https://unix.stackexchange.com/a/214306 + autoload -Uz is-at-least + # The comment at the top of this file causes zsh to invoke this script directly, + # so we must explicitly call the global completion function. + _python_argcomplete_global fi diff --git a/test/test.py b/test/test.py index 2498526..bae9ece 100755 --- a/test/test.py +++ b/test/test.py @@ -80,7 +80,6 @@ def bash_repl(command="bash"): def zsh_repl(command="zsh"): sh = _repl_sh(command, ["--no-rcs", "-V"], non_printable_insert="%(!..)") - sh.run_command("autoload compinit; compinit -u") # Require two tabs to print all options (some tests rely on this). sh.run_command("setopt BASH_AUTO_LIST") return sh @@ -1251,6 +1250,7 @@ def test_comp_point(self): class TestBashZshBase(TestShellBase): maxDiff = None + init_cmd = None # 'dummy' argument unused; checks multi-command registration works # by passing 'prog' as the second argument. install_cmd = 'eval "$(register-python-argcomplete dummy prog)"' @@ -1262,8 +1262,12 @@ def setUp(self): path = ":".join([os.path.join(BASE_DIR, "scripts"), TEST_DIR, "$PATH"]) sh.run_command("export PATH={0}".format(path)) sh.run_command("export PYTHONPATH={0}".format(BASE_DIR)) - output = sh.run_command(self.install_cmd) - self.assertEqual(output, "") + if self.init_cmd is not None: + output = sh.run_command(self.init_cmd) + self.assertEqual(output, "") + if self.install_cmd is not None: + output = sh.run_command(self.install_cmd) + self.assertEqual(output, "") # Register a dummy completion with an external argcomplete script # to ensure this doesn't overwrite our previous registration. output = sh.run_command('eval "$(register-python-argcomplete dummy --external-argcomplete-script dummy)"') @@ -1311,6 +1315,8 @@ def test_nounset(self): class TestZsh(TestBashZshBase, unittest.TestCase): + init_cmd = "autoload compinit; compinit -u" + skipped = [ "test_parse_special_characters", "test_parse_special_characters_dollar", @@ -1404,7 +1410,13 @@ class TestBashGlobal(TestBash, TestBashZshGlobalBase): class TestZshGlobal(TestZsh, TestBashZshGlobalBase): - pass + # In zsh, the file is not sourced directly; + # it is added to fpath and autoloaded by the completion system. + # Running `eval "$(activate-global-python-argcomplete --dest=-)"` + # as in bash does *not* work in zsh! + zsh_fpath = os.path.join(os.path.abspath(os.path.dirname(argcomplete.__file__)), "bash_completion.d") + init_cmd = f'fpath=( {zsh_fpath} "${{fpath[@]}}" ); autoload compinit; compinit -u' + install_cmd = None class Shell: