-
-
Notifications
You must be signed in to change notification settings - Fork 135
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
Fix and test global completion in zsh #463
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -138,12 +138,6 @@ _python_argcomplete_global() { | |
req_argv=( "" "${COMP_WORDS[@]:1}" ) | ||
__python_argcomplete_expand_tilde_by_ref executable | ||
else | ||
if [[ "$service" != "-default-" ]]; then | ||
# TODO: this may not be sufficient - see https://zsh.sourceforge.io/Doc/Release/Completion-System.html | ||
# May need to call _complete with avoid-completer=_python-argcomplete or something like that | ||
_default | ||
return | ||
fi | ||
executable="${words[1]}" | ||
req_argv=( "${words[@]:1}" ) | ||
fi | ||
|
@@ -208,7 +202,15 @@ _python_argcomplete_global() { | |
_ARGCOMPLETE_SHELL="zsh" \ | ||
_ARGCOMPLETE_SUPPRESS_SPACE=1 \ | ||
__python_argcomplete_run "$executable" "${(@)req_argv[1, ${ARGCOMPLETE}-1]}")) | ||
_describe "$executable" completions | ||
local nosort=() | ||
local nospace=() | ||
if is-at-least 5.8; then | ||
nosort=(-o nosort) | ||
fi | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
|
||
if [[ "${completions-}" =~ ([^\\]): && "${BASH_REMATCH[2]}" =~ [=/:] ]]; then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note this must use |
||
nospace=(-S '') | ||
fi | ||
_describe "$executable" completions "${nosort[@]}" "${nospace[@]}" | ||
else | ||
COMPREPLY=($(IFS="$IFS" \ | ||
COMP_LINE="$COMP_LINE" \ | ||
|
@@ -234,5 +236,9 @@ _python_argcomplete_global() { | |
if [[ -z "${ZSH_VERSION-}" ]]; then | ||
complete -o default -o bashdefault -D -F _python_argcomplete_global | ||
else | ||
compdef _python_argcomplete_global -P '*' | ||
autoload is-at-least | ||
# Replace only the default completer (_default). | ||
# There are many other special contexts we don't want to override. | ||
# https://zsh.sourceforge.io/Doc/Release/Completion-System.html | ||
compdef _python_argcomplete_global -default- | ||
fi |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,7 +42,15 @@ | |
_ARGCOMPLETE_SHELL="zsh" \ | ||
_ARGCOMPLETE_SUPPRESS_SPACE=1 \ | ||
__python_argcomplete_run ${script:-${words[1]}})) | ||
_describe "${words[1]}" completions -o nosort | ||
local nosort=() | ||
local nospace=() | ||
if is-at-least 5.8; then | ||
nosort=(-o nosort) | ||
fi | ||
if [[ "${completions-}" =~ ([^\\]): && "${match[1]}" =~ [=/:] ]]; then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note the different indexing semantics between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The returned completion format is |
||
nospace=(-S '') | ||
fi | ||
_describe "${words[1]}" completions "${nosort[@]}" "${nospace[@]}" | ||
else | ||
local SUPPRESS_SPACE=0 | ||
if compopt +o nospace 2> /dev/null; then | ||
|
@@ -67,6 +75,7 @@ | |
if [[ -z "${ZSH_VERSION-}" ]]; then | ||
complete %(complete_opts)s -F _python_argcomplete%(function_suffix)s %(executables)s | ||
else | ||
autoload is-at-least | ||
compdef _python_argcomplete%(function_suffix)s %(executables)s | ||
fi | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -77,6 +77,8 @@ 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 | ||
|
||
|
||
|
@@ -1256,9 +1258,6 @@ 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)) | ||
if self.repl_provider == bash_repl: | ||
# Disable the "python" module provided by bash-completion | ||
sh.run_command("complete -r python python2 python3") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was not actually doing anything, since |
||
output = sh.run_command(self.install_cmd) | ||
self.assertEqual(output, "") | ||
# Register a dummy completion with an external argcomplete script | ||
|
@@ -1313,18 +1312,24 @@ class TestZsh(TestBashZshBase, unittest.TestCase): | |
"test_parse_special_characters_dollar", | ||
"test_comp_point", # FIXME | ||
"test_completion_environment", # FIXME | ||
"test_continuation", # FIXME | ||
"test_wordbreak_chars", # FIXME | ||
] | ||
|
||
def repl_provider(self): | ||
return zsh_repl() | ||
|
||
|
||
@unittest.skipIf(BASH_MAJOR_VERSION < 4, "complete -D not supported") | ||
class TestBashGlobal(TestBash): | ||
class TestBashZshGlobalBase(TestBashZshBase): | ||
install_cmd = 'eval "$(activate-global-python-argcomplete --dest=-)"' | ||
|
||
def test_redirection_completion(self): | ||
with TempDir(prefix="test_dir_py", dir="."): | ||
self.sh.run_command("cd " + os.getcwd()) | ||
self.sh.run_command("echo failure > ./foo.txt") | ||
self.sh.run_command("echo success > ./foo.\t") | ||
with open("foo.txt") as f: | ||
msg = f.read() | ||
self.assertEqual(msg, "success\n") | ||
|
||
def test_python_completion(self): | ||
self.sh.run_command("cd " + TEST_DIR) | ||
self.assertEqual(self.sh.run_command("python3 ./prog basic f\t"), "foo\r\n") | ||
|
@@ -1364,9 +1369,6 @@ def _test_console_script(self, package=False, wheel=False): | |
command = "pip install {} --target .".format(test_package) | ||
if not wheel: | ||
command += " --no-binary :all:" | ||
if sys.platform == "darwin": | ||
# Work around https://stackoverflow.com/questions/24257803 | ||
command += ' --install-option="--prefix="' | ||
install_output = self.sh.run_command(command) | ||
self.assertEqual(self.sh.run_command("echo $?"), "0\r\n", install_output) | ||
command = "test-module" | ||
|
@@ -1375,27 +1377,32 @@ def _test_console_script(self, package=False, wheel=False): | |
command += " a\t" | ||
self.assertEqual(self.sh.run_command(command), "arg\r\n") | ||
|
||
@unittest.skipIf(os.uname()[0] == "Darwin", "Skip test that fails on MacOS") | ||
kislyuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def test_console_script_module(self): | ||
"""Test completing a console_script for a module.""" | ||
self._test_console_script() | ||
|
||
@unittest.skipIf(os.uname()[0] == "Darwin", "Skip test that fails on MacOS") | ||
def test_console_script_package(self): | ||
"""Test completing a console_script for a package.""" | ||
self._test_console_script(package=True) | ||
|
||
@unittest.skipIf(os.uname()[0] == "Darwin", "Skip test that fails on MacOS") | ||
def test_console_script_module_wheel(self): | ||
"""Test completing a console_script for a module from a wheel.""" | ||
self._test_console_script(wheel=True) | ||
|
||
@unittest.skipIf(os.uname()[0] == "Darwin", "Skip test that fails on MacOS") | ||
def test_console_script_package_wheel(self): | ||
"""Test completing a console_script for a package from a wheel.""" | ||
self._test_console_script(package=True, wheel=True) | ||
|
||
|
||
@unittest.skipIf(BASH_MAJOR_VERSION < 4, "complete -D not supported") | ||
class TestBashGlobal(TestBash, TestBashZshGlobalBase): | ||
pass | ||
|
||
|
||
class TestZshGlobal(TestZsh, TestBashZshGlobalBase): | ||
pass | ||
|
||
|
||
class Shell: | ||
def __init__(self, shell): | ||
self.child = pexpect.spawn(shell, encoding="utf-8") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kislyuk This was added in afdf75b, but I'm not sure what the intent was. This was causing default completions to be mixed in with the returned completions in my tests, and removing it doesn't seem to have caused any of the existing tests to fail. If you remember what problem this was addressing, I can take another look into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@evanunderscore please do not delete this without a replacement, and especially please do not delete the comment. This was added in response to #454. Specifically, it was found that the default completer interferes with zsh completion in the redirect target context (words after
>
or2>
).I didn't manage to write a test for this, and I since realized that this was a stopgap solution that will need to be further refined. For example, instead of calling _default unconditionally as a fallback here, we may need to call it only when certain contexts like
-redirect-
are listed in$service
(see the link in the comment for a full list of contexts - there are others in the list that will almost certainly need similar treatment).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the issue link, I did search initially but wasn't able to find it.
I've pushed a fix in 1f6e661. Rather than handle all of the special contexts, I've updated the
compdef
command to only override-default-
which leaves the other special contexts undisturbed. The tests are all passing and I've poked around manually in the shell for a bit, and it seems to be working as expected.Are you happy with this fix and the comment I've left?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the macos tests are failing and I don't have a good way to troubleshoot them. I can just disable the global zsh tests on macos (since they weren't running at all before I added them), but it is strange that they were all passing before I made the
compdef
change.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that some of the runners already have
python-argcomplete
installed as a dependency ofpipx
, which explains why the failures seemed completely inconsistent - the tests were actually testing whichever version happened to be installed and not what was in the source. For now, I've added a step to uninstall it, but a better solution would be to revisit the change I suggested in #255. Without it, there is no good way to avoid this mismatch.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, thanks.