-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Fix: include sub-actions in tab completion #13140
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
base: main
Are you sure you want to change the base?
Fix: include sub-actions in tab completion #13140
Conversation
("cache", "d", "dir"), | ||
("cache", "in", "info"), | ||
("cache", "l", "list"), | ||
("cache", "re", "remove"), | ||
("cache", "pu", "purge"), | ||
("config", "li", "list"), | ||
("config", "e", "edit"), | ||
("config", "ge", "get"), | ||
("config", "se", "set"), | ||
("config", "unse", "unset"), | ||
("config", "d", "debug"), | ||
("index", "ve", "versions"), |
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.
this list could probably be auto-generated from all commands with a non-empty handler_map
but I just hard coded it for simplicity (read: laziness 🙃)
Do such limitation apply to the current completions for the main commands ? At first sight it seems they don't (at least |
No, those limitations don't apply: for the sub-commands the completion behaviour is split:
So the only time we present sub-commands as completion options is when we haven't already seen any sub-commands The relevant code starts at
If we want to avoid the behaviour you mentioned in this change I think we can do something like: handler_names = subcommand.handler_map().keys()
# present handler names as completion only if we haven't already seen any handler names
if not any(name in cwords for name in handler_names):
for handler_name in handler_names:
if handler_name.startswith(current):
print(handler_name) This comes with two edge cases I can think of:
|
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.
@matthewhughes934 let's do your alternative. I'd rather offer no completions than offer bad completions. Otherwise this looks good.
FWIW, I strongly dislike that we call these "sub-actions" and not "sub-commands" like most other CLIs. I get that pip list
is considered a sub-command internally, but even our help output calls the top-level actions "commands".
@@ -0,0 +1 @@ | |||
Fix: include sub-actions in tab completion |
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.
Fix: include sub-actions in tab completion | |
Include sub-commands in tab completion. |
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.
78f19ec
to
de1ed6c
Compare
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.
Thank you!
handler_names = subcommand.handler_map().keys() | ||
if not any(name in cwords for name in handler_names): | ||
for handler_name in subcommand.handler_map(): | ||
if handler_name.startswith(current): | ||
print(handler_name) |
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.
handler_names = subcommand.handler_map().keys() | |
if not any(name in cwords for name in handler_names): | |
for handler_name in subcommand.handler_map(): | |
if handler_name.startswith(current): | |
print(handler_name) | |
# Complete sub-actions (unless one is already given). | |
if not any(name in cwords for name in subcommand.handler_map()): | |
for handler_name in subcommand.handler_map() | |
if handler_name.startswith(current): | |
print(handler_name) |
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.
👍 squashed that change in with the rest: 7a20ed1
de1ed6c
to
7a20ed1
Compare
For commands like `pip cache` with sub-commands like `remove`, so that e.g. `pip cache re<TAB>` completes to `pip cache remove`. All the existing commands that used such sub-commands followed the same approach: using a dictionary of names to methods to run, so the implementation is just teaching the `Command` object about this mapping and using it in the autocompletion function. There's no handling for the position of the argument, so e.g. `pip cache re<TAB>` and `pip cache --user re<TAB>` will both complete the final word to `remove`. This is mostly because it was simpler to implement like this, but also I think due to how `optparse` works such invocations are valid, e.g. `pip config --user set global.timeout 60`. This is a feature that may be simpler to implement, or just work out of the box, with some argument parsing libraries, but moving to another such library looks to be quite a bit of work (see discussion[1]). I also took the opportunity to tighten some typing: dropping some use of `Any` Link: pypa#4659 [1] Fixes: pypa#13133
7a20ed1
to
14f2acb
Compare
For commands like
pip cache
with sub-actions likeremove
, so that e.g.pip cache re<TAB>
completes topip cache remove
.All the existing commands that used such sub-actions followed the same approach for: using a dictionary of names to methods to run, so the implementation is just teaching the
Command
object about this mapping and using it in the autocompletion function.There's no handling for the position of the argument, so e.g.
pip cache re<TAB>
andpip cache --user re<TAB>
will both complete the final word toremove
. This is mostly because it was simpler to implement like this, but also I think due to howoptparse
works such invocations are valid, e.g.pip config --user set global.timeout 60
. Similarly, there's no duplication handling sopip cache remove re<TAB>
will also complete.This is a feature that may be simpler to implement, or just work out of the box, with some argument parsing libraries, but moving to another such library looks to be quite a bit of work (see discussion[1]).
I also took the opportunity to tighten some typing: dropping some use of
Any
Link: #4659 [1]
Fixes: #13133