-
Notifications
You must be signed in to change notification settings - Fork 0
completions: Support adding additional code to complete values #65
Comments
Comment by kbknapp 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. |
Comment by joshtriplett 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. |
Comment by mathstuf 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 |
Comment by emk 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 |
Comment by kbknapp @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! |
Comment by emk Another approach worth a quick glance might be optcomplete for Python: I think another Python library just uses a '--_complete' on the program Thank you so much for a great library and for looking into this! |
Comment by emk 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. :-) |
Comment by joshtriplett @kbknapp Any updates on this mechanism? I have someone asking after completions, and I'd love to beta-test this. |
Comment by jcreekmore @kbknapp I would be interested in this as well. I am currently post-processing my completions to substitute in |
Comment by kbknapp 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? |
Comment by emk @kbknapp For This is pretty much how The problem with an |
Comment by joshtriplett @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),
} |
Comment by kbknapp @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 |
Comment by joshtriplett That's true, "one of these fixed strings" should be an option as well. Updating the type in my previous comment. |
Comment by kbknapp From @Xion in #816
|
Comment by kbknapp After reading through some of the |
Comment by kbknapp I'm guessing what'll end up happening is some sort of double run with hidden args. |
Comment by kbknapp Expanding on the ideas (from #818)
|
Comment by joshtriplett @kbknapp I like the idea of using parameters like Naming the argument/parameter seems sufficient to dispatch to the right function, though I'd also like to have the other arguments available to handle things like Also, those And what does the first |
Comment by mathstuf I'd just like to note that for really expensive queries (imagine tab-completion for |
Comment by joshtriplett
I'd expect tab-completion for Rather than attempting to integrate with any particular shell's completion caching, perhaps if an app considers its completions expensive to generate, it could cache the necessary information to make them fast? Caching policies seem much easier to implement in Rust, since they could use arbitrary freshness metrics ("the underlying data hasn't changed so return the cached list"). |
Comment by kbknapp @mathstuf @joshtriplett yeah, I'd prefer not to incorporate shell specific arguments if at all possible and leave that to the Rust function of the implementer.
It referred to the current arg being parsed (as determined by
I thought about this as well, and I'm torn between providing a simple list of strings (at which point why include them at all because people could just use |
Comment by joshtriplett @kbknapp Parsing partial arguments (without enforcing all of the argument requirements) seems like a pain, but I'd rather not manually implement argument parsing in order to do completion. That said, implementing this without support for parsing other arguments at first would still help in many cases. Does the "current arg being parsed" exist to allow passing the same completion function for multiple arguments, and distinguishing them via argument? If so, why not just do that using a closure? You could pass |
Comment by kbknapp
Actually I don't think it will be.
Actually I like that idea, I'll have to try this out when I get some time to sit down and try implementing this! |
Comment by droundy I would suggest rather than an enum that multiple methods setting completion possibilities would be a better and more extensible API. So rather than
you would have
and a whole set of other
where it is important to provide the completed flags, since that can affect what is a valid completion for a given flag. |
Comment by droundy Here is an example of a bash completion that simply calls command-line flags that return the possible completions for darcs. darcs does no shell-specific munging in its Haskell code (since it is usable by multiple shells), just outputs a |
Comment by kbknapp I'm copying some comments from @joshtriplett on gitter so the discussion is all in one place.
Here's my responses:
Agreed. However, I'm willing to "settle" for simply passing in argument to complete sans the argv, since the Rust code could essentially see the same thing by querying
Also agreed. I'm thinking I'd like to expose this as a sort of enum where the implementation allows checking the shell which we're outputting for and either using the shell builtins or augmenting the missing parts with our own code.
Correct. I don't think this needs to be fully hashed out though, as we can always add. For now, files is a good starting point. List of predetermined values is possible-ish today (in some shells, but we'd need to augment in the lacking shells) via
The ways I see are allowing the user to send shell specific code up front a la More generally, my goal is to pull the completion script generation out of clap proper and into a What that means is I don't want to get too into the weeds implementing ideas we reach here on the current 2.x branch only to have massive changes in 3.x. I do want to reach a consensus though, or agree upon a foundation API which could be implemented on the 3.x branch. Having said that, if someone puts time into actual implementation on the 2.x branch I'd be more than happy to include it. I just don't personally have the time to pour into separate 2.x and 3.x implementations. |
Comment by kpcyrd I think it's important that the function that is called by I'm about to add completion to two of my programs, mostly because the values that the user is usually trying to complete are hard to type and would require looking them up manually. I think a minimal invasive 2.0 compatible solution that would already work for most of us would be something along the lines of: .arg(Arg::with_name("foo")
.complete_with_cmd(&["myprog", "internal", "something"])
) This would instruct the shell to execute For now, this would require writing additional subcommands, but that code can be re-used for a more advanced solution that requires breaking changes to clap. If this is acceptable I would try to prepare a patch, completion is crucial for one of those programs and I would either have to maintain the tab completion code on my own or fallback to post-processing as well. |
Comment by softprops Here's some inspiration from how golang kingpin package handles dynamic tab completion using the bash COMPREPLY bash protocol Kingpin supports dynamic completions via it's HintAction interface I imagine this should be possible providing a fn interface to clap's Arg type for dynamic completions Under the covers kingpin forks program control when in completion mode to invoke that completion func then exits the process. It switches modes of operation based on a flag that the completion script passes. |
Comment by adamtulinius
Please not that kingpin currently can't complete file paths, which need something like alecthomas/kingpin@master...DBCDK:hint-files to fix. I'm just mentioning this, because it required work on the compreply stuff, and might be useful here as well. |
Issue by joshtriplett
Monday Jul 04, 2016 at 12:48 GMT
Originally opened as clap-rs/clap#568
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: