File Coverage

lib/Devel/Trepan/CmdProcessor/Command/Help.pm
Criterion Covered Total %
statement 153 405 37.7
branch 11 112 9.8
condition 0 36 0.0
subroutine 41 57 71.9
pod 0 22 0.0
total 205 632 32.4


line stmt bran cond sub pod time code
1             # Copyright (C) 2011-2012, 2014-2015 Rocky Bernstein <rocky@cpan.org>
2             # -*- coding: utf-8 -*-
3              
4 12     12   88 use rlib '../../../..';
  12     1   27  
  12         65  
  1         7  
  1         2  
  1         6  
5              
6             package Devel::Trepan::CmdProcessor::Command::Help;
7 12     12   4358 use warnings; no warnings 'redefine'; use utf8;
  12     12   26  
  12     12   332  
  12     1   52  
  12     1   24  
  12     1   349  
  12         57  
  12         21  
  12         59  
  1         358  
  1         2  
  1         35  
  1         5  
  1         2  
  1         21  
  1         4  
  1         2  
  1         5  
8              
9 12     12   5706 use Devel::Trepan::Pod2Text qw(pod2string help2podstring);
  12     1   24820  
  12         773  
  1         54  
  1         2  
  1         60  
10 12     12   83 use Devel::Trepan::Complete qw(complete_token);
  12     1   24  
  12         630  
  1         5  
  1         2  
  1         48  
11              
12 12     12   67 use if !@ISA, Devel::Trepan::CmdProcessor::Command ;
  12     1   25  
  12         63  
  1         18  
  1         3  
  1         11  
13 12     12   1513 use strict;
  12     1   25  
  12         286  
  1         38  
  1         2  
  1         20  
14              
15 12     12   56 use vars qw(@ISA);
  12     1   22  
  12         956  
  1         4  
  1         1  
  1         84  
16             unless (@ISA) {
17 12     12   82 eval <<'EOE';
  12     12   28  
  12     12   1264  
  12     12   84  
  12     12   24  
  12     12   645  
  12         89  
  12         28  
  12         604  
  12         96  
  12         37  
  12         569  
  12         70  
  12         22  
  12         544  
  12         71  
  12         22  
  12         508  
18             use constant ALIASES => ('?', 'h');
19             use constant CATEGORY => 'support';
20             use constant SHORT_HELP => 'Print commands or give help for command(s)';
21             use constant MIN_ARGS => 0; # Need at least this many
22             use constant MAX_ARGS => undef; # Need at most this many - undef -> unlimited.
23             use constant NEED_STACK => 0;
24             EOE
25             }
26              
27             @ISA = @CMD_ISA;
28 12     12   75 use vars @CMD_VARS; # Value inherited from parent
  12     1   22  
  12         1687  
  1         5  
  1         3  
  1         154  
29              
30             our $NAME = set_name();
31             =pod
32              
33             =head2 Synopsis:
34              
35             =cut
36             our $HELP = <<'HELP';
37             =pod
38              
39             B<help> [I<command> [I<subcommand>]|I<expression>]
40              
41             Without argument, print the list of available debugger commands. a
42             When an argument is given, it is first checked to see if it is command
43             name. For example, C<help backtrace> gives help on the
44             L<C<backtrace>|Devel::Trepan::CmdProcessor::Command::Backtrace>
45             debugger command.
46              
47             Some commands like
48             L<C<info>|Devel::Trepan::CmdProcessor::Command::Info>,
49             L<C<set>|Devel::Trepan::CmdProcessor::Command::Set>, and
50             L<C<show>|Devel::Trepan::CmdProcessor::Command::Show> can accept an
51             additional subcommand to give help just about that particular
52             subcommand. For example C<help info line> gives help about the C<info
53             line> command.
54              
55             =cut
56             HELP
57              
58             BEGIN {
59 12 50   12   80 eval "use constant CATEGORIES => {
  12 50   12   1425  
  12     1   26  
  12         911  
  1         6  
60             'breakpoints' => 'Making the program stop at certain points',
61             'data' => 'Examining data',
62             'files' => 'Specifying and examining files',
63             'running' => 'Running the program',
64             'status' => 'Status inquiries',
65             'support' => 'Support facilities',
66             'stack' => 'Examining the call stack',
67             'syntax' => 'Debugger command syntax'
68             };" unless declared('CATEGORIES');
69             };
70              
71 12     12   71 use File::Basename;
  12     1   33  
  12         732  
  1         37  
  1         2  
  1         49  
