File Coverage

lib/App/oo_modulino_zsh_completion_helper.pm
Criterion Covered Total %
statement 29 128 22.6
branch 0 52 0.0
condition 0 21 0.0
subroutine 10 23 43.4
pod 0 12 0.0
total 39 236 16.5


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2             package App::oo_modulino_zsh_completion_helper;
3 1     1   645 use 5.010;
  1         3  
4 1     1   5 use strict;
  1         2  
  1         15  
5 1     1   5 use warnings;
  1         2  
  1         67  
6              
7             our $VERSION = "0.04";
8              
9 1         12 use MOP4Import::Base::CLI_JSON -as_base
10             , [fields => [eol => default => "\n"]]
11 1     1   481 ;
  1         82259  
12              
13 1     1   2174 use MOP4Import::FieldSpec;
  1         2  
  1         40  
14 1     1   5 use MOP4Import::Util qw/fields_hash fields_array/;
  1         2  
  1         36  
15 1     1   414 use MOP4Import::Util::FindMethods;
  1         490  
  1         42  
16              
17 1     1   445 use Module::Runtime ();
  1         1425  
  1         26  
18              
19 1     1   360 use MOP4Import::Util::ResolveSymlinks;
  1         455  
  1         33  
20              
21             use MOP4Import::Types
22 1         7 ZshParams => [[fields => qw/pmfile words NUMERIC CURRENT BUFFER CURSOR/]]
23 1     1   6 ;
  1         2  
