#!perl

our $DATE = '2014-12-03'; # DATE
our $VERSION = '0.04'; # VERSION
# FRAGMENT id=bash-completion-prog-nohint

use 5.010;
use strict;
use warnings;

use App::BashCompletionProg;
use Perinci::CmdLine::Any -prefer_lite=>1;

our %SPEC;

sub _all_exec_in_PATH {
    my @res;
    for my $dir (split /:/, $ENV{PATH}) {
        opendir my($dh), $dir or next;
        for my $f (readdir $dh) {
            next if $f eq '.' || $f eq '..';
            next if $f =~ /~\z/; # skip backup files
            next unless ((-f "$dir/$f") && (-x _));
            push @res, "$dir/$f";
        }
    }
    \@res;
}

$SPEC{prog} = {
    v => 1.1,
    summary => 'Add appropriate bash completion command for a program',
    args => {
        file => {
            summary => 'Specify alternate location for bash-completion-prog file',
            description => <<'_',

The default is `/etc/bash-completion-prog` (if running as root), or
`~/.bash-completion-prog` (if running as normal user).

_
            schema  => 'str*',
        },
        action => {
            schema => 'str*',
            cmdline_aliases => {
                list       => { is_flag=>1, code=>sub { $_[0]{action} = 'list' } },
                l          => { is_flag=>1, code=>sub { $_[0]{action} = 'list' } },
                clean      => { is_flag=>1, code=>sub { $_[0]{action} = 'clean' } },
                remove     => { is_flag=>1, code=>sub { $_[0]{action} = 'remove' } },
                remove_all => { is_flag=>1, code=>sub { $_[0]{action} = 'remove_all' } },
            },
            default => 'add',
        },
        detail => {
            schema => 'bool*',
        },
        replace => {
            schema => 'bool*',
        },
       args => {
            schema => ['array*' => of => 'str*'],
            pos    => 0,
            greedy => 1,
        },
    },
};
sub prog {
    my %args = @_;

    my $action = $args{action};
    my $args   = $args{args};

    if ($action eq 'add') {
        $args = _all_exec_in_PATH() unless $args;
        return App::BashCompletionProg::_add(
            file    => $args{file},
            progs   => $args,
            replace => $args{replace},
        );
    } elsif ($action eq 'list') {
        return App::BashCompletionProg::_list(
            file   => $args{file},
            detail => $args{detail},
        );
    } elsif ($action eq 'clean') {
        return App::BashCompletionProg::_clean(
            file     => $args{file},
        );
    } elsif ($action eq 'remove') {
        return [400, "Please specify one or more programs"] unless $args;
        return App::BashCompletionProg::_remove(
            file   => $args{file},
            progs  => $args,
        );
    } elsif ($action eq 'remove_all') {
        return App::BashCompletionProg::_remove(
            file     => $args{file},
            criteria => sub {1},
        );
    }
}

Perinci::CmdLine::Any->new(
    url => '/main/prog',
    log => 1,
)->run;

# ABSTRACT: Add appropriate bash completion command for a program
# PODNAME: bash-completion-prog

__END__

=pod

=encoding UTF-8

=head1 NAME

bash-completion-prog - Add appropriate bash completion command for a program

=head1 VERSION

This document describes version 0.04 of bash-completion-prog (from Perl distribution App-BashCompletionProg), released on 2014-12-03.

=head1 SYNOPSIS

Detect completion for all programs found in C<PATH>:

 % bash-completion-prog

Detect completion for some programs (replace if already listed before):

 % bash-completion-prog --replace prog1 prog2 ./bin/prog3

List completions:

 % bash-completion-prog --list

Remove some programs:

 % bash-completion-prog --remove prog1 prog2

Remove all:

 % bash-completion-prog --remove-all

=head1 DESCRIPTION

B<bash-completion-prog> is a script to make it easy to add completion for
programs. It tries to detect programs/scripts in PATH and add C<complete> bash
commands for programs that it recognizes. Currently it recognizes:

=over

=item * Scripts which are tagged with hints of what completion program to use

You can put this in a script, e.g. in a script called C<foo>:

 # FRAGMENT id=bash-completion-prog-hints command=bar

The above line tells C<bash-completion-prog> that the script should be completed
using an external program called C<bar>. This will add this line to
C</etc/bash-completion-prog> (or C<~/.bash-completion-prog>):

 complete -C bar foo # FRAGMENT id=foo

Ignore the C<# FRAGMENT ...> or C<# BEGIN FRAGMENT ... # END FRAGMENT> part, it
is just used to aid in adding or removing the fragment.

=item * Getopt::Long::Complete-based CLI scripts

If a script like C<foo> is detected as a Perl script using
L<Getopt::Long::Complete>, we know that it can complete itself. Thus,
C<bash-completion-prog> will add this line:

 complete -C foo foo # FRAGMENT id=foo

=item * Completion scripts which are tagged with hints of what scripts they complete

You can create a completion script in Perl (or other language, actually), e.g.
C<_foo> and tag it with hints of what scripts they complete, e.g.

 # FRAGMENT id=bash-completion-prog-hints completer=1 for=foo,foo-this-host

