Skip to content
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

Stablize Rust-Native Completion Engine Tracking Issue #3166

Open
12 of 22 tasks
epage opened this issue Dec 13, 2021 · 22 comments
Open
12 of 22 tasks

Stablize Rust-Native Completion Engine Tracking Issue #3166

epage opened this issue Dec 13, 2021 · 22 comments
Labels
A-completion Area: completion generator C-enhancement Category: Raise on the bar on expectations C-tracking-issue Category: A tracking issue for an unstable feature E-help-wanted Call for participation: Help is requested to fix this issue. E-medium Call for participation: Experience needed to fix: Medium / intermediate

Comments

@epage
Copy link
Member

epage commented Dec 13, 2021

Maintainer's notes:

Remaining work for feature parity

Non-blocking work

Design considerations

  • Make it easy to use for the natively supported shells but allow other shells to be supported

Prior art


#3022 was a tipping point for me in realizing that maybe our current approach to completions doesn't work. We effectively have to implement a mostly-untested parser within each shell. Examples of other problems that seem to stem from this:

If we take the approach of argcomplete where we do the parsing in our core code, rather than in each completion script, this will help us share parsing logic between shell, share some or all parsing logic with clap itself, and make a subset of the logic more testable.

We also need to decide whether to morph the existing parser into supporting this or create a custom parser (since the needs are pretty special). If we do a custom parser, we should probably do #2915 first so we can reuse lexing logic between clap and the completion generation. I've also been considering #2912 which would allow reusing the completion logic with any CLI parser.

@epage epage added C-enhancement Category: Raise on the bar on expectations A-completion Area: completion generator S-blocked Status: Blocked on something else such as an RFC or other implementation work. labels Dec 13, 2021
@epage
Copy link
Member Author

epage commented Apr 15, 2022

Bash's expectations