72 12     12   73 use File::Spec;
  12     1   23  
  12         51035  
  1         5  
  1         2  
  1         2295  
73             my $ROOT_DIR = dirname(__FILE__);
74             my $HELP_DIR = File::Spec->catfile($ROOT_DIR, 'Help');
75              
76             sub command_names($)
77             {
78 6     6 0 16 my ($self) = @_;
  0     0 0 0  
79 6         13 my $proc = $self->{proc};
  0         0  
80 6         12 my %cmd_hash = %{$proc->{commands}};
  6         119  
  0         0  
  0         0  
81 6         43 my @commands = keys %cmd_hash;
  0         0  
82 6 50       21 if ($proc->{terminated}) {
  0 0       0  
83 0         0 my @filtered_commands=();
  0         0  
84 0         0 for my $cmd (@commands) {
  0         0  
85 0 0       0 push @filtered_commands, $cmd unless $cmd_hash{$cmd}->NEED_STACK;
  0 0       0  
86             }
87 0         0 return @filtered_commands;
  0         0  
88             } else {
89 6         75 return @commands;
  0         0  
90             }
91             }
92              
93             sub complete($$)
94             {
95 5     5 0 14 my ($self, $prefix) = @_;
  0     0 0 0  
96 5         10 my $proc = $self->{proc};
  0         0  
97 5         9 my @candidates = (keys %{CATEGORIES()}, qw(* all),
  5         43  
  0         0  
  0         0  
98             $self->command_names());
99 5         31 my @matches = complete_token(\@candidates, $prefix);
  0         0  
100             # my @aliases =
101             # Devel::Trepan::Complete::complete_token_filtered($proc->{aliases},
102             # $prefix, \@matches);
103             # sort (@matches, @aliases);
104 5         22 sort @matches;
  0         0  
105             }
106              
107             sub complete_syntax($$) {
108 1     1 0 3 my ($self, $prefix) = @_;
  0     0 0 0  
109 1         3 my @candidates = @{$self->syntax_files()};
  1         3  
  0         0  
  0         0  
110 1         4 my @matches = complete_token(\@candidates, $prefix);
  0         0  
111 1         7 sort @matches;
  0         0  
112             }
113              
114             sub complete_token_with_next($$;$)
115             {
116 5     5 0 16 my ($self, $prefix, $cmd_prefix) = @_;
  0     0 0 0  
117 5         14 my $proc = $self->{proc};
  0         0  
118 5         11 my @result = ();
  0         0  
119 5         17 my @matches = $self->complete($prefix);
  0         0  
120 5         13 foreach my $cmd (@matches) {
  0         0  
121 5         11 my %commands = %{$proc->{commands}};
  5         68  
  0         0  
  0         0  
122 5 100       25 if (exists $commands{$cmd}) {
  0 50       0  
    0          
    0          
123 4         22 push @result, [$cmd, $commands{$cmd}];
  0         0  
124             } elsif ('syntax' eq $cmd) {
125 1         2 my @syntax_files = @{$self->syntax_files()};
  1         4  
  0         0  
  0         0  
126             push @result, [$cmd,
127 1     1   2 sub { my $prefix = shift;
  0     0   0  
128 1         20 $self->complete_syntax($prefix) } ];
  1         4  
  0         0  
  0         0  
129             } else {
130 0         0 push @result, [$cmd, ['*'] ];
  0         0  
131             }
132             }
133 5         20 return @result;
  0         0  
134             }
135              
136             # List the command categories and a short description of each.
137             sub list_categories($) {
138 0     0 0 0 my $self = shift;
  0     0 0 0  
139 0         0 $self->section('Help classes:');
  0         0  
140 0         0 for my $cat (sort(keys %{CATEGORIES()})) {
  0         0  
  0         0  
  0         0  
141 0         0 $self->msg(sprintf "%-13s -- %s", $cat, CATEGORIES->{$cat});
  0         0  
142             }
143 0         0 my $final_msg = '
  0         0  
144             Type "help" followed by a class name for a list of help items in that class.
145             Type "help aliases" for a list of current aliases.
146             Type "help macros" for a list of current macros.
147             Type "help *" for the list of all commands, macros and aliases.
148             Type "help all" for a brief description of all commands.
149             Type "help REGEXP" for the list of commands matching /^${REGEXP}/.
150             Type "help CLASS *" for the list of all commands in class CLASS.
151             Type "help" followed by a command name for full documentation.
152             ';
153 0         0 $self->msg($final_msg);
  0         0  
154             }
155              
156             sub show_aliases($)
157             {
158 1     1 0 3 my $self = shift;
  0     0 0 0  
159 1         4 $self->section('All alias names:');
  0         0  
160 1         16 my @aliases = sort(keys(%{$self->{proc}{aliases}}));
  1         34  
  0         0  
  0         0  
161 1         8 $self->msg($self->columnize_commands(\@aliases));
  0         0  
162             }
163              
164             # Show short help for all commands in `category'.
165             sub show_category($$$)
166             {
167 0     0 0 0 my ($self, $category, $args) = @_;
  0     0 0 0  
168 0 0 0     0 if (scalar @$args == 1 && $args->[0] eq '*') {
  0 0 0     0  
169 0         0 $self->section("Commands in class $category:");
  0         0  
170 0         0 my @commands = ();
  0         0  
171 0         0 while (my ($key, $value) = each(%{$self->{proc}{commands}})) {
  0         0  
  0         0  
  0         0  
172 0 0       0 push(@commands, $key) if $value->Category eq $category;
  0 0       0  
173             }
174 0         0 $self->msg($self->columnize_commands([sort @commands]));
  0         0  
175 0         0 return;
  0         0  
176             }
177              
178 0         0 $self->section("Command class: ${category}");
  0         0  
179 0         0 my %commands = %{$self->{proc}{commands}};
  0         0  
  0         0  
  0         0  
180 0         0 for my $name (sort keys %commands) {
  0         0  
181 0 0       0 next if $category ne $commands{$name}->Category;
  0 0       0  
182             my $short_help = defined $commands{$name}{short_help} ?
183 0 0       0 $commands{$name}{short_help} : $commands{$name}->short_help;
  0 0       0  
184 0         0 my $msg = sprintf("%-13s -- %s", $name, $short_help);
  0         0  
185 0         0 $self->msg($msg);
  0         0  
186             }
187             }
188              
189             sub syntax_files($)
190             {
191 2     2 0 5 my $self = shift;
  0     0 0 0  
192 2 100       8 return $self->{syntax_files} if $self->{syntax_files};
  0 0       0  
193 1         169 my @pods = glob(File::Spec->catfile($HELP_DIR, "/*.pod"));
  0         0  
194 1         5 my @result = map({ $_ = basename($_, '.pod') } @pods);
  6         213  
  0         0  
  0         0  
195 1         4 $self->{syntax_files} = \@result;
  0         0  
196 1         4 return \@result;
  0         0  
197             }
198              
199             sub show_command_syntax($$)
200             {
201 0     0 0 0 my ($self, $args) = @_;
  0     0 0 0  
202 0 0       0 if (scalar @$args == 2) {
  0 0       0  
203 0   0     0 $self->{syntax_summary_help} ||= {};
  0   0     0  
204 0         0 $self->section("List of syntax help");
  0         0  
205 0         0 for my $name (@{$self->syntax_files()}) {
  0         0  
  0         0  
  0         0  
206 0 0       0 unless($self->{syntax_summary_help}{$name}) {
  0 0       0  
207 0         0 my $filename = File::Spec->catfile($HELP_DIR, "${name}.pod");
  0         0  
208 0         0 my @lines = $self->readlines($filename);
  0         0  
209 0         0 my $summary_help = $lines[0];
  0         0  
210 0         0 $summary_help =~ s/^#\s*//;
  0         0  
211 0         0 $self->{syntax_summary_help}{$name} = $summary_help;
  0         0  
212             }
213             my $msg = sprintf(" %-8s -- %s", $name,
214 0         0 $self->{syntax_summary_help}{$name});
  0         0  
215 0         0 $self->msg($msg, {unlimited => 1});
  0         0  
216             }
217             } else {
218 0         0 my @args = splice(@{$args}, 2);
  0         0  
  0         0  
  0         0  
219 0         0 for my $name (@args) {
  0         0  
220 0   0     0 $self->{syntax_help} ||= {};
  0   0     0  
221 0         0 my $filename = File::Spec->catfile($HELP_DIR, "${name}.pod");
  0         0  
222 0 0       0 if ( -r $filename) {
  0 0       0  
223 0         0 my $proc = $self->{proc};
  0         0  
224             my $text = pod2string($filename,
225             $proc->{settings}{highlight},
226 0         0 $proc->{settings}{maxwidth});
  0         0  
227 0         0 $self->msg($text);
  0         0  
228             } else {
229 0         0 $self->errmsg("No syntax help for ${name}");
  0         0  
230             }
231             }
232             }
233             }
234              
235             # This method runs the command
236             sub run($$)
237             {
238 1     1 0 3 my ($self, $args) = @_;
  0     0 0    
239 1         3 my $proc = $self->{proc};
  0            
240 1         4 my $cmd_name = $args->[1];
  0            
241 1 50       9 if (scalar(@$args) > 1) {
  0 0          
242 1         2 my $real_name;
  0            
243 1 50 0     4 if ($cmd_name eq '*') {
  0 0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
244 1         12 $self->section('All currently valid command names:');
  0            
245 1         28 my @cmds = sort($self->command_names());
  0            
246 1         11 $self->msg($self->columnize_commands(\@cmds));
  0            
247 1 50       17 if (scalar keys %{$proc->{aliases}}) {
  1 0       6  
  0            
  0            
248 1         5 $self->msg('');
  0            
249 1         17 show_aliases($self)
  0            
250             }
251             # $self->show_macros unless scalar @$self->{proc}->macros;
252             } elsif ($cmd_name =~ /^aliases$/i) {
253 0           show_aliases($self);
  0            
254             # } elsif (cmd_name =~ /^macros$/i) {
255             # $self->show_macros;
256             } elsif ($cmd_name =~ /^syntax$/i) {
257 0           show_command_syntax($self, $args);
  0            
258             } elsif ($cmd_name =~ /^all$/i) {
259 0           for my $category (sort keys %{CATEGORIES()}) {
  0            
  0            
  0            
260 0           show_category($self, $category, []);
  0            
261 0           $self->msg('');
  0            
262             }
263             } elsif (CATEGORIES->{$cmd_name}) {
264 0           splice(@$args,0,2);
  0            
265 0           show_category($self, $cmd_name, $args);
  0            
266             } elsif ($proc->{commands}{$cmd_name}
267             || $proc->{aliases}{$cmd_name}) {
268 0 0         if ($proc->{commands}{$cmd_name}) {
  0 0          
269 0           $real_name = $cmd_name;
  0            
270             } else {
271 0           $real_name = $proc->{aliases}{$cmd_name};
  0            
272             }
273 0           my $cmd_obj = $proc->{commands}{$real_name};
  0            
274             my $help_text =
275             $cmd_obj->can('help') ? $cmd_obj->help($args)
276 0 0         : $cmd_obj->{help};
  0 0          
277 0 0         if ($help_text) {
  0 0          
278             $help_text = help2podstring($help_text,
279             $proc->{settings}{highlight},
280 0           $proc->{settings}{maxwidth});
  0            
281 0           chomp $help_text; chomp $help_text;
  0            
  0            
  0            
282 0           $self->msg($help_text) ;
  0            
283 0           my $aliases_ref = $cmd_obj->{aliases};
  0            
284 0 0 0       if ($aliases_ref && scalar @{$aliases_ref} && $args && scalar @$args == 2) {
  0 0 0        
  0   0        
  0   0        
      0        
      0        
285 0           $self->section("\n Aliases:");
  0            
286 0           $self->msg($self->columnize_commands($cmd_obj->{aliases}));
  0            
287             }
288             }
289             # } elsif ($self->{proc}{macros}{$cmd_name}) {
290             # $self->msg("${cmd_name} is a macro which expands to:");
291             # $self->msg(" ${@proc.macros[cmd_name]}", {:unlimited => true});
292             } else {
293 0           my @command_names = $self->command_names();
  0            
294 0           my @matches = sort grep(/^${cmd_name}/, @command_names );
  0            
295 0 0         if (!scalar @matches) {
  0 0          
    0          
    0          
296 0           $self->errmsg("No commands found matching /^${cmd_name}/. Try \"help\".");
  0            
297             } elsif (scalar @matches == 1) {
298 0           $self->msg("Pattern '${cmd_name}' matches command ${matches[0]}...");
  0            
299 0           $args->[1] = $matches[0];
  0            
300 0           $self->run($args);
  0            
301             } else {
302 0           $self->section("Command names matching /^${cmd_name}/:");
  0            
303 0           $self->msg($self->columnize_commands(sort \@matches));
  0            
304             }
305             }
306             } else {
307 0           list_categories($self);
  0            
308             }
309             }
310              
311             sub readlines($$$) {
312 0     0 0   my($self, $filename) = @_;
  0     0 0    
313 0 0         unless (open(FH, $filename)) {
  0 0          
314 0           $self->errmsg("Can't open $filename: $!");
  0            
315 0           return ();
  0            
316             }
317 0           local $_;
  0            
318 0           my @lines = ();
  0            
319 0           while (<FH>) { chomp $_; push @lines, $_; }
  0            
  0            
  0            
  0            
  0            
320 0           close FH;
  0            
321 0           return @lines;
  0            
322             }
323              
324             # sub show_macros
325             # section 'All macro names:'
326             # msg columnize_commands(@proc.macros.keys.sort)
327             # }
328              
329             # }
330              
331             # Demo it.
332             unless (caller) {
333             require Devel::Trepan::CmdProcessor;
334             my $proc = Devel::Trepan::CmdProcessor->new;
335             my $help_cmd = __PACKAGE__->new($proc);
336             my $sep = '=' x 30 . "\n";
337             print join(', ', $help_cmd->complete('br')), "\n";
338             print join(', ', $help_cmd->complete('un')), "\n";
339             print join(', ', $help_cmd->complete("sy")), "\n";
340             $help_cmd->list_categories();
341             print $sep;
342             $help_cmd->run([$NAME, 'help']);
343             print $sep;
344             $help_cmd->run([$NAME, 'kill']);
345             print $sep;
346             $help_cmd->run([$NAME, '*']);
347             print $sep;
348             $help_cmd->run([$NAME]);
349             print $sep;
350             $help_cmd->run([$NAME, 'fdafsasfda']);
351             print $sep;
352             $help_cmd->run([$NAME, 'running', '*']);
353             print $sep;
354             $help_cmd->run([$NAME, 'syntax']);
355             print $sep;
356             $help_cmd->run([$NAME, 'syntax', 'command']);
357             print $sep;
358             $proc->{terminated} = 1;
359             $help_cmd->run([$NAME, '*']);
360             print $sep;
361             # $help_cmd->run %W(${$NAME} s.*)
362             # print $sep;
363             # $help_cmd->run %W(${$NAME} s<>)
364             # print $sep;
365             }
366              
367             1;