File Coverage

lib/Devel/Trepan/Complete.pm
Criterion Covered Total %
statement 105 132 79.5
branch 22 46 47.8
condition 5 12 41.6
subroutine 18 20 90.0
pod 5 10 50.0
total 155 220 70.4


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011-2012, 2014 Rocky Bernstein <rocky@cpan.org>
3 13     13   61516 use warnings; use strict; use utf8;
  13     13   31  
  13     13   371  
  13         61  
  13         25  
  13         210  
  13         5102  
  13         114  
  13         73  
4 13     13   333 use Exporter;
  13         22  
  13         683  
5              
6             package Devel::Trepan::Complete;
7             =head1 SUMMARY
8              
9             Completion routines for L<Devel::Trepan>
10              
11             =cut
12 13     13   68 use vars qw(@ISA @EXPORT); @ISA = qw(Exporter);
  13         25  
  13         982  
13             @EXPORT = qw(
14             complete_builtins complete_subs complete_packages
15             complete_token complete_token_with_next
16             next_token signal_complete
17             complete_token_filtered_with_next);
18              
19 13     13   77 use constant BUILTIN_CONST => qw(__FILE__ __LINE__ __PACKAGE__);
  13         22  
  13         2158  
20 13         1670 use constant BUILTIN_FNS => qw(
21             abs accept alarm
22             and atan2 bind binmode bless caller
23             chdir chmod chown chr chroot close
24             closedir cmp connect continue cos crypt
25             dbmclose dbmopen die dump endgrent
26             endhostent endnetent endprotoent endpwent
27             endservent eof eq exec exit exp fcntl
28             fileno flock fork formline ge getc
29             getgrent getgrgid getgrnam gethostbyaddr
30             gethostbyname gethostent getlogin
31             getnetbyaddr getnetbyname getnetent
32             getpeername getpgrp getppid getpriority
33             getprotobyname getprotobynumber getprotoent
34             getpwent getpwnam getpwuid getservbyname
35             getservbyport getservent getsockname
36             getsockopt glob gmtime gt hex index int
37             ioctl join kill lc lcfirst le length
38             link listen localtime lock log lstat lt
39             mkdir msgctl msgget msgrcv msgsnd ne
40             not oct open opendir or ord pack pipe
41             quotemeta rand read readdir readline
42             readlink readpipe recv ref rename require
43             reset reverse rewinddir rindex rmdir seek
44             seekdir select semctl semget semop send
45             setgrent sethostent setnetent setpgrp
46             setpriority setprotoent setpwent setservent
47             setsockopt shmctl shmget shmread shmwrite
48             shutdown sin sleep socket socketpair
49             sprintf sqrt srand stat substr symlink
50             syscall sysopen sysread system syswrite tell
51             telldir time times truncate uc ucfirst
52             umask unlink unpack utime values vec
53             wait waitpid wantarray warn write x xor
54 13     13   81 );
  13         28  
55              
56 13     13   88 use constant BUILTIN_CORE_FNS => map { 'CORE::' . $_ } BUILTIN_FNS;
  13         39  
  13         52  
  2262         18702  