24              
25             sub cli_inspector {
26 0     0 0   require MOP4Import::Util::Inspector;
27 0           'MOP4Import::Util::Inspector';
28             }
29              
30             sub onconfigure_zero {
31 0     0 0   (my MY $self) = @_;
32 0           $self->{eol} = "\0";
33             }
34              
35             sub cmd_joined {
36 0     0 0   (my MY $self, my ($method, @args)) = @_;
37 0           my @completion = $self->$method(@args);
38 0           print join($self->{eol}, @completion), $self->{eol};
39             }
40              
41 0     0 0   sub IGNORE_OPTIONS_FROM {'MOP4Import::Base::CLI_JSON'}
42              
43             sub zsh_options {
44 0     0 0   (my MY $self, my %opts) = @_;
45              
46 0           my ZshParams $opts = \%opts;
47              
48             my ($targetClass, $has_shbang) = $self->load_module_from_pm($opts->{pmfile})
49 0 0         or Carp::croak "Can't extract class name from $opts->{pmfile}";
50              
51 0           my $optionPrefix = $self->word_prefix($opts);
52 0 0         $optionPrefix =~ s/^--?// if defined $optionPrefix;
53              
54 0           my $universal_argument = $opts->{NUMERIC};
55              
56 0           my @options = $self->cli_inspector->list_options_of($targetClass);
57 0 0 0       if (defined $optionPrefix and $optionPrefix ne '') {
58 0           @options = grep {/^$optionPrefix/} @options;
  0            
59             }
60              
61             my @grouped = map {
62 0           my ($implClass, @specs) = @$_;
  0            
63 0 0         if (not $universal_argument) {
64 0 0 0       ($implClass eq '' || $implClass ne $self->IGNORE_OPTIONS_FROM) ? @specs : ();
65             } else {
66 0           @specs;
67             }
68             } $self->cli_inspector->group_options_of($targetClass, @options);
69              
70             map {
71 0 0         if (ref (my FieldSpec $spec = $_)) {
  0            
72 0 0         "--$spec->{name}=-". ($spec->{doc} ? "[$spec->{doc}]" : "");
73             } else {
74 0           "--$_=-";
75             }
76             } @grouped;
77             }
78              
79             sub zsh_methods {
80 0     0 0   (my MY $self, my %opts) = @_;
81              
82 0           my ZshParams $opts = \%opts;
83              
84             my ($targetClass, $has_shbang) = $self->load_module_from_pm($opts->{pmfile})
85 0 0         or Carp::croak "Can't extract class name from $opts->{pmfile}";
86              
87 0           my $insp = $self->cli_inspector;
88              
89 0           my $methodPrefix = $self->word_prefix($opts);
90              
91 0           my $universal_argument = $opts->{NUMERIC};
92              
93             # default => methods implemented in $targetClass only.
94             # one universal_argument => find superclasses too.
95             # two universal_argument => find all methods including getters, new,...
96              
97 0 0 0       my @gather_default = (($methodPrefix || (($universal_argument || 0) >= 4*4))
98             ? (all => 1) : (no_getter => 1));
99 0           my @methods = $self->gather_methods_from($targetClass, undef, @gather_default);
100 0 0 0       if ($methodPrefix or $universal_argument) {
101 0           my %seen; $seen{$_} = 1 for @methods;
  0            
102 0           (undef, my @super) = @{mro::get_linear_isa($targetClass)};
  0            
103 0           foreach my $super (@super) {
104 0           push @methods, $self->gather_methods_from($super, \%seen, @gather_default);
105             }
106             }
107              
108 0 0         if ($methodPrefix) {
109 0           @methods = grep {/^$methodPrefix/} @methods;
  0            
110             }
111              
112             map {
113 0 0         my $method = $targetClass->can("cmd_$_") ? "cmd_$_" : $_;
  0            
114 0 0         if (defined (my $doc = $insp->info_method_doc_of($targetClass, $method, 1))) {
115 0           "$_:$doc"
116             } else {
117 0           $_;
118             }
119             } @methods;
120             }
121              
122             sub word_prefix {
123 0     0 0   (my MY $self, my ZshParams $opts) = @_;
124              
125 0 0 0       unless ($opts->{words} && $opts->{CURRENT}) {
126 0           return undef;
127             }
128              
129 0           $opts->{words}[$opts->{CURRENT} - 1];
130             }
131              
132             sub gather_methods_from {
133 0     0 0   (my MY $self, my $targetClass, my $seenDict, my %opts) = @_;
134 0           my $no_getter = delete $opts{no_getter};
135 0           my $all = delete $opts{all};
136             MOP4Import::Util::function_names(
137             from => $targetClass,
138             matching => qr{^(?:cmd_)?[a-z]},
139             grep => sub {
140 0     0     my ($realName, $code) = @_;
141 0           s/^cmd_//;
142 0 0         if ($seenDict->{$_}++) {
143 0           return 0;
144             }
145 0 0         if ($self->cli_inspector->info_code_attribute(MetaOnly => $code)) {
146 0           return 0;
147             }
148 0 0         if ($all) {
149 0           return 1;
150             } else {
151 0 0         return 0 if MOP4Import::Base::Configure->can($_);
152             }
153 0 0         if ($no_getter) {
154 0           return not $self->cli_inspector->is_getter_of($targetClass, $_);
155             }
156 0           1;
157             # MOP4Import::Util::has_method_attr($code); # Too strict.
158             },
159 0           %opts,
160             );
161             }
162              
163             sub load_module_from_pm {
164 0     0 0   (my MY $self, my $pmFile) = @_;
165              
166 0 0         my ($modname, $libpath, $has_shbang) = $self->find_package_from_pm($pmFile)
167             or Carp::croak "Can't find module name and library root from $pmFile'";
168              
169             {
170 0           local @INC = ($libpath, @INC);
  0            
171 0           Module::Runtime::require_module($modname);
172             }
173              
174 0 0         wantarray ? ($modname, $has_shbang) : $modname;
175             }
176              
177             sub find_package_from_pm {
178 0     0 0   (my MY $self, my $pmFile) = @_;
179              
180 0           my $realFn = MOP4Import::Util::ResolveSymlinks::normalize($pmFile);
181 0           $realFn =~ s/\.\w+\z//;
182              
183 0           my @dir = $self->splitdir($realFn);
184              
185 0           local $_ = $self->cli_read_file__($pmFile);
186              
187 0           my $has_shbang = m{^\#!};
188              
189 0           while (/(?:^|\n) [\ \t]* (?# line beginning + space)
190             package [\n\ \t]+ (?# newline is allowed here)
191             ([\w:]+) (?# module name)
192             \s* [;\{] (?# statement or block)
193             /xsg) {
194 0           my ($modname) = $1;
195              
196             # Tail of $modname should be equal to it's rootname.
197 0 0         if (my $libprefix = $self->test_modname_with_path($modname, \@dir)) {
198 0 0         return wantarray ? ($modname, $libprefix, $has_shbang) : $modname;
199             }
200             }
201 0           return;
202             }
203              
204             sub test_modname_with_path {
205 0     0 0   (my MY $self, my ($modname, $pathlist)) = @_;
206 0           my @modpath = split /::/, $modname;
207 0   0       shift @modpath while @modpath and $modpath[0] eq '';
208 0           my @copy = @$pathlist;
209 0   0       do {
210 0 0         if (pop(@copy) ne pop(@modpath)) {
211 0           return;
212             }
213             } while (@copy and @modpath);
214 0 0         if (@modpath) {
    0          
215 0           return;
216             }
217             elsif (@copy) {
218 0           File::Spec->catdir(@copy)
219             }
220             }
221              
222             sub splitdir {
223 0     0 0   (my MY $self, my $fn) = @_;
224 0           File::Spec->splitdir($fn);
225             }
226              
227             MY->cli_run(\@ARGV, {0 => 'zero'}) unless caller;
228              
229              
230              
231             1;
232             __END__