pericmd 022: Tab completion

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.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s