CLIUtils / CLI11

CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.
https://cliutils.github.io/CLI11/book/
Other
3.29k stars 347 forks source link

Autocomplete for CLI11 #343

Open jbriales opened 4 years ago

jbriales commented 4 years ago

About "Autocomplete: This might eventually be added to both Plumbum and CLI11, but it is not supported yet." Is there any plans or thoughts on this feature at this point?

I've been thinking about giving this a try myself for some time. Before going for it, I'd appreciate any pointers/caveats/ideas in this regard :)

jbriales commented 4 years ago

No thoughts here? :)

henryiii commented 4 years ago

Yes, thoughts are just slow coming. :)

I looked into this a bit, and have some general ideas. I'll try to collect my ideas and would be happy to have someone work on this. Ping me on Thursday-Friday if I don't do something by then (have to prepare materials to teach a class Wednesday).

jbriales commented 4 years ago

Haha thanks, that sounds good! Now I can align my plans on this better too ;)

jbriales commented 4 years ago

Hey @henryiii friendly ping here :)

henryiii commented 4 years ago

I’ll write it up when I get home today. :) Edit: midday/evening tomorrow, too late now.

henryiii commented 4 years ago

I've previously talked briefly with the author of argcomplete:

https://github.com/kislyuk/argcomplete/issues/116

It would be nice to be able to reuse as much as possible from argcomplete, possibly even have a general completer. Not sure how much would work for C++, since the procedure would have been different, though plumbum and other CLI toolkits for scripting languages could. I don't know how to ensure a text string is near the beginning of a binary; you probably cannot as that might be used for indexing. So that method is probably out.

How familiar are you with argcomplete? The basics there would be the same here. There would be a way to run the program to get just the completions, given a string and a location. It would predict the possible options that would make sense for that string and location. It would then return the list of completions. The hard parts are letting the system know that an executable supports completions; the trick of reading the first few bytes of a program would probably be quite hard in a compiled program.

So I think we would need:

Your thoughts?

I've worked through the beginnings of this in a branch somewhere in plumbum, quite a while ago.

jbriales commented 4 years ago

I've used argcomplete in the past, so I have some very high-level idea how it works , although I've never looked into the internal implementation. I'm not sure what's your idea of reusing argcomplete as a general completer. Are you thinking of emulating the logic there in C++ or how?

I don't know how to ensure a text string is near the beginning of a binary; you probably cannot as that might be used for indexing. So that method is probably out.

Not sure I follow your thought here. Can you provide an example of what are you pursuing here?

A general .completions(string in, int loc) method that could do the work

Where would that completions method live? As part of the Option interface? Or App? Or both (probably this)?

A way to trigger and run this. Ideally matching argcomplete to the degree reasonable.

To run the program in the completion mode we might probably add a special option or subcommand to check for? Akin to --help, say __complete?

A way to tell the shell this can be done.

To let the system know about completion I'd probably just register the foo __complete function as completion function for foo in bash: I.e. sth like calling complete -F 'foo __complete' foo (need to check the exact syntax).

So from the comments above I feel like you have definitely thought through this before in the past. I also feel like you're pursuing some kind of general abstraction/tool? To be honest I haven't a huge amount of time so I might be up for trying to just find a working solution for CLI11. I'll start by quickly checking argcomplete when I get a while to get some common perspective. Let me know if you have any suggestion/pointers to the most relevant/reusable part of the code.

A possible different approach is the one mentioned here: I.e. have a special command/option/flag again to generate some bash-completion files that you can then source to enable completion via bash.

henryiii commented 4 years ago

There are several ways to go.

Argcomplete looks in the first X bytes of a file to see if a special string is there. If it is, it then runs the file with (I think) a special environment variable (a command line option would also work, I think). There's just one argcomplete script, so if someone installed argcomplete, then any executable that has the argcomplete special string and supports the argcomplete method of requesting a completion would work without installing any other scripts.

Having a special argument that generates an autocomplete script would be a possibility too, though then there might be a bit of extra work to make sure we eventually can support bash/zsh/fish (at a minimum). It might be the most natural approach for C++. It's disappointing that you'd have to install it for each app, but might be much more useful for compiled apps being distributed than asking users to install a general tool, like argcomplete.

A one line registration would be an acceptable alternative somewhere in-between the two methods.

The basic work would be the same for the first or the third idea above, the completions method is the most important part, the rest is mostly plumbing (and it is important to look into them just a bit to make sure the completions method is flexible enough to handle them).

The second example is quite a bit different and probably would be more work, though would make a fairly nice result, as it could run quickly without having to load the executable.

If Option 3 works, that's probably what we should target as the simplest way to get this in, otherwise 1 or 2 are fine. I'm most familiar with 1. Your call. If you do 3, eventually maybe I can work on Plumbum completion as well and adapt it to option 1 so either would work.

jbriales commented 4 years ago

Sounds good!

I'd avoid using/having argcomplete as a strict requirement because this would introduce an external dependency for the completion feature. I'd rather have a solution that can be plumbed into the user's environment choice (like bash, etc.) following the usual workflow for that system. I think having multiple simple integration approaches external to the core completion feature is going to scale and maintain better.

I'd start implementing a solution and trying it with bash because, being selfish, it's what I use and because hopefully keeping the right abstract completion interface at the CLI level it should be possible to plumb with any other typical shell.

Will let you know once I have any progress/questions in here :)

henryiii commented 4 years ago

Sounds good. I use fish, so will probably will try to extend it to fish once fairly far along.

henryiii commented 4 years ago

I've given you access so you can make a branch directly in the repository, if you'd like.

elazarl commented 2 years ago

Hi @henryiii , Thanks for the great library!

Are you open for a PR that adds a complete(args, arg_index, in_arg_index) and just runs a cb giving all possible completions for the argument + its metadata?

Later it could be used as a building block for other projects to build completion lists from that, offline or online.

I don't have a clear idea, but if you think it's the right direction, I'll try to sketch a design.

hailo-oro commented 2 years ago

Hi @henryiii,

I've just implemented autocompletion in a very basic way in my forked repository here. You can try playing with it by calling the function cli11_install_completion_file(<target name>) from your CMakeLists.txt file and then installing the project. Don't forget to open another bash or simply execute the autogenerated script.

I'd like to hear what you think about it :)

trapexit commented 2 months ago

@hailo-oro I think something like rclone genautocomplete would be more friendly. It can generate and install the appropriate file for bash, fish, powershell, and zsh.

rjzak commented 1 month ago

As mentioned in the first few responses, having a runtime command which triggers CLI11 to emit the autocomplete script for the desired shell is probably easier, and this is what the Rust community has done with the clap_complete crate.

Maybe something like:

CLI::App app("foo");
std::string output_path;
enum CLI::KnownShell shell_type;
...
CLI::App* autocomplete = app.add_subcommand(...);
autocomplete->add_option("--shell", shell_type)->check(CLI::KnownShells);
autocomplete->add_option("--output", output_path);

if (*autocomplete) {
    app.emit_script(output_path, shell_type);
    return 0; // from main()
}

app has the context for all possible arguments and the expected types (values, file path, directory path, etc). Combined with the shell type, all of the data is available to create the script. The user can then place the script in the desired location. A maintainer/packager for a program using CLI11 would then include the generated scripts into the package.