Inputs for -F and -C:

  • COMP_LINE: The current command line. This variable is available only in shell functions and external commands invoked by the programmable completion facilities
  • COMP_POINT: The index of the current cursor position relative to the beginning of the current command. If the current cursor position is at the end of the current command, the value of this variable is equal to ${#COMP_LINE}. This variable is available only in shell functions and external commands invoked by the programmable completion facilities
  • COMP_KEY: The key (or final key of a key sequence) used to invoke the current completion function.
  • COMP_TYPE: Set to an integer value corresponding to the type of completion attempted that caused a completion function to be called: TAB, for normal completion, ‘?’, for listing completions after successive tabs, ‘!’, for listing alternatives on partial word completion, ‘@’, to list completions if the word is not unmodified, or ‘%’, for menu completion. This variable is available only in shell functions and external commands invoked by the programmable completion facilities
  • 1: name of the command whose arguments are being completed
  • 2: word being completed
  • 3: word preceding the word being completed on the current command line

Inputs for -F:

  • COMP_WORDS, COMP_CWORD when used with -F

Output for -C:

  • print a list of completions, one per line, to the standard output. Backslash may be used to escape a newline, if necessary.

Output for -F:

  • It must put the possible completions in the COMPREPLY array variable, one per array element.

See

@epage
Copy link
Member Author

epage commented Apr 16, 2022

argcomplete emulates bash's interface in fish by

    set -x COMP_LINE (commandline -p)
    set -x COMP_POINT (string length (commandline -cp))
    set -x COMP_TYPE

and then just registers that function

https://github.com/kislyuk/argcomplete/blob/develop/argcomplete/shell_integration.py#L60

@epage
Copy link
Member Author

epage commented Apr 16, 2022

Value hints are supported on

  • zsh
  • fish

Tooltips are supported on

  • zsh
  • powershell
  • elvish
  • fish

We should make sure we don't lose these features as part of this transition, ie we shouldn't drop to the lowest common denominator.

@epage
Copy link
Member Author

epage commented Apr 22, 2022

For Powershell

Register-ArgumentCompleter
        -CommandName <String[]>
        -ScriptBlock <ScriptBlock>
        [-Native]
        [<CommonParameters>]

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/register-argumentcompleter?view=powershell-7.2

The block receives

When you specify the Native parameter, the script block must take the following parameters in the specified order. The names of the parameters aren't important because PowerShell passes in the values by position.

  • $wordToComplete (Position 0) - This parameter is set to value the user has provided before they pressed Tab. Your script block should use this value to determine tab completion values.
  • $commandAst (Position 1) - This parameter is set to the Abstract Syntax Tree (AST) for the current input line. For more information, see Ast Class.
  • $cursorPosition (Position 2) - This parameter is set to the position of the cursor when the user pressed Tab.

The block provides CompletionResult

The CompletionResult object allows you to provide additional details to each returned value:

  • completionText (String) - The text to be used as the auto completion result. This is the value sent to the command.
  • listItemText (String) - The text to be displayed in a list, such as when the user presses Ctrl+Space. This is used for display only and is not passed to the command when selected.
  • resultType (CompletionResultType) - The type of completion result.
  • toolTip (String) - The text for the tooltip with details to be displayed about the object. This is visible when the user selects an item after pressing Ctrl+Space.

So it seems like Powershell can fit within rust-driven completions and provide the full feature set.

@epage
Copy link
Member Author

epage commented Apr 22, 2022

I'm starting small and prototyping for just bash support. Looking at argcomplete, it seems they had to use their own shlex implementation. Hoping we can avoid that. The first step is comex/rust-shlex#12.

@epage epage added E-medium Call for participation: Experience needed to fix: Medium / intermediate and removed S-blocked Status: Blocked on something else such as an RFC or other implementation work. labels Apr 27, 2022
@epage epage added the C-tracking-issue Category: A tracking issue for an unstable feature label Jun 16, 2022
@epage epage changed the title Leverage clap's parser for completions Stablize Rust-Native Completion Engine Trakcing Issue Jun 16, 2022
@epage epage changed the title Stablize Rust-Native Completion Engine Trakcing Issue Stablize Rust-Native Completion Engine Tracking Issue Jun 16, 2022
@epage epage added E-help-wanted Call for participation: Help is requested to fix this issue. 💸 $20 and removed 💸 $20 labels Jun 16, 2022
@happenslol
Copy link

Hi, I'm interested in helping with this this I'm developing a few personal tools using clap that would hugely benefit from dynamic completions. Is there any good issues that nobody is working on you could point me towards?

@epage
Copy link
Member Author

epage commented Jun 28, 2022

@happenslol Thanks!

Anything unchecked in the "Remaining work" section is up for grabs; just post here that you are getting started on it. The highest priority is the support for each shell as that will help gauge the feasibility and provide feedback on the API. The rest is flushing out parsing implementation.

I should split those out into individual issues to make it easier for people to coordinate on those (and to add bounties) but I probably won't have time for another week or so.

@happenslol
Copy link

Alright, had a busy week, so I'm only getting back around to this now.

I've looked at the work done in #3656, and it looks like the next steps to add zsh and fish support would be to pull some of the functionality out of clap_complete::dynamic::bash, and adapt it to the other shells to provide the same functionality. Am I correct in assuming that?

I'd look at argcomplete for the other implementations here, since the current one also seems to be coming from there.

@epage
Copy link
Member Author

epage commented Jul 3, 2022

@happenslol Yes, that is correct. I'd like to focus on shell support initially as that is where the most unknowns exist

@happenslol
Copy link

Started work here. I'm looking at cobra for zsh completions since argcomplete basically takes the easy way out and tells people to enable bash completion support for zsh 😅

@epage
Copy link
Member Author

epage commented Jul 11, 2022

Oh if cobra is doing the same type of dynamic completions as argcomplete, that is great! That'll serve as a much better example!

bors added a commit to rust-lang/cargo that referenced this issue Sep 10, 2024
feat: Add native comlpetion with CompleteEnv under the nightly

### What does this PR try to resolve?

Related issue #6645
Tracking issue #14520
This PR is the first step to move cargo shell completions to native completions by using `clap_complete` crate. It makes users could complete cargo subcommand and flags.

By using `clap_complete` crate, we could extend the supported shells to Bash, Zsh, Elvish, Fish, and PowerShell. However, at the current stage, the support for PowerShell in `clap_complete` is not fully developed.
See clap-rs/clap#3166 to get more context about what features `clap_complete` has supported.

### How to test and review this PR?

1. Build a test environment, including the necessary short completion scripts, and the `complete` function to start an interactive shell with the help of a pty device and obtain completion results.
2. Simply test the completion results of subcommands in bash, zsh, fish, elvish.
rsteube added a commit to carapace-sh/carapace-bin that referenced this issue Oct 24, 2024
rsteube added a commit to carapace-sh/carapace-bin that referenced this issue Oct 24, 2024
@calebbourg
Copy link

Hello @epage I'd love to get involved with this. Where might be a good place to provide some value here (something on the easier side to start maybe)?

@epage
Copy link
Member Author

epage commented Nov 5, 2024

The areas we need the most help are

@daniel5151
Copy link

Hey all, just wanted to drop a link to my own hand-rolled implementation of Rust-native argument completion for clap. The code is all MIT licensed. I wrote this to support a feature I wanted in $work project, and now that we went open-source, I can actually link to my effort on this issue.

https://github.com/microsoft/openvmm/blob/main/support/clap_dyn_complete/src/lib.rs

Note that I wrote this code a few years back, before the recent uptick in investments in resolving this issue, and may not reflect the current approach clap_complete is taking.

This implementation is not fully-featured by any means, but it does include support for several different shells (zsh, fish, powershell), various clap constructs, and support for dynamic runtime completions + async support (which $work project uses to dynamically suggest fields based on the return values from async RPC calls).

I'm quite interested in getting this sort of functionality working in clap_complete, but I unfortunately don't have time to do so myself. Folks should feel free to crib any/all functionality from this implementation, in case any of it is useful.

@epage
Copy link
Member Author

epage commented Nov 5, 2024

Some more quick ones

@epage
Copy link
Member Author

epage commented Nov 5, 2024

@daniel5151 looking over that, some differences in approach

  • clap_dyn_complete completes using the index into the string rather than an index into an array of arguments. This allows mid-word completions and I assume avoids COMP_WORDBREAKS problems (Support flags with values in native completions #3920) at the cost of needing to re-implement quoting. I wouldn't be surprised if we need to switch to that but wanting to see how viable our current approach is
  • clap_dyn_complete accepts completion information through the CLI. This creates more overhead as you now need your completer to run the CLI parser and enough application startup to get there and it is more rigid for adapting to shell-specific needs or features
  • clap_dyn_complete offers native async support. That makes it a lot heavier weight but can offer better concurrency for longer operations
  • clap_dyn_complete delay creates custom completers. The use case isn't too clear to me (complete would just be called once, why not just do the expensive stuff there?)
  • clap_dyn_complete builds on top of the existing parser to get context, relying on ignore_errors. imo a lot of different parts of the parser shouldn't run (like overrides) and instead we should be parse only whats done. I also don't trust ignore_errors enough to unwrap with it :). So far, we don't provide context though there is an issue for it.

@epage
Copy link
Member Author

epage commented Nov 5, 2024

@daniel5151 any integrating of ideas from clap_dyn_complete will require someone to drive it, creating issues from anything "needed" from it for the clap_complete solution to include, being able to talk to the problems or use cases that lead to the differences.

@daniel5151
Copy link

@epage, glad to see you took a look through the code!

A few additional thoughts, in response to your analysis.

  • clap_dyn_complete completes using the index into the string rather than an index into an array of arguments. This allows mid-word completions and I assume avoids COMP_WORDBREAKS problems (Support flags with values in native completions #3920) at the cost of needing to re-implement quoting. I wouldn't be surprised if we need to switch to that but wanting to see how viable our current approach is

This was quite important to me, as the key completion I wanted to support was akin to navigating a virtual directory listing (i.e: completing --remote-path foo/bar/baz, and I wanted to be able to support completions after any intermediate /.

But you're right - I certainly recall wrestling with nuances around quoting and shell arguments. Not sure I ever solved it cleanly.

  • clap_dyn_complete offers native async support. That makes it a lot heavier weight but can offer better concurrency for longer operations

IIRC, async support was purely a nice-to-have. I'm sure some more judicious use of block_on could've worked just as well (at least, for my use-case).

  • clap_dyn_complete delay creates custom completers. The use case isn't too clear to me (complete would just be called once, why not just do the expensive stuff there?)

I believe part of the rationale here was that I wanted to plumb-through some shared context into different completion handlers without needing to use a global static.

  • clap_dyn_complete builds on top of the existing parser to get context, relying on ignore_errors. imo a lot of different parts of the parser shouldn't run (like overrides) and instead we should be parse only whats done. I also don't trust ignore_errors enough to unwrap with it :). So far, we don't provide context though there is an issue for it.

Given that I basically wrote this implementation "for fun" at $work, I wasn't super keen getting bogged down in forking / digging through clap internals to get things up and running. I did the best I could with the API surface that was already available.

And of course - the only performance consideration I had in mind when writing clap_dyn_complete was "does it run well enough for the CLI in our project", which I'm sure wouldn't scale well to sorts of massive CLIs that exist out in the wild.


But yeah, ultimately, I only wrote clap_dyn_complete because there was a brief lull at $work, and I thought it'd be a fun little QOL feature to hack on. I'm sure many of my design choices wouldn't translate to a robust, "productized" version of dynamic argument completions.

My goal here was to simply call-out some prior art in this space, and have clap_dyn_complete serve as yet another reference point for any folks actively involved in driving this issue forwards from the clap side.

@shannmu
Copy link
Contributor

shannmu commented Nov 6, 2024

@daniel5151 Thank you for sharing. Regarding the issue of allowing completion in the middle of words and with COMP_WORDBREAKS, the current clap_complete doesn't handle it very well. I’ll take a look at your approach.

I’ve tried using bash built-in functions like __init_completions in scripts to mask certain characters, and it successfully entered the common complete logic. However, it seems that bash’s post-processing of completions has some special handling. For example, when completing --flag=, it turns the --flag=bar completion into --flag=--flag=bar.

@epage , I have an important deadline on December 2nd, so during this period, I might only push some of the previous PRs. However, I’ve been following the community’s progress on clap_complete and I will accelerate the effort in December.

And thanks for your involvement, @calebbourg . I’ve contributed to the clap_complete project as well. Feel free to reach out to me via email or zulip https://rust-lang.zulipchat.com/#narrow/channel/421156-gsoc/topic/Project.3A.20Move.20cargo.20shell.20completions.20to.20Rust if you have any questions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-completion Area: completion generator C-enhancement Category: Raise on the bar on expectations C-tracking-issue Category: A tracking issue for an unstable feature E-help-wanted Call for participation: Help is requested to fix this issue. E-medium Call for participation: Experience needed to fix: Medium / intermediate
Projects
None yet
Development

No branches or pull requests

6 participants