Perinci::CmdLine gives you tab completion feature for free, meaning you don’t have to do anything to make it work (in fact, it’s more effort to enable it in your shell — but there are tools for that). Thanks to the rich Rinci metadata, many of the information required to do tab completion is already provided.
This post assumes you’re using bash. I will cover other shells in the future.
Let’s return to our example hello script. If you put the script somewhere in your PATH (for example in /usr/local/bin, if your PATH environment variable includes it, or perhaps in ~/bin) and type:
% complete -C hello hello
then you can already do the following:
% hello <tab><tab> -\? .gitignore --json --name --salutation --config-path -h lcpan.log --no-config scratch.txt --config-profile hello lcpan.stats pause/ scratch.txt~ --format hello~ -n perl-App-hello/ -v .git/ --help --naked-res -s --version
What is that? The completion is giving you a list of all possible options as well as files in the current directory. If you type:
% hello -<tab><tab> -\? --format --json --name --salutation --config-path -h -n --no-config -v --config-profile --help --naked-res -s --version
It will give the list of options only. It can even take advantage of schema information to complete option values:
% hello -s <tab><tab> Miss Mr Mrs
since the schema for the salutation argument says:
['str*', in=>[qw/Mr Mrs Miss/]]
Of course, you can help provide hints so completion works even better. This is done by adding these properties: completion or element_completion to your argument specification. Since tab completion is very useful and very convenient for users, there will be more posts with emphasis on completion in this series. Stay tuned.
How completion works
In bash, completion can be handled by a shell function or an external command. When using an external command, bash will set environment variables COMP_LINE (the raw command line string) and COMP_POINT (an integer pointing to the location of cursor in the command-line string) and then run the command. The command is supposed to return a list of possible completions by outputing them one-per-line to STDOUT.
When you call the run() method, Perinci::CmdLine detects the above environment variables and enter into completion mode which then tries to find the appropriate completion, print them to STDOUT, and exit the program.
Therefore, Perinci::CmdLine-based scripts can “complete themselves”. You tell bash via complete -C hello hello that to complete the hello command, the hello command is used.
Now how does Perinci::CmdLine determine the completion? First, it parses COMP_LINE and COMP_POINT into a list of words, and which word the cursor is at. It then parses the resulting words like it were @ARGV, trying to figure out the command-line options and command-line arguments. If the cursor word happens to be a command-line option, it will try to complete from the list of known options. If the cursor word falls after the command-line option, it will try to complete the option value by using several means (the abovementioned completion routine, or information from the schema), and so on. Finally it just prints the result into STDOUT.