-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
completions: Support adding additional code to complete values #568
Comments
Perhaps adding something like was discussed in in #376 where there is a "completer" function? I'm all for this, but figured it's also addable in a backwards compatible way once I had the base implementation complete. I'm just not sure which would be the best way to add this so I'm open to all ideas. |
I'm honestly not sure either. I'm really hesitant to suggest inlining shell script snippets into Rust code as strings; I'd rather see those written in a separate shell file and included from there (not least of which to get the right filetype and highlighting). bash (via I think what I'd suggest is that the This is turning out to be a remarkably hairy yak. |
In zsh at least, clap could generate completion function calls such as: (( $+functions[_appname_clap_complete_ARG] )) || _appname_clap_complete_ARG () {
} Which can then be overridden in a supplemental file included before this one (via |
I've just converted cage to use clap, and I'm very happy with the results. Basic completion works under both bash and fish. Great code! But cage would benefit enormously from being able to dynamically complete the names of docker services for commands like: cage test $SERVICE_NAME If the project in the current directory has the ervices > cage test f<TAB>
foo
frontend/bar I would be happy to add an extra argument to the app, something like: > cage --_complete-service f
foo
frontend/bar And declare this as: - SERVICE:
value_name: "SERVICE"
required: true
complete_with: "_complete-service"
help: "The name of the service in which to run a command" Obviously the details could vary a bit, but we would ultimately have |
Note that our completion support is incomplete pending solutions for these two issues: clap-rs/clap#568 clap-rs/clap#685
@emk Your post has me thinking about this more and more, I'm thinking some sort of hybrid between what @joshtriplett listed above and what you're proposing. My schedule is pretty busy this week, but I should be able to at least test some ideas and see the feasibility. Stay tuned to this thread for updates! |
Another approach worth a quick glance might be optcomplete for Python:
http://furius.ca/optcomplete/ As far as I can tell, this uses one small,
universal shell script for each supported shell, and offloads all the
actual completion work to the application's own arg parsing machinery.
I think another Python library just uses a '--_complete' on the program
that does all the actual work, but I can't find it right now. I'll keep
Googling around when I have moment and post anything interesting I find.
Thank you so much for a great library and for looking into this!
|
Ah, here we go. Some docs on how several Python arg parsing libraries handle
Here is the generic completion function for bash: _foo()
{
prog="$1"
while read glob_str; do
case $prog in
$glob_str)
return 1;;
esac
done < <( echo "$SELFCOMPLETION_EXCLUSIONS" )
which "$prog" >/dev/null || return 1
_COMP_OUTPUTSTR="$( $prog --_completion "${COMP_WORDS[*]}" 2>/dev/null )"
if test $? -ne 0; then
return 1
fi
readarray -t COMPREPLY < <( echo -n "$_COMP_OUTPUTSTR" )
}
complete -o default -o nospace -D -F _foo The advantage of this approach is that the per-shell code can be written only once, and all the hard work can be done directly by the application itself. Obviously, there might be disadvantages as well. But I figured it was worth tracking down all the existing attempts to standardize this to see if any of them had helpful ideas. :-) |
@kbknapp Any updates on this mechanism? I have someone asking after completions, and I'd love to beta-test this. |
@kbknapp I would be interested in this as well. I am currently post-processing my completions to substitute in |
Now that the ZSH implementation is complete I've got a better handle on this. The biggest issue I see holding this up is that completions are done differently between all three (so far) supported shells. I'm all for some sort of enum with variants that allow things like, I'm just unsure of the best way to expose this. Perhaps on an I guess, first what is the shell you're trying to support, and what particular portions are you wanting to inject into the completion code? |
@kbknapp For This is pretty much how The problem with an |
@kbknapp I don't think you need to support embedding arbitrary shell code from Rust. My suggestion would be to support the lowest-common-denominator bits (filenames, usernames, filenames matching one of these patterns, etc), and then have a "call this Rust function" variant that invokes the program itself with some internal hidden --clap-complete option that dispatches to that Rust function. That makes it easy to do things like "a git ref matching this pattern", by calling a Rust function implementing that. For those common categories like filenames or usernames, use the shell built-in mechanisms if available, or provide and use code implementing those simple completions if the shell doesn't have them. If people want "invoke this shell code", I'd suggest adding a Rust variant to call a named shell function, and then letting people add that shell function to the resulting generated completions for any shell they support. That seems preferable to embedding many variants of shell code directly. enum Completion<F: Fn(...) -> ...> {
File,
User,
Hostname,
Strings(&[str]),
Globs(&[str]),
ShellFunction(&str), // maybe
RustFunction(F),
} |
@emk Yes, and no. It would be extensible, so more variants could be added. But Some of the variants could also take additional parameters, and ultimately (possibly) injecting arbitrary shell script via something like , At the same time, I haven't looked into exactly where this code would be injected and ultimately if it's even feasible yet. This is just straight of the top of my head right now. Also, if the "types" are know prior to runtime ZSH already supports this just by using the |
That's true, "one of these fixed strings" should be an option as well. Updating the type in my previous comment. |
|
After reading through some of the |
I'm guessing what'll end up happening is some sort of double run with hidden args. |
Expanding on the ideas (from #818)
|
@NotBad4U you can start working on it. if you need any help you can ask us in #wg-cli channel on discord. |
https://github.com/posener/complete is a library dedicated to dynamic completion written in Go, if you're looking for some ideas/inspiration. Despite the repo's description, it supports bash, zsh & fish. |
Here is a proposal that I think would be relatively simple. I haven't checked how zsh completion is done, but it's my understanding that it understands bash so you could just re-use it? The generated scripts for both fish and bash already support dynamic completions. Both compgen for bash and complete for fish honor parameter expansion, including shell substitution and word split. That is to say if you throw Proposal: add |
I've forgotten almost all I once knew about the Zsh completion system, but I recall that it's much more powerful/expressive(/fancy) than Bash's such that, if I recall correctly, telling it to use Bash completion, while the easy way out, could provide a needlessly suboptimal user experience. That said, I would think that such a project could start with telling Zsh to use Bash completion and come back and write native Zsh completion later. |
#1793 is an attempt at fixing this in zsh. |
Are #1232 and this now dupes? Should we close one in favor of the other to make it easier to browse the backlog? |
I kept them separate because this issue is more concentrated on leveraging shell completions while the other is for a full fledged solution. |
I'm looking to try and implement this. Is there an API in specific that should be used? |
We currently provide ValueHint for leveraging shell logic, though it isn't customizable. For that, we have #1232. I'm inclined to close this in favor of wrapping up this request with #1232. I'm hesitant for us to have two different solutions (a Rust driven and shell-specific snippets) and doing shell snippets would couple args to completion so they would know what shell they are completing for. We can always expand how we are using If there is any concern with closing this, please let us know! |
Closing comment:
For some argument values, the bash-completions may want to include additional logic for what type of value to complete, rather than allowing arbitrary strings. For instance, an option might accept a path to a file that exists; bash has a mechanism for that. Or, an option might accept the name of a git ref that exists; that's something more easily implemented in the program in Rust. Either way, it makes sense to augment clap's completion logic.
This also argues for implementing the completions by calling the program at runtime, rather than via shell; that way, arbitrary logic in Rust can determine what to complete, rather than providing a template system to feed in shell code (ick).
The text was updated successfully, but these errors were encountered: