File Coverage

blib/lib/App/Cmd/Command/commands.pm
Criterion Covered Total %
statement 42 111 37.8
branch 11 34 32.3
condition 0 3 0.0
subroutine 7 9 77.7
pod 3 3 100.0
total 63 160 39.3


line stmt bran cond sub pod time code
1 9     9   7074 use strict;
  9         18  
  9         381  
2 9     9   43 use warnings;
  9         14  
  9         613  
3              
4             package App::Cmd::Command::commands 0.340;
5              
6 9     9   47 use App::Cmd::Command;
  9         41  
  9         469  
7 9     9   14954 BEGIN { our @ISA = 'App::Cmd::Command' };
8              
9             # ABSTRACT: list the application's commands
10              
11             #pod =head1 DESCRIPTION
12             #pod
13             #pod This command will list all of the application commands available and their
14             #pod abstracts.
15             #pod
16             #pod =method execute
17             #pod
18             #pod This is the command's primary method and raison d'etre. It prints the
19             #pod application's usage text (if any) followed by a sorted listing of the
20             #pod application's commands and their abstracts.
21             #pod
22             #pod The commands are printed in sorted groups (created by C); each
23             #pod group is set off by blank lines.
24             #pod
25             #pod =cut
26              
27             sub opt_spec {
28             return (
29 6     6 1 52 [ 'stderr' => 'hidden' ],
30             [ 'for-completion', 'one per line, for use in tab completion scripts' ],
31             [ 'bash-completion', 'output a bash completion script for this application' ],
32             [ 'zsh-completion', 'output a zsh completion script for this application' ],
33             );
34             }
35              
36             sub execute {
37 4     4 1 12 my ($self, $opt, $args) = @_;
38              
39 4 100       14 my $target = $opt->stderr ? *STDERR : *STDOUT;
40              
41 4         52 my @cmd_groups = $self->app->command_groups;
42 4 0       10 my @primary_commands = map { @$_ if ref $_ } @cmd_groups;
  0         0  
43              
44 4 50       15 if (!@cmd_groups) {
45             @primary_commands =
46 36 100       91 grep { $_ ne 'version' or $self->app->{show_version} }
47 4         12 map { ($_->command_names)[0] }
  36         168  
48             $self->app->command_plugins;
49              
50 4         19 @cmd_groups = $self->sort_commands(@primary_commands);
51             }
52              
53 4 50       17 if ($opt->for_completion) {
54 0         0 print "$_\n" for map {; @$_ } @cmd_groups;
  0         0  
55 0         0 return;
56             }
57              
58 4 50       24 if ($opt->bash_completion) {
59 0         0 $self->_print_bash_completion(\@cmd_groups);
60 0         0 return;
61             }
62              
63 4 50       21 if ($opt->zsh_completion) {
64 0         0 $self->_print_zsh_completion(\@cmd_groups);
65 0         0 return;
66             }
67              
68 4         15 my $fmt_width = 0;
69 4 100       12 for (@primary_commands) { $fmt_width = length if length > $fmt_width }
  33         64  
70 4         9 $fmt_width += 2; # pretty
71              
72 4         9 foreach my $cmd_set (@cmd_groups) {
73 8 50       79 if (!ref $cmd_set) {
74 0         0 print { $target } "$cmd_set:\n";
  0         0  
75 0         0 next;
76             }
77 8         45 for my $command (@$cmd_set) {
78 33         743 my $abstract = $self->app->plugin_for($command)->abstract;
79 33         85 printf { $target } "%${fmt_width}s: %s\n", $command, $abstract;
  33         285  
80             }
81 8         188 print { $target } "\n";
  8         28  
82             }
83             }
84              
85             #pod =method C
86             #pod
87             #pod my @sorted = $cmd->sort_commands(@unsorted);
88             #pod
89             #pod This method orders the list of commands into groups which it returns as a list of
90             #pod arrayrefs, and optional group header strings.
91             #pod
92             #pod By default, the first group is for the "help" and "commands" commands, and all
93             #pod other commands are in the second group.
94             #pod
95             #pod This method can be overridden by implementing the C method in
96             #pod your application base clase.
97             #pod
98             #pod =cut
99              
100             sub _print_bash_completion {
101 0     0   0 my ($self, $cmd_groups) = @_;
102              
103 0 0       0 die "--bash-completion requires a version of Getopt::Long::Descriptive "
104             . "that supports shell completion generation\n"
105             unless Getopt::Long::Descriptive->can('_completion_for_bash');
106              
107 0         0 my $app = $self->app;
108 0         0 my $prog = $app->arg0;
109 0         0 (my $func = "_${prog}_complete") =~ s/\W/_/g;
110              
111 0         0 my @all_cmds = map {; @$_ } @$cmd_groups;
  0         0  
112              
113 0         0 my %cmd_completion;
114 0         0 for my $cmd (@all_cmds) {
115 0 0       0 my $plugin = $app->plugin_for($cmd) or next;
116 0         0 $cmd_completion{$cmd} =
117             Getopt::Long::Descriptive::_completion_for_bash($plugin->opt_spec);
118             }
119              
120 0         0 my $cmds_str = join q{ }, @all_cmds;
121              
122 0         0 print <<"END_HEADER";
123             $func() {
124             local cur prev words cword
125             _init_completion 2>/dev/null || {
126             cur="\${COMP_WORDS[COMP_CWORD]}"
127             prev="\${COMP_WORDS[COMP_CWORD-1]}"
128             }
129             words=("\${COMP_WORDS[\@]}")
130             cword=\$COMP_CWORD
131              
132             local cmd=""
133             local i
134             for ((i=1; i < cword; i++)); do
135             if [[ "\${words[i]}" != -* ]]; then
136             cmd="\${words[i]}"
137             break
138             fi
139             done
140              
141             if [[ -z "\$cmd" ]]; then
142             COMPREPLY=(\$(compgen -W "$cmds_str" -- "\$cur"))
143             return
144             fi
145              
146             case "\$cmd" in
147             END_HEADER
148              
149 0         0 for my $cmd (sort keys %cmd_completion) {
150 0         0 my $completion = $cmd_completion{$cmd};
151 0         0 my $flags_str = $completion->{flags};
152              
153 0 0 0     0 next unless $flags_str || @{ $completion->{prev_cases} };
  0         0  
154              
155 0         0 print " $cmd)\n";
156              
157 0 0       0 if (@{ $completion->{prev_cases} }) {
  0         0  
158 0         0 print " case \"\$prev\" in\n";
159 0         0 for my $case (@{ $completion->{prev_cases} }) {
  0         0  
160 0         0 print " $case->{pattern})\n";
161 0         0 print " $case->{action}\n";
162 0         0 print " return ;;\n";
163             }
164 0         0 print " esac\n";
165             }
166              
167 0         0 print " COMPREPLY=(\$(compgen -W \"$flags_str\" -- \"\$cur\"))\n";
168 0         0 print " ;;\n";
169             }
170              
171 0         0 print <<"END_FOOTER";
172             *)
173             COMPREPLY=()
174             ;;
175             esac
176             }
177             complete -F $func $prog
178             END_FOOTER
179             }
180              
181             sub _print_zsh_completion {
182 0     0   0 my ($self, $cmd_groups) = @_;
183              
184 0 0       0 die "--zsh-completion requires a version of Getopt::Long::Descriptive "
185             . "that supports shell completion generation\n"
186             unless Getopt::Long::Descriptive->can('_completion_for_zsh');
187              
188 0         0 my $app = $self->app;
189 0         0 my $prog = $app->arg0;
190 0         0 (my $func = "_${prog}_complete") =~ s/\W/_/g;
191              
192 0         0 my @all_cmds = map {; @$_ } @$cmd_groups;
  0         0  
193              
194 0         0 my @cmd_descs;
195             my %cmd_zsh_args;
196 0         0 for my $cmd (@all_cmds) {
197 0 0       0 my $plugin = $app->plugin_for($cmd) or next;
198 0         0 (my $abstract = $plugin->abstract) =~ s/'/'\\''/g;
199 0         0 push @cmd_descs, " '$cmd:$abstract'";
200 0         0 $cmd_zsh_args{$cmd} = [ Getopt::Long::Descriptive::_completion_for_zsh($plugin->opt_spec) ];
201             }
202              
203 0         0 my $cmd_list = join "\n", @cmd_descs;
204              
205 0         0 print <<"END_HEADER";
206             #compdef $prog
207              
208             $func() {
209             local curcontext="\$curcontext" state line
210             typeset -A opt_args
211              
212             _arguments -C \\
213             '1: :->command' \\
214             '*:: :->args'
215              
216             case \$state in
217             command)
218             local -a _cmds
219             _cmds=(
220             $cmd_list
221             )
222             _describe 'command' _cmds
223             ;;
224             args)
225             case \$line[1] in
226             END_HEADER
227              
228 0         0 for my $cmd (sort keys %cmd_zsh_args) {
229 0         0 my @args = @{ $cmd_zsh_args{$cmd} };
  0         0  
230 0         0 print " $cmd)\n";
231 0 0       0 if (@args) {
232 0         0 print " _arguments \\\n";
233 0         0 for my $i (0 .. $#args) {
234 0 0       0 my $cont = $i < $#args ? ' \\' : '';
235 0         0 print " $args[$i]$cont\n";
236             }
237             } else {
238 0         0 print " _arguments\n";
239             }
240 0         0 print " ;;\n";
241             }
242              
243 0         0 print <<"END_FOOTER";
244             *)
245             ;;
246             esac
247             ;;
248             esac
249             }
250             $func "\$\@"
251             END_FOOTER
252             }
253              
254             sub sort_commands {
255 4     4 1 16 my ($self, @commands) = @_;
256              
257 4         16 my $float = qr/^(?:help|commands)$/;
258              
259 4         10 my @head = sort grep { $_ =~ $float } @commands;
  33         112  
260 4         10 my @tail = sort grep { $_ !~ $float } @commands;
  33         96  
261              
262 4         21 return (\@head, \@tail);
263             }
264              
265             1;
266              
267             __END__