This will add this entry to C<bash-completion-prog> file:

 # BEGIN FRAGMENT id=_foo
 complete -C _foo foo
 complete -C _foo foo-this-host
 # END FRAGMENT id=_foo

For an example of completer scripts, see C<App::BashCompleter::*> modules like
C<App::BashCompleter::cpanm>.

=item * Perinci::CmdLine-based CLI scripts

If a script like C<foo> is detected as a Perl script using L<Perinci::CmdLine>
(or its variant like L<Perinci::CmdLine::Lite> or L<Perinci::CmdLine::Any>) we
know that it can complete itself. Thus, C<bash-completion-prog> will add this
line:

 complete -C foo foo # FRAGMENT id=foo

Note: this is the original reason why C<bash-completion-prog> came into being:
to easily add completion for Perinci::CmdLine-based scripts, which I write a
lot.

=item * Other methods

Other methods will be added in the future, e.g. using C<.
/etc/bash_completion.d/foo> if it exists, etc.

=back

=head2 Installation

To install system-wide:

 % touch /etc/bash-completion-prog

and put this line (notice the dot):

 . /etc/bash-completion-prog

in C</etc/bash.bashrc> or C</etc/profile>.

To install for a single user:

 % touch ~/.bash-completion-prog

and put this line (notice the dot):

 . ~/.bash-completion-prog

in C<~/.bashrc> or C<~/.bash_profile>.

=head1 Usage

The simplest usage would be to call the program without any argument, which will
scan all programs found in C<PATH> and add completion for all recognizable
commands. This will fill C</etc/bash-completion-prog> (if you're root) or
C<~/.bash-completion-prog> (if you're a normal user):

 % bash-completion-prog

Or you can add individual programs. Program names without directory (without
C</>) will be searched in C<PATH>:

 % bash-completion-prog foo ./bar/baz

If a program's completion cannot be determined, it will simply be ignored. If a
program's completion command is already listed, it will also be ignored, unless
C<--replace> is specified.

=head1 OPTIONS

=head2 --file, -f

Specify an alternate location for the C<bash-completion-prog> file. By default
it's C</etc/bash-completion-prog> for root, or C<~/.bash-completion-prog> for
normal users.

=head2 --list, -l

List all programs which are listed in the C<bash-completion-prog> file.

=head2 --remove

Remove all programs in C<bash-completion-prog> file that are not in C<PATH>.

=head2 --remove

Remove specified programs from C<bash-completion-prog> file.

=head2 --remove-all

Remove all entries in C<bash-completion-prog> file.

=head2 --replace

When adding a program that already exists in C<bash-completion-prog> file,
replace it without warning. The default behavior is to ignore the program.

=head1 ENVIRONMENT

=head2 DEBUG => bool

Set to true to enable debugging messages.

=head1 TODO

C<--disable> and C<--enable> options.

=head1 SEE ALSO

B<bash-completion> package, http://bash-completion.alioth.debian.org/

L<Dist::Zilla::Plugin::Rinci::InstallCompletion>

=head1 COMPLETION

This script has shell tab completion capability with support for several shells.

=head2 bash

To activate bash completion for this script, put:

 complete -C bash-completion-prog bash-completion-prog

in your bash startup (e.g. C<~/.bashrc>). Your next shell session will then recognize tab completion for the command. Or, you can also directly execute the line above in your shell to activate immediately.

You can also install L<App::BashCompletionProg> which makes it easy to add completion for Perinci::CmdLine-based scripts. After you install the module and put C<. ~/.bash-complete-prog> (or C<. /etc/bash-complete-prog>), you can just run C<bash-completion-prog> and the C<complete> command will be added to your C<~/.bash-completion-prog>. Your next shell session will then recognize tab completion for the command.

=head2 fish

To activate fish completion for this script, execute:

 begin; set -lx COMP_SHELL fish; set -lx COMP_MODE gen_command; bash-completion-prog; end > $HOME/.config/fish/completions/bash-completion-prog.fish

Or if you want to install globally, you can instead write the generated script to C</etc/fish/completions/bash-completion-prog.fish> or C</usr/share/fish/completions/bash-completion-prog.fish>. The exact path might be different on your system. Please check your C<fish_complete_path> variable.

=head2 tcsh

To activate tcsh completion for this script, put:

 complete bash-completion-prog 'p/*/`bash-completion-prog`/'

in your tcsh startup (e.g. C<~/.tcshrc>). Your next shell session will then recognize tab completion for the command. Or, you can also directly execute the line above in your shell to activate immediately.

=head2 zsh

To activate zsh completion for this script, put:

 _bash_completion_prog() { read -l; local cl="$REPLY"; read -ln; local cp="$REPLY"; reply=(`COMP_SHELL=zsh COMP_LINE="$cl" COMP_POINT="$cp" bash-completion-prog`) }

 compctl -K _bash_completion_prog bash-completion-prog

in your zsh startup (e.g. C<~/.zshrc>). Your next shell session will then recognize tab completion for the command. Or, you can also directly execute the line above in your shell to activate immediately.

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/App-BashCompletionProg>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-App-BashCompletionProg>.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-BashCompletionProg>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
