pericmd 036: More on tab completion (7): –config-profile

Back to tab completion on today’s post. In this post I want to show you how the completion of --config-profile works. I think this is a nice feature of Perinci::CmdLine and studying the code highlights the mechanism of the tab completion routine.

What is –config-profile for?

As already discussed in a previous blog post, a configuration file can consist of several profiles (or perhaps “scenarios”) which can be selected by the user via command-line option. For example:

# in ~/.config/myapp.conf


# in /etc/myapp.conf


So if you run myapp --config-profile=p10 you’ll get (foo=11, bar=12, baz=3). If you run myapp --config-profile=p20 you’ll get (foo=21, bar=22, baz=3). And if you run without using any profile: myapp you’ll get (foo=1, bar=2, baz=3).

There is completion available for --config-profile:

% myapp --config-profile <tab><tab>
p10     p20

How the completion routine works

In Perinci/CmdLine/, in the definition of common options (%copts), we have the following code:

    config_profile => {
        getopt  => 'config-profile=s',
        summary => 'Set configuration profile to use',
        handler => sub {
            my ($go, $val, $r) = @_;
            $r->{config_profile} = $val;
        completion => sub {
            # return list of profiles in read config file

            my %args = @_;
            my $word    = $args{word} // '';
            my $cmdline = $args{cmdline};
            my $r       = $args{r};

            # we are not called from cmdline, bail (actually we might want to
            # return list of programs anyway, but we want to read the value of
            # bash_global_dir et al)
            return undef unless $cmdline;

            # since this is common option, at this point we haven't parsed
            # argument or even read config file. so we need to do that first.
                # this is not activated yet
                $r->{read_config} = 1;

                my $res = $cmdline->parse_argv($r);
                #return undef unless $res->[0] == 200;

            # we are not reading any config file, return empty list
            return [] unless $r->{config};

            my @profiles;
            for (keys %{$r->{config}}) {
                if (length $r->{subcommand_name}) {
                    push @profiles, $1
                        if /\A\Q$r->{subcommand_name}\E \s+ profile=(.+)/x;
                } else {
                    push @profiles, $1 if /\Aprofile=(.+)/;

            require Complete::Util;
                array=>[sort @profiles], word=>$word, ci=>1);

Below is the description of how the completion routine works. Lines 210-212 takes in the arguments. Aside from the familiar word, we also have cmdline and r. cmdline contains the Perinci::CmdLine object and is passed to the completion routine so the routine can access the attributes of the command-line application, as well as know that it is run in the context of CLI (completion routine can also be run under different context, like GUI or HTTP over AJAX for autocompletion of form fields). r is a hash structure that contains per-request data, and is explained in the POD documentation of Perinci::CmdLine::Base, for example: format (the output format chosen by user), action (the action chosen, like “help” when program should display help message and exit, “version” to display version and exit, or “call” to execute Riap function, which is the main action), and so on.

r also contains information about the configuration paths and content. Lines 221-227 force the reading of config files via calling the parse_argv() method. After this, if we are reading any config files, the configuration data will be in $r->{config} and we just need to grab all the available profiles (lines 232-240).

Finally we call the usual complete_array_elem to filter the list of profile names with word and return the final answer (line 242-244).


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 )

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s