File Coverage

lib/Devel/Trepan/CmdProcessor/Complete.pm
Criterion Covered Total %
statement 76 109 69.7
branch 34 54 62.9
condition 15 30 50.0
subroutine 7 10 70.0
pod 0 5 0.0
total 132 208 63.4


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2014 Rocky Bernstein <rocky@cpan.org>
3              
4             # Part of Devel::Trepan::CmdProcessor that loads up debugger commands from
5             # builtin and user directories.
6             # Top-level command completion routines.
7 12     12   81 use rlib '../../..';
  12         24  
  12         70  
8              
9             package Devel::Trepan::CmdProcessor;
10 12     12   4122 use warnings; use strict;
  12     12   24  
  12         299  
  12         154  
  12         29  
  12         271  
11 12     12   320 no warnings 'redefine';
  12         23  
  12         420  
12 12     12   5104 use Devel::Trepan::Complete;
  12         31  
  12         16188  
13              
14             my $_list_complete_i = -1;
15             sub list_complete($$$)
16             {
17 0     0 0 0 my($self, $text, $state) = @_;
18             # clear counter at the first call
19 0         0 eval { state $_list_complete_i = -1;
  0         0  
20 0         0 $_list_complete_i++;;
21             };
22 0         0 my $cw = $self->{completions};
23 0         0 for (; $_list_complete_i <= $#{$cw}; $_list_complete_i++) {
  0         0  
24 0 0 0     0 return $cw->[$_list_complete_i]
25             if defined $cw->[$_list_complete_i] and ($cw->[$_list_complete_i] =~ /^\Q$text/);
26             }
27 0         0 return undef;
28             };
29              
30              
31             my ($_last_line, $_last_start, $_last_end, @_last_return, $_last_token);
32              
33             # Handle initial completion. We draw from the commands, aliases,
34             # and macros for completion. However we won't include aliases which
35             # are prefixes of other commands.
36             sub complete($$$$$)
37             {
38 18     18 0 10852 my ($self, $text, $line, $start, $end) = @_;
39 18         39 $self->{leading_str} = $line;
40              
41 18 100       50 $_last_line = '' unless defined $_last_line;
42 18 100       37 $_last_start = -1 unless defined $_last_start;
43 18 100       38 $_last_end = -1 unless defined $_last_end;
44 18 100       35 $_last_token = '' unless defined $_last_token;
45 18 100 66     111 $_last_token = '' unless
46             $_last_start < length($line) &&
47             0 == index(substr($line, $_last_start), $_last_token);
48             # print "\ntext: $text, line: $line, start: $start, end: $end\n";
49             # print "\nlast_line: $_last_line, last_start: $_last_start, last_end: $last_end\n";
50 18         29 my $stripped_line;
51 18         130 ($stripped_line = $line) =~ s/\s*$//;
52 18 50 33     59 if ($_last_line eq $stripped_line && $stripped_line) {
53 0         0 $self->{completions} = \@_last_return;
54 0         0 return @_last_return;
55             }
56 18         41 ($_last_line, $_last_start, $_last_end) = ($line, $start, $end);
57              
58 18         27 my @commands = sort keys %{$self->{commands}};
  18         375  
59 18         103 my ($next_blank_pos, $token) =
60             Devel::Trepan::Complete::next_token($line, 0);
61 18 0 33     44 if (!$token && !$_last_token) {
62 0         0 @_last_return = @commands;
63 0         0 $_last_token = $_last_return[0];
64 0         0 $_last_line = $line . $_last_token;
65 0         0 $_last_end += length($_last_token);
66 0         0 $self->{completions} = \@_last_return;
67 0         0 return (@commands);
68             }
69              
70 18   33     37 $token ||= $_last_token;
71 18         65 my @match_pairs = complete_token_with_next($self->{commands}, $token);
72              
73 18         38 my $match_hash = {};
74 18         41 for my $pair (@match_pairs) {
75 22         77 $match_hash->{$pair->[0]} = $pair->[1];
76             }
77              
78             my @alias_pairs = complete_token_filtered_with_next($self->{aliases},
79             $token, $match_hash,
80 18         57 $self->{commands});
81 18         32 push @match_pairs, @alias_pairs;
82 18 100       50 if ($next_blank_pos >= length($line)) {
83 4         10 @_last_return = sort map {$_->[0]} @match_pairs;
  9         26  
84 4         10 $_last_token = $_last_return[0];
85 4 50       12 if (defined($_last_token)) {
86 4         9 $_last_line = $line . $_last_token;
87 4         9 $_last_end += length($_last_token);
88             }
89 4 0 33     27 if (scalar @_last_return == 0 && $self->{settings}{autoeval}) {
90 0         0 return Devel::Trepan::Complete::complete_subs($stripped_line);
91             }
92 4         89 $self->{completions} = \@_last_return;
93 4         37 return @_last_return;
94             } else {
95 14         29 for my $pair (@alias_pairs) {
96 0         0 $match_hash->{$pair->[0]} = $pair->[1];
97             }
98             }
99 14 50       34 if (scalar(@match_pairs) > 1) {
100             # FIXME: figure out what to do here.
101             # Matched multiple items in the middle of the string
102             # We can't handle this so do nothing.
103 0         0 return ();
104             # return match_pairs.map do |name, cmd|
105             # ["#{name} #{args[1..-1].join(' ')}"]
106             # }
107             }
108             # scalar @match_pairs == 1
109 14         74 @_last_return = $self->next_complete($line, $next_blank_pos,
110             $match_pairs[0]->[1],
111             $token);
112              
113 14         31 $self->{completions} = \@_last_return;
114 14 50 66     49 if (scalar @_last_return == 0 && $self->{settings}{autoeval}) {
115 2         10 return Devel::Trepan::Complete::complete_subs($stripped_line);
116             }
117              
118 12         90 return @_last_return;
119             }
120              
121             sub next_complete($$$$$)
122             {
123 20     20 0 61 my($self, $str, $next_blank_pos, $cmd, $last_token) = @_;
124              
125 20         29 my $token;
126 20         51 ($next_blank_pos, $token) =
127             Devel::Trepan::Complete::next_token($str, $next_blank_pos);
128 20 50 66     68 return () if !$token && !$last_token;
129 20 50       42 return () unless defined($cmd);
130 20 50       59 return @{$cmd} if ref($cmd) eq 'ARRAY';
  0         0  
131 20 100       40 return $cmd->($token) if (ref($cmd) eq 'CODE');
132              
133 19 100       147 if ($cmd->can("complete_token_with_next")) {
    50          
134 15         53 my @match_pairs = $cmd->complete_token_with_next($token);
135 15 50       38 return () unless scalar @match_pairs;
136 15 100       36 if ($next_blank_pos >= length($str)) {
137 9         21 return map {$_->[0]} @match_pairs;
  37         84  
138             } else {
139 6 50       17 if (scalar @match_pairs == 1) {
140 6 50 66     39 if ($next_blank_pos == length($str)-1
    50          
141             && ' ' ne substr($str, length($str)-1)) {
142 0         0 return map {$_->[0]} @match_pairs;
  0         0  
143             } elsif ($match_pairs[0]->[0] eq $token) {
144 6         38 return $self->next_complete($str, $next_blank_pos,
145             $match_pairs[0]->[1],
146             $token);
147             } else {
148 0         0 return ();
149             }
150             } else {
151             # FIXME: figure out what to do here.
152             # Matched multiple items in the middle of the string
153             # We can't handle this so do nothing.
154 0         0 return ();
155             }
156             }
157             } elsif ($cmd->can('complete')) {
158 4         12 my @matches = $cmd->complete($token);
159 4 50       11 return () unless scalar @matches;
160 4 50       17 if (substr($str, $next_blank_pos) =~ /\s*$/ ) {
161 4 100 100     23 if (1 == scalar(@matches) && $matches[0] eq $token) {
162             # Nothing more to complete.
163 2         8 return ();
164             } else {
165 2         25 return @matches;
166             }
167             } else {
168             # FIXME: figure out what to do here.
169             # Matched multiple items in the middle of the string
170             # We can't handle this so do nothing.
171 0           return ();
172             }
173             } else {
174 0           return ();
175             }
176             }
177              
178             sub filename_complete($$) {
179 0     0 0   my ($self, $prefix) = @_;
180 0           $self->{interfaces}[-1]->rl_filename_list($prefix);
181             }
182              
183             unless (caller) {
184             require Devel::Trepan::CmdProcessor;
185             my $cmdproc = Devel::Trepan::CmdProcessor->new;
186             # $cmdproc->run_cmd(['list', 5]); # Invalid - nonstring arg
187             printf "complete('s') => %s\n", join(', ', $cmdproc->complete("s", 's', 0, 1));
188             printf "complete('') => %s\n", join(', ', $cmdproc->complete("", '', 0, 1));
189             printf "complete('help se') => %s\n", join(', ', $cmdproc->complete("help se", 'help se', 0, 1));
190              
191             eval {
192             sub complete_it($$) {
193 0     0 0   my ($cmdproc, $str) = @_;
194 0           my @c = $cmdproc->complete($str, $str, 0, length($str));
195 0           printf "complete('$str') => %s\n", join(', ', @c);
196 0           return @c;
197             }
198             };
199              
200             my @c = complete_it($cmdproc, "set ");
201             @c = complete_it($cmdproc, "help set base");
202             @c = complete_it($cmdproc, "set basename on ");
203             my $str = './';
204             @c = $cmdproc->filename_complete($str);
205             printf "complete('$str') => %s\n", join(', ', @c);
206             }
207              
208             1;