dotnet / command-line-api

Command line parsing, invocation, and rendering of terminal output.
https://github.com/dotnet/command-line-api/wiki
MIT License
3.34k stars 375 forks source link

Support Fish Shell Suggestions #1173

Open slang25 opened 3 years ago

slang25 commented 3 years ago

It would be great if Fish was added to the supported shells for dotnet-suggest as documented here.

slang25 commented 3 years ago

Could someone please add the shell suggestion label please 🙂

deg0nz commented 2 years ago

Hi,

I spent the last two evenings on writing a shim script for fish that provides basic completion. I used the zsh shim script as a basis.

Here it is:

# dotnet suggest shell complete script start
function _dotnet_fish_complete
    # Get current content of the command line
    set --local cmd_line (commandline --current-process --tokenize --cut-at-cursor)

    # Get full path to script because dotnet-suggest needs it
    # NOTE: this requires a command registered with dotnet-suggest be on the PATH
    set --local full_path (which $cmd_line[1]) # fish arrays are 1-indexed

    # Get the completion results, will be newline-delimited
    # `string collect` is used to receive multiline input and leave it like this
    set --local completions (dotnet-suggest get --executable "$full_path" -- "$cmd_line" | string collect)

    # Return list of completions as content for `complete --arguments`
    echo $completions
end

# Apply this function to each command the dotnet-suggest knows about
function _register_dotnet_completions
    for cmd in (dotnet-suggest list)
        # This is a workaround for the fact that Fish is missing something like compdef (zsh) or complete -F (bash)
        # It calls _dotnet_fish_complete when tabbing the respective command, 
        # which then fills --arguments with a list of the current suggestions generated by `dotnet-suggest`
        complete --no-files --command $cmd --arguments '(_dotnet_fish_complete)'
    end
end

# Call register function
_register_dotnet_completions

set -x DOTNET_SUGGEST_SCRIPT_VERSION "1.0.0"
# dotnet suggest shell complete script end

However, since the fish completion system is mightier than the ones of other shells, this is not adequate imho. It only delivers a very basic completion style. But it's better than nothing for now.

It also comes with the following caveats:

Additional thoughs about this

Note: For better understanding how fish completions usually work, here is an example for the dotnet-suggest --help flag, which would then represent one suggestion item:

complete --short h --long help --description "Show help and usage information"

A more fish-agnostic way would be to generate real fish completion files for every command (Edit: examples); containing all the bells and whistles (--short, --long, Files yes/no, --description, subcommand distinction ...) for each argument and subcommand and put them into the right folder. Of course this also comes with the caveat that one has to generate all these scripts again when anything in the CLI changes. But this is something you have to deal with when using fish either way. So there should be a possibility for developers using System.CommandLine.Suggestions to generate a completion file for their program.

This would probably also mean that one would need to touch System.CommandLine.Suggestions to add a generator class (which would probably not be that big, the Rust clap CLI library one is not even 200 lines long), but that is a descision the maintainers have to make.

Another alternative would be to write a sophisticated fish script for this that would be quite big (which wouldn't be a big deal, because one should but this script to ~/.config/fish/functions/ and just call the register function in ~/.config/fish/config.fish -- what would be a big deal though, is the shell start time when generating all the scripts for the first time)

I'm also still wrapping my head around how one could achieve this dynamically by constructing the right complete commands for every suggestion item, but I wasn't able to find a way that tells fish to use a function for completions.

(I was also wondering where the correct fish-agnostic completions for the dotnet command come from on my system, but it seems that they are shipped with fish itself)

Edit: grammar, typos

jonsequitur commented 2 years ago

Thanks, @deg0nz! Would you be open to submitting a pull request?

However, since the fish completion system is mightier than the ones of other shells, this is not adequate imho. It only delivers a very basic completion style. But it's better than nothing for now.

Can you provide some additional details or screenshots here? Are there features that System.CommandLine appears to be missing to provide this experience? We've recently made its completion model richer, though dotnet-suggest doesn't expose this yet.

We've also been discussing how best to support richer scripts for specific shells, including approaches like generating scripts that handle many completions without needing to call dotnet-suggest.

Regarding file completions, we're planning to include better support for including them in the default System.CommandLine completions (#1697), but as you pointed out this is also an area where specific shells might have better approaches. It's just a bit more work to do something different for each shell and community involvement will be important.

deg0nz commented 2 years ago

Hey, @jonsequitur

Would you be open to submitting a pull request?

Sure, can do. I have no experience in dotnet but I will just orientate on the zsh shim PR, that looks pretty straightforward.

Can you provide some additional details or screenshots here? Are there features that System.CommandLine appears to be missing to provide this experience? We've recently made its completion model richer, though dotnet-suggest doesn't expose this yet.

I tried to explain that in the "additional thoughts" section above. What I meant by that was just the fact that fish actually needs dedicated completion scripts to use the full potential of it's completion system.

Fish suggestions actually look like this:

Screenshot 2022-04-26 at 12 40 50

And fish is also able to distinguish between subcommands and flags like so:

Screenshot 2022-04-26 at 12 42 32

Those completions rely on more or less complex completion scripts as you can see here in the official Git completion file (This is a very complex example though...)

When you look at the complete commands from line 839 onwards, you can see that every option has it's own entry including the description. So the shim script I wrote above is just a dirty workaround to provide suggestions offered by dotnet-suggest to the user, since only 1 complete is used that is filled with the output of dotnet-suggest.

We've also been discussing how best to support richer scripts for specific shells, including approaches like generating scripts that handle many completions without needing to call dotnet-suggest.

This is exactly what I meant :) So yeah, until completion-script-generators get implemented the shim script above will suffice as basic completion helper I think.