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 56 73.2
pod 0 22 0.0
total 205 631 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   106 use rlib '../../../..';
  12     1   35  
  12         85  
  1         8  
  1         2  
  1         10  
5              
6             package Devel::Trepan::CmdProcessor::Command::Help;
7 12     12   4559 use warnings; no warnings 'redefine'; use utf8;
  12     12   28  
  12     12   351  
  12     1   69  
  12     1   28  
  12     1   364  
  12         69  
  12         30  
  12         88  
  1         412  
  1         4  
  1         30  
  1         5  
  1         2  
  1         27  
  1         5  
  1         3  
  1         9  
8              
9 12     12   5366 use Devel::Trepan::Pod2Text qw(pod2string help2podstring);
  12     1   45  
  12         839  
  1         31  
  1         2  
  1         55  
10 12     12   94 use Devel::Trepan::Complete qw(complete_token);
  12     1   33  
  12         727  
  1         5  
  1         3  
  1         55  
11              
12 12     12   87 use if !@ISA, Devel::Trepan::CmdProcessor::Command ;
  12     1   32  
  12         83  
  1         6  
  1         3  
  1         5  
13 12     12   1850 use strict;
  12     1   35  
  12         336  
  1         50  
  1         3  
  1         29  
14              
15 12     12   71 use vars qw(@ISA);
  12     1   33  
  12         931  
  1         4  
  1         2  
  1         73  
16             unless (@ISA) {
17 12     12   122 eval <<'EOE';
  12     12   36  
  12     12   881  
  12     12   90  
  12     12   32  
  12     12   564  
  12         78  
  12         32  
  12         546  
  12         84  
  12         32  
  12         640  
  12         81  
  12         41  
  12         512  
  12         82  
  12         36  
  12         464  
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   78 use vars @CMD_VARS; # Value inherited from parent
  12     1   37  
  12         1771  
  1         6  
  1         3  
  1         115  
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   93 eval "use constant CATEGORIES => {
  12 50   12   112  
  12     1   39  
  12         903  
  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   81 use File::Basename;
  12     1   29  
  12         734  
  1         5  
  1         3  
  1         60  
72 12     12   76 use File::Spec;
  12     1   28  
  12         26886  
  1         6  
  1         4  
  1         2096  
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 18 my ($self) = @_;
  0     0 0 0  
79 6         16 my $proc = $self->{proc};
  0         0  
80 6         13 my %cmd_hash = %{$proc->{commands}};
  6         150  
  0         0  
  0         0  
81 6         69 my @commands = keys %cmd_hash;
  0         0  
82 6 50       34 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         92 return @commands;
  0         0  
90             }
91             }
92              
93             sub complete($$)
94             {
95 5     5 0 16 my ($self, $prefix) = @_;
  0     0 0 0  
96 5         15 my $proc = $self->{proc};
  0         0  
97 5         15 my @candidates = (keys %{CATEGORIES()}, qw(* all),
  5         39  
  0         0  
  0         0  
98             $self->command_names());
99 5         35 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         39 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         5 my @matches = complete_token(\@candidates, $prefix);
  0         0  
111 1         9 sort @matches;
  0         0  
112             }
113              
114             sub complete_token_with_next($$;$)
115             {
116 5     5 0 19 my ($self, $prefix, $cmd_prefix) = @_;
  0     0 0 0  
117 5         17 my $proc = $self->{proc};
  0         0  
118 5         15 my @result = ();
  0         0  
119 5         25 my @matches = $self->complete($prefix);
  0         0  
120 5         19 foreach my $cmd (@matches) {
  0         0  
121 5         14 my %commands = %{$proc->{commands}};
  5         87  
  0         0  
  0         0  
122 5 100       30 if (exists $commands{$cmd}) {
  0 50       0  
    0          
    0          
123 4         30 push @result, [$cmd, $commands{$cmd}];
  0         0  
124             } elsif ('syntax' eq $cmd) {
125 1         2 my @syntax_files = @{$self->syntax_files()};
  1         6  
  0         0  
  0         0  
126             push @result, [$cmd,
127 1     1   3 sub { my $prefix = shift;
  0         0  
128 1         12 $self->complete_syntax($prefix) } ];
  1         5  
  0         0  
  0         0  
129             } else {
130 0         0 push @result, [$cmd, ['*'] ];
  0         0  
131             }
132             }
133 5         25 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         2 my @aliases = sort(keys(%{$self->{proc}{aliases}}));
  1         25  
  0         0  
  0         0  
161 1         7 $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 4 my $self = shift;
  0     0 0 0  
192 2 100       13 return $self->{syntax_files} if $self->{syntax_files};
  0 0       0  
193 1         255 my @pods = glob(File::Spec->catfile($HELP_DIR, "/*.pod"));
  0         0  
194 1         6 my @result = map({ $_ = basename($_, '.pod') } @pods);
  5         184  
  0         0  
  0         0  
195 1         5 $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         3 my $cmd_name = $args->[1];
  0            
241 1 50       4 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         9 $self->section('All currently valid command names:');
  0            
245 1         4 my @cmds = sort($self->command_names());
  0            
246 1         10 $self->msg($self->columnize_commands(\@cmds));
  0            
247 1 50       3 if (scalar keys %{$proc->{aliases}}) {
  1 0       4  
  0            
  0            
248 1         4 $self->msg('');
  0            
249 1         3 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;