57              
58             =head2 Subroutines
59              
60             =head3 complete_token
61              
62             Return an list of string, I<$complete_ary>, which start out with
63             String I<$prefix>.
64              
65             =cut
66             sub complete_token($$)
67             {
68 41     41 1 3215 my ($complete_ary, $prefix) = @_;
69 41         55 my @result = ();
70 41         94 for my $cmd (@$complete_ary) {
71 7710 100       10806 if (0 == index($cmd, $prefix)) {
72 1132         1451 push @result, $cmd ;
73             }
74             }
75 41         461 sort @result;
76             }
77              
78             sub complete_token_with_next($$;$)
79             {
80 32     32 0 1758 my ($complete_hash, $prefix, $cmd_prefix) = @_;
81 32 50       90 $cmd_prefix ='' if scalar(@_) < 3;
82 32         49 my $cmd_prefix_len = length($cmd_prefix);
83 32         44 my @result = ();
84 32         70 while (my ($cmd_name, $cmd_obj) = each %{$complete_hash}) {
  870         1644  
85 838 100       1512 if (0 == index($cmd_name, $cmd_prefix . $prefix)) {
86 67         183 push @result, [substr($cmd_name, $cmd_prefix_len), $cmd_obj]
87             }
88             }
89 32         114 sort {$a->[0] cmp $b->[0]} @result;
  85         141  
90             }
91              
92             =head3 complete_token_filtered
93              
94             Find all starting matches in Hash I<$aliases+>that start with
95             I<$prefix>, but filter out any matches already in I<$expanded>.
96              
97             =cut
98             sub complete_token_filtered($$$)
99             {
100 0     0 1 0 my ($aliases, $prefix, $expanded) = @_;
101 0         0 my @complete_ary = keys %{$aliases};
  0         0  
102 0         0 my @result = ();
103 0         0 for my $cmd (@complete_ary) {
104             push @result, $cmd if
105 0 0 0     0 0 == index($cmd, $prefix) && !exists $expanded->{$aliases->{$cmd}};
106             }
107 0         0 sort @result;
108             }
109              
110             =head3 complete_token_filtered_with_next
111              
112             Find all starting matches in hash I<$aliases> that start with I<$prefix>,
113             but filter out any matches already in I<$expanded>.
114              
115             =cut
116             sub complete_token_filtered_with_next($$$$)
117             {
118 18     18 1 42 my ($aliases, $prefix, $expanded, $commands) = @_;
119             # require Enbugger; Enbugger->stop;
120 18         26 my @complete_ary = keys %{$aliases};
  18         141  
121 18         29 my %expanded = %{$expanded};
  18         70  
122 18         32 my @result = ();
123 18         41 for my $cmd (@complete_ary) {
124 756 100 100     1338 if (0 == index($cmd, $prefix) && !exists $expanded{$aliases->{$cmd}}) {
125 1         4 push @result, [$cmd, $commands->{$aliases->{$cmd}}];
126             }
127             }
128 18         80 @result;
129             }
130              
131             =head3 next_token
132              
133             Find the next token in str string from start_pos. We return
134             the token and the next blank position after the token or
135             length($str) if this is the last token. Tokens are delimited by
136             white space.
137              
138             =cut
139             sub next_token($$)
140             {
141 47     47 1 7971 my ($str, $start_pos) = @_;
142 47         97 my $look_at = substr($str, $start_pos);
143 47         60 my $strlen = length($look_at);
144 47 100       95 return (1, '') if 0 == $strlen;
145 46         60 my $next_nonblank_pos = $start_pos;
146 46         52 my $next_blank_pos;
147 46 100       207 if ($look_at =~ /^(\s*)(\S+)\s*/) {
    50          
    0          
148 43         86 $next_nonblank_pos += length($1);
149 43         66 $next_blank_pos = $next_nonblank_pos+length($2);
150             } elsif ($look_at =~ /^(\s+)$/) {
151 3         22 return ($start_pos + length($1), '');
152             } elsif ($look_at =~/^(\S+)\s*/) {
153 0         0 $next_blank_pos = $next_nonblank_pos + length($1);
154             } else {
155 0         0 die "Something is wrong in next_token";
156             }
157 43         62 my $token_size = $next_blank_pos - $next_nonblank_pos;
158 43         132 return ($next_blank_pos, substr($str, $next_nonblank_pos, $token_size));
159             }
160              
161             =head3 filename_list
162              
163             I<filename_list> is from I<Term::ReadLine::readline.pm>:
164              
165             For use in passing to completion_matches(), returns a list of
166             filenames that begin with the given pattern. The user of this
167             package can set I<$rl_completion_function> to 'rl_filename_list' to
168             restore the default of filename matching if they'd changed it
169             earlier, either directly or via I<&rl_basic_commands>.
170              
171             =cut
172             sub filename_list(;$$)
173             {
174 0     0 1 0 my ($pattern, $add_suffix) = @_;
175 0 0       0 $pattern = '' unless defined $pattern;
176 0 0       0 $add_suffix = 0 unless defined $add_suffix;
177             # $pattern = glob($pattern) if substr($pattern, 0, 1) = '~';
178 0         0 my @files = (<$pattern*>);
179 0 0       0 if ($add_suffix) {
180 0         0 foreach (@files) {
181 0 0 0     0 if (-l $_) {
    0          
    0          
    0          
182 0         0 $_ .= '@';
183             } elsif (-d _) {
184 0         0 $_ .= '/';
185             } elsif (-x _) {
186 0         0 $_ .= '*';
187             } elsif (-S _ || -p _) {
188 0         0 $_ .= '=';
189             }
190             }
191             }
192 0         0 return @files;
193             }
194              
195             # Custom completion routines
196             my @signal_complete_completions=();
197             sub signal_complete($) {
198 6     6 0 1759 my ($prefix) = @_;
199 6 100       22 unless(@signal_complete_completions) {
200 2         53 @signal_complete_completions = keys %SIG;
201 2         6 my $last_sig = scalar @signal_complete_completions;
202             push(@signal_complete_completions,
203 2         7 map({lc $_} @signal_complete_completions));
  136         255  
204 2         21 my @nums = (-$last_sig .. $last_sig);
205 2         43 push @signal_complete_completions, @nums;
206             }
207 6         27 complete_token(\@signal_complete_completions, $prefix);
208             }
209              
210             sub complete_builtins($)
211             {
212 5     5 0 2793 my ($prefix) = @_;
213 5         130 my @builtin_fns = BUILTIN_FNS;
214 5 100       20 if (0 == index($prefix ,'CORE::')) {
215 2         7 map { 'CORE::' . $_ }
  1         11  
216             complete_token(\@builtin_fns, substr($prefix, length('CORE::')));
217             } else {
218 3         7 complete_token(\@builtin_fns, $prefix);
219             }
220             }
221              
222             sub complete_subs($)
223             {
224 10     10 0 3467 my ($prefix) = @_;
225 13     13   101 no warnings 'once';
  13         33  
  13         3655  
226 10         709 my @all_fns = sort((keys(%DB::sub),
227             BUILTIN_FNS, BUILTIN_CORE_FNS, BUILTIN_CONST));
228 10         24 my $have_fn_sigl = 0;
229 10 50       32 if (substr($prefix, 0, 1) eq '&') {
230 0         0 @all_fns = map { '&' . $_ } @all_fns;
  0         0  
231 0         0 $have_fn_sigl = 1;
232             }
233 10         25 my @functions = complete_token(\@all_fns, $prefix);
234 10 100 66     46 if (scalar @functions == 0 && !($prefix =~ /::/)) {
235 4         12 my $pkg_prefix = $DB::package . '::';
236 4 50       39 if ($have_fn_sigl) {
237 0         0 my $new_prefix = '&' . $pkg_prefix . substr($prefix, 1);
238 0         0 @functions = map { substr($_, length($pkg_prefix)+1) }
  0         0  
239             complete_token(\@all_fns, $new_prefix);
240             } else {
241 4         9 my $new_prefix = $pkg_prefix . $prefix;
242 4         18 @functions = map { substr($_, length($pkg_prefix)) }
  1         4  
243             complete_token(\@all_fns, $new_prefix);
244             }
245             }
246 10         113 return sort @functions;
247             }
248              
249             sub complete_packages($)
250             {
251 2     2 0 1068 my ($prefix) = @_;
252 2         5 my %pkgs;
253 13     13   94 no warnings 'once';
  13         31  
  13         7798  
254 2         9 foreach my $function (keys %DB::sub) {
255 2         9 my @parts = split('::', $function);
256 2 50       9 if (scalar @parts > 1) {
257 2         4 pop(@parts);
258 2         7 my $pkg = join('::', @parts);
259 2 50       35 $pkgs{$pkg} = 1 if $pkg =~ /^$prefix/;
260             }
261             }
262 2         10 return sort keys %pkgs;
263             }
264              
265             unless (caller) {
266             my $hash_ref = {'ab' => 1, 'aac' => 2, 'aa' => 3, 'b' => 4};
267             my @cmds = keys %{$hash_ref};
268             printf("complete_token(@cmds, '') => %s\n",
269             join(', ', complete_token(\@cmds, '')));
270             printf("complete_token(@cmds, 'a') => %s\n",
271             join(', ', complete_token(\@cmds, 'a')));
272             printf("complete_token(@cmds, 'b') => %s\n",
273             join(', ', complete_token(\@cmds, 'b')));
274             printf("complete_token(@cmds, 'c') => %s\n",
275             join(', ', complete_token(\@cmds, 'c')));
276             my @ary = complete_token_with_next($hash_ref, 'a');
277             my @ary_str = map "($_->[0], $_->[1])", @ary;
278             printf("complete_token_with_next(\$hash_ref, 'a') => %s\n",
279             join(', ', @ary_str));
280             print "0 1 \n";
281             print "0123456789012345678\n";
282             my $x = ' now is the time';
283             print "$x\n";
284             for my $pos (0, 2, 5, 6, 8, 9, 13, 18, 19) {
285             my @ary = next_token($x, $pos);
286             printf "next_token($pos) = %d, '%s'\n", $ary[0], $ary[1];
287             }
288             print "List of filenames:\n";
289             print join(', ', filename_list), "\n";
290             print "List of filenames beginning with C:\n";
291             print join(', ', filename_list('C')), "\n";
292              
293             print join(', ', signal_complete('C')), "\n";
294              
295             foreach my $prefix (qw(CORE::len len db foo CORE::foo)) {
296             printf("complete_subs($prefix) => %s\n",
297             join(', ', complete_subs($prefix)));
298             }
299              
300             $DB::package = 'main';
301             %DB::sub = qw(main::gcd 1);
302             foreach my $prefix (qw(end CORE::end gcd main::gcd foo CO __FI)) {
303             printf("complete_subs($prefix) => %s\n",
304             join(', ', complete_subs($prefix)));
305             }
306             my $prefix = 'mai';
307             printf("complete_packages($prefix) => %s\n",
308             join(', ', complete_packages($prefix)));
309              
310             # FIXME: We don't handle ~ expansion right now.
311             # print "List of filenames expanded from ~\n";
312             }
313              
314             1;