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   17318 use warnings; use strict; use utf8;
  13     13   80  
  13     13   376  
  13         66  
  13         55  
  13         290  
  13         5063  
  13         122  
  13         81  
4 13     13   342 use Exporter;
  13         32  
  13         749  
5              
6             package Devel::Trepan::Complete;
7             =head1 SUMMARY
8              
9             Completion routines for L<Devel::Trepan>
10              
11             =cut
12 13     13   81 use vars qw(@ISA @EXPORT); @ISA = qw(Exporter);
  13         28  
  13         1105  
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   94 use constant BUILTIN_CONST => qw(__FILE__ __LINE__ __PACKAGE__);
  13         61  
  13         2087  
20 13         1526 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   80 );
  13         28  
55              
56 13     13   90 use constant BUILTIN_CORE_FNS => map { 'CORE::' . $_ } BUILTIN_FNS;
  13         28  
  13         42  
  2262         18591  
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 3641 my ($complete_ary, $prefix) = @_;
69 41         81 my @result = ();
70 41         96 for my $cmd (@$complete_ary) {
71 7709 100       15466 if (0 == index($cmd, $prefix)) {
72 1132         1750 push @result, $cmd ;
73             }
74             }
75 41         572 sort @result;
76             }
77              
78             sub complete_token_with_next($$;$)
79             {
80 32     32 0 2126 my ($complete_hash, $prefix, $cmd_prefix) = @_;
81 32 50       99 $cmd_prefix ='' if scalar(@_) < 3;
82 32         60 my $cmd_prefix_len = length($cmd_prefix);
83 32         64 my @result = ();
84 32         61 while (my ($cmd_name, $cmd_obj) = each %{$complete_hash}) {
  870         2318  
85 838 100       2142 if (0 == index($cmd_name, $cmd_prefix . $prefix)) {
86 67         233 push @result, [substr($cmd_name, $cmd_prefix_len), $cmd_obj]
87             }
88             }
89 32         151 sort {$a->[0] cmp $b->[0]} @result;
  93         215  
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 57 my ($aliases, $prefix, $expanded, $commands) = @_;
119             # require Enbugger; Enbugger->stop;
120 18         32 my @complete_ary = keys %{$aliases};
  18         235  
121 18         66 my %expanded = %{$expanded};
  18         73  
122 18         42 my @result = ();
123 18         47 for my $cmd (@complete_ary) {
124 756 100 100     1873 if (0 == index($cmd, $prefix) && !exists $expanded{$aliases->{$cmd}}) {
125 1         4 push @result, [$cmd, $commands->{$aliases->{$cmd}}];
126             }
127             }
128 18         99 @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 8431 my ($str, $start_pos) = @_;
142 47         117 my $look_at = substr($str, $start_pos);
143 47         87 my $strlen = length($look_at);
144 47 100       129 return (1, '') if 0 == $strlen;
145 46         71 my $next_nonblank_pos = $start_pos;
146 46         81 my $next_blank_pos;
147 46 100       218 if ($look_at =~ /^(\s*)(\S+)\s*/) {
    50          
    0          
148 43         110 $next_nonblank_pos += length($1);
149 43         92 $next_blank_pos = $next_nonblank_pos+length($2);
150             } elsif ($look_at =~ /^(\s+)$/) {
151 3         25 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         93 my $token_size = $next_blank_pos - $next_nonblank_pos;
158 43         171 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 2008 my ($prefix) = @_;
199 6 100       18 unless(@signal_complete_completions) {
200 2         48 @signal_complete_completions = keys %SIG;
201 2         8 my $last_sig = scalar @signal_complete_completions;
202             push(@signal_complete_completions,
203 2         6 map({lc $_} @signal_complete_completions));
  136         235  
204 2         27 my @nums = (-$last_sig .. $last_sig);
205 2         21 push @signal_complete_completions, @nums;
206             }
207 6         18 complete_token(\@signal_complete_completions, $prefix);
208             }
209              
210             sub complete_builtins($)
211             {
212 5     5 0 3247 my ($prefix) = @_;
213 5         93 my @builtin_fns = BUILTIN_FNS;
214 5 100       19 if (0 == index($prefix ,'CORE::')) {
215 2         8 map { 'CORE::' . $_ }
  1         9  
216             complete_token(\@builtin_fns, substr($prefix, length('CORE::')));
217             } else {
218 3         10 complete_token(\@builtin_fns, $prefix);
219             }
220             }
221              
222             sub complete_subs($)
223             {
224 10     10 0 5719 my ($prefix) = @_;
225 13     13   119 no warnings 'once';
  13         35  
  13         3436  
226 10         523 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       48 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         27 my @functions = complete_token(\@all_fns, $prefix);
234 10 100 66     87 if (scalar @functions == 0 && !($prefix =~ /::/)) {
235 4         13 my $pkg_prefix = $DB::package . '::';
236 4 50       15 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         12 my $new_prefix = $pkg_prefix . $prefix;
242 4         16 @functions = map { substr($_, length($pkg_prefix)) }
  1         6  
243             complete_token(\@all_fns, $new_prefix);
244             }
245             }
246 10         129 return sort @functions;
247             }
248              
249             sub complete_packages($)
250             {
251 2     2 0 1326 my ($prefix) = @_;
252 2         5 my %pkgs;
253 13     13   100 no warnings 'once';
  13         33  
  13         6953  
254 2         7 foreach my $function (keys %DB::sub) {
255 2         11 my @parts = split('::', $function);
256 2 50       9 if (scalar @parts > 1) {
257 2         6 pop(@parts);
258 2         6 my $pkg = join('::', @parts);
259 2 50       45 $pkgs{$pkg} = 1 if $pkg =~ /^$prefix/;
260             }
261             }
262 2         12 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;