-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Completion support for Nushell #1857
base: main
Are you sure you want to change the base?
Conversation
Jack Wright seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
Hey @ayax79 this exciting! I have never used I see that this PR generates a script containing the completions. However, this is no longer the approach used by Cobra. Instead, the program, through Cobra, provides the completions itself through the hidden This approach means that we have centralized all the logic to generate completions and coded it in Go (in Cobra's Following this approach would be your first step. You can look at the completion scripts for any of the other shells to see how this is done. These scripts are in files named for the shell they support. Eventually, the nushell script should also support the ShellCompDirectives, which you will notice in the other scripts. Feel free to ask for more info if needed. |
Hey @marckhouzam, Thanks for looking at this and thanks for the feedback! Nushell completions quite a bit differently than completions in other shells I have used (zsh). nushell_completions.go doesn't generate a script for the completions, it generates something more akin to a header file. each "export extern " maps to an external, non-nushell command. An example would be For example, see the minikube-completions.nu I generated. You can load it into nushell via (added .txt to get it to upload): I'll dig into the new model, but I would love any implementation advice. |
We had a similar situation with Fish where normal completions are a long list of choices. So in our case we have one generic completion entry which calls a function that calls The nushell doc talks about custom completions which seem like an avenue to investigate. For example it shows:
which calls |
An important advantage of Cobra's approach is that using the |
@rsteube the author of (carapace)[https://github.com/rsteube/carapace] chimed in with some good feedback on the nushell discord that I will include too. |
I have played around with a few options and haven't found anything that works well utilizing __command. This works for only the first level for commands and breaks flags:
To access a flag you have to bypass nushell via:
By adding another level:
This overrides the completion list and removes the description (unless it is manually added as comment). It also breaks any flags. The other option is to utilize the external completer support. This however only provides an option for one global fallback completer. This how carapace-bin works with nushell. I'll poke around more, but it seems like I am may be running out of options. |
Thanks for continuing the investigation @ayax79. In the above comment you typed It is also important to pass the current command-line arguments that the user typed. So, if the user typed Finally, if the last character on the command-line before the user pressed TAB is a space, you must pass an extra empty argument. So, if the user typed You can find documentation about all that here: https://github.com/spf13/cobra/blob/main/shell_completions.md#debugging |
Yeah you'll definitely have no success with the let external_completer = {|spans|
{
$spans.0: { } # default
kubectl: { kubectl __complete ($spans | into df | slice 1 1000) | handleOutput }
minikube: { minikube __complete ($spans | into df | slice 1 1000) | handleOutput }
} | get $spans.0 | each {|it| do $it}
} |
I finally got some more time to work on this. I also spent some time digging into the cobra completions and the nushell external completions code . This seems to work, although it needs some more testing. There might be some weirdness around mixing flags and commands:
As @rsteube mentioned, registration will be a little bit odd. I think that can be documented in commends for the completion generation though. It will require adding the that code block to the nushell config file ($nu.config-path) and updating config.external_completer entry:
One nice thing is that for other cobra commands, only the external_completer block will need to be updated. @marckhouzam, if this approach looks good, I'll update nushell_completer.go with it. |
Thanks @ayax79. I'm assuming that each tool,
The problem is that if I have multiple tools using this, only the last sourced script will work as each one sets the same After that, we need to figure out a way to have the one
manage to include all tools that have cobra completions. |
If we took this approach, it would still be possible to wrap the cobra completer and chain completers together -- it would be up to the user to figure how they wanted to handle it. The configuration itself isn't changeable once the shell is loaded. Nushell works much more like a compiled language. Dynamic loading of resources and execution isn't really possible. The two phase execution approach is part of the reason why nushell is so fast, but it does create some awkwardness. You can see how I got around this, by creating a subshell to execute the command I assembled:
What I really don't like about this approach is that nushell's environment has to load to execute the command (I think, need to verify). It might be feasible to do something kind of like execute completions from a directory, but it could get expensive:
Using something like carapace-bin is the best use of external_completers, because carpace itself is plugable. |
Not knowing anything about nushell, I'm having trouble following the subtleties. If we can reach a solution where any one tool can generate some nushell completion script and the user could add something to their configuration file to setup any number of such scripts, we would have something sufficient. |
I simplified the nushell external configurator to this:
The process would be:
There will be no need to change the configuration for any subsequent cobra based app. The above configuration will work with any cobra based app without any further change. |
Wow! After sourcing the script in the latest comment and then doing
I was able to do completion for helm and kubectl! One question with this approach: are we going to break completion for any non-cobra program that uses the external_completer? @ayax79 Could you update the PR with the new script and then I can start testing more thoroughly? One thing to investigate is that flag value completion does not seem to work:
We should be getting a list of namespaces. But this is a great start! |
The external completer is a fallback. Completions using externs will load first. If someone wants to use more than one external completer they could write a completer that wraps two:
I'll poke around with the flag completions. It should work the same as anything else. |
There was a bug in the line parsing causing it to drop entries that weren't exactly {value}\t{description}. Anything without a description was getting dropped. I changed to parse via regex. The only thing that might get dropped with this one is values (commands/flags/options) that have particularly exotic characters, I am matching [\w-.:+]. This is the corrected version:
I will have an updated pull request shortly. |
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 is looking great. Here are some things I noticed:
- Could you add
nushell
to the defaultcompletion
command? For example: https://github.com/spf13/cobra/blob/main/completions.go#L782-L785 - The
=
form of flags does not quite work. When doingkubectl -n=<tab>
, the-n=
part is removed - Probably linked to the previous point, doing
kubectl --namespace=<tab>
then selecting a completion choice, and then pressing backpace actually crashes the shell - When a completion is accepted a space should be added after it. For example
kubectl comple<tab>
should becomekubectl completion
(with a space aftercompletion
) ( except if theShellCompDirectiveNoSpace
is specified) - Would we be able to support the ShellComp directives? Or maybe a subset of them?
Lines 54 to 78 in 7bb1440
const ( // ShellCompDirectiveError indicates an error occurred and completions should be ignored. ShellCompDirectiveError ShellCompDirective = 1 << iota // ShellCompDirectiveNoSpace indicates that the shell should not add a space // after the completion even if there is a single completion provided. ShellCompDirectiveNoSpace // ShellCompDirectiveNoFileComp indicates that the shell should not provide // file completion even when no completion is provided. ShellCompDirectiveNoFileComp // ShellCompDirectiveFilterFileExt indicates that the provided completions // should be used as file extension filters. // For flags, using Command.MarkFlagFilename() and Command.MarkPersistentFlagFilename() // is a shortcut to using this directive explicitly. The BashCompFilenameExt // annotation can also be used to obtain the same behavior for flags. ShellCompDirectiveFilterFileExt // ShellCompDirectiveFilterDirs indicates that only directory names should // be provided in file completion. To request directory names within another // directory, the returned completions should specify the directory within // which to search. The BashCompSubdirsInDir annotation can be used to // obtain the same behavior but only for flags. ShellCompDirectiveFilterDirs - File completion does not seem to work: if there are no completions choices, the shell should suggests file names (except if the
ShellCompDirectiveNoFileComp
is specified)
- Renamed completer to be cobra_completer to match docs - Added whitespace after each completion - Implemented ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp - Disabled active help as it isn't supported by nushell - Added nushell to the default completion command
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
I just found a nasty bug. When running things vim, it locks up because vim interprets |
I think I am going to have to put a whitelist in for commands to execute the completer for:
I don't think it will be too onerous. The workflow will be: If you haven't setup cobra_completer
If you have setup cobra_completer
|
For other shells we don't use a "global" completer but instead have to register each program individually. So if kubectl generates the completion script, it also register If nushell completions don't work like that then your allow-list idea sounds like the right approach. I wonder if we could automate adding a program name to the list in the script itself? |
Ok. Until that is fixed it just won't work for Cobra. It's not a blocker to get this merged. |
|
||
let return_val = if $last_span =~ '=' { | ||
# if the completion is of the form -n= return flag as part of the completion so that it doesn't get replaced | ||
$completions | each {|it| $"($last_span | split row '=' | first)=($it.value)" } |
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.
It is valid for a program to request file completion for a flag value. For example:
kubectl apply -f <tab>
This currently works.
However, if we use the =
for it does not:
kubectl apply -f=<tab>
I'm not sure if you will be to handle that case or not. If not, we'll just have to live with it. It's not critical.
|
||
# Cobra returns a list of completions that are supported with this directive | ||
# There is no way to currently support this in a nushell external completer | ||
let completions = if $directive == $ShellCompDirectiveFilterFileExt { |
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.
Might as well check for ShellCompDirectiveFilterDirs
here also, since it is also not supported.
Yeah, got the same behaviour. Seems to be caused by nushell. So best to open an issue there. gh <TAB> issue
# 2022/12/30 10:45:45 nushell ["carapace","_carapace","nushell","gh","a"] If i am not mistaken this is related to space suffix determination. I think it was nushell that was adding a character ( see https://github.com/nushell/nushell/blob/be311829690c511edd16149b3183b8e0fef0d336/crates/nu-cli/src/completions/completer.rs#L119 and the block at line |
@ayax79 Just checking about this PR. I believe the ball was in your court, or am I mistaken? |
Hi @marckhouzam, I know.. I have been pretty slammed with starting a new job. I am hoping to get back to it soon. |
No worries, I am in the same situation. I was just asking to be sure. |
Hey, may I ask for a summary of what is actually still to do here? Is it just what is mentioned here? #1857 (comment) Maybe I could chime in and help. It is sad to see this sitting around here for so long :o |
Yes I believe that’s the main part that remains. |
Well, I don't know nushell very well either, but it is a nice cross platform shell and I see more and more people using it. So yeah, I'd have to read the docs first as well :D Thanks for the quick reply |
I can try to pick it up again. It's been on the back of my mind lately. |
I'm also on the nushell core team now. |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
@marckhouzam, I have been thinking more about the direction this PR has currently taken of generating a script. A lot has matured around external completers and nushell since I first began this. Other external completers such as nushell, fish, etc handle most of the logic within the command themselves (from external completers nushell cookbook chapter): let carapace_completer = {|spans|
carapace $spans.0 nushell ...$spans | from json
}
let fish_completer = {|spans|
fish --command $'complete "--do-complete=($spans | str join " ")"'
| $"value(char tab)description(char newline)" + $in
| from tsv --flexible --no-infer
} What if instead of generating a rather long nushell script, if let minikube_completer = {|spans|
minikube completions nushell $spans | from json
} thoughts? |
This is actually what cobra does right now. If you look at the generated
If memory serves, we had moved towards such a solution in this PR and were very close to having everything working. Le me know if you need clarifications. |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
Completion support for Nushell. Besides the tests included, I have also tested against minikube.