File Coverage

blib/lib/PDL/Doc/Perldl.pm
Criterion Covered Total %
statement 49 242 20.2
branch 6 114 5.2
condition 1 34 2.9
subroutine 8 23 34.7
pod 8 16 50.0
total 72 429 16.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PDL::Doc::Perldl - commands for accessing PDL doc database from 'perldl' shell
4              
5             =head1 DESCRIPTION
6              
7             This module provides a set of functions to
8             access the PDL documentation database, for use
9             from the I shell as well as the
10             I command-line program.
11              
12             Autoload files are also matched, via a search of the PDLLIB autoloader
13             tree. That behavior can be switched off with the variable
14             C<$PERLDL::STRICT_DOCS> (true: don't search autoload tree; false: search
15             the autoload tree.)
16              
17             In the interest of brevity, functions that print module names (at the moment
18             just L and L) use some shorthand notation for module names.
19             Currently-implemented shorthands are
20              
21             =over 3
22              
23             =item * P:: (short for PDL::)
24              
25             =item * P::G:: (short for PDL::Graphics::)
26              
27             =back
28              
29             To turn this feature off, set the variable $PERLDL::long_mod_names to a true value.
30             The feature is assumed to be on for the purposes of this documentation.
31              
32             =head1 SYNOPSIS
33              
34             use PDL::Doc::Perldl; # Load all documentation functions
35              
36             =head1 FUNCTIONS
37              
38             =cut
39              
40             package PDL::Doc::Perldl;
41              
42 1     1   600 use Exporter;
  1         2  
  1         44  
43 1     1   3 use strict;
  1         2  
  1         16  
44 1     1   2 use warnings;
  1         2  
  1         74  
45              
46             our @ISA = qw(Exporter);
47             our @EXPORT = qw( apropos aproposover usage help sig badinfo whatis );
48              
49 1     1   4 use PDL::Doc;
  1         1  
  1         23  
50 1     1   3 use Pod::Text;
  1         1  
  1         34  
51 1     1   3 use Cwd; # to help Debian packaging
  1         1  
  1         3663  
52              
53             # Find std file
54              
55             sub FindStdFile {
56 0     0 0 0 my ($f) = PDL::Doc::_find_inc([qw(PDL pdldoc.db)], 0);
57 0 0       0 warn("Unable to find PDL/pdldoc.db in ".join(":",@INC)."\n"), return if !defined $f;
58 0 0       0 print "Found docs database $f\n" if $PDL::verbose;
59 0         0 return $f;
60             }
61              
62             # used to find out how wide the screen should be
63             # for format_ref - really should check for a sensible lower limit
64             sub screen_width {
65             local $@;
66             eval {
67             require Term::ReadKey;
68             ( Term::ReadKey::GetTerminalSize(\*STDOUT) )[0];
69             } // 72;
70             }
71              
72             sub printmatch {
73 0 0   0 0 0 return print "no match\n" if !@_;
74 0         0 print for format_ref(@_);
75             }
76              
77             # given a long module name, return the (perhaps shortened) module name.
78              
79             sub shortmod {
80 2     2 0 5 my $module = shift;
81 2         6 $module =~ s/::$//;
82 2 50 33     9 unless ($PERLDL::long_mod_names && $PERLDL::long_mod_names){ # silence warn
83 2         7 $module =~ s/^PDL::/P::/;
84 2         5 $module =~ s/^P::Graphics::/P::G::/;
85             #additional abbreviation substitutions go here
86             }
87 2         9 return $module;
88             }
89              
90             # return a string containing a formated version of the Ref string
91             # for the given matches
92             my $LONG_FMT = "%s ...\n " . ' 'x15 . "%-*s %s\n";
93             my $NORMAL_FMT = "%-15s %-*s %s\n";
94             sub format_ref {
95 1     1 0 1357 my @match = @_;
96 1         5 my @text = ();
97             #finding the max width before doing the printing means looping through @match an extra time; so be it.
98 1         3 my @module_shorthands = map { shortmod($_->[1]) } @match;
  1         5  
99 1         2 my $max_mod_length = -1;
100 1 50       3 map {$max_mod_length = length if (length>$max_mod_length) } @module_shorthands;
  1         6  
101 1         4 my $width = screen_width()-17-1-$max_mod_length;
102 1         8 my @parser_args = (width => $width, indent => 0, sentence => 0);
103 1         3 my %seen;
104 1         3 for my $m (@match) {
105 1         3 my $name = $m->[0];
106 1         4 my $module = shortmod($m->[1]);
107 1         4 my $ref = $m->[2]{Ref};
108 1 50       6 if (!$ref) {
    50          
109             $ref = defined $m->[2]{CustomFile}
110 0 0       0 ? "[No ref avail. for `".$m->[2]{CustomFile}."']"
111             : "[No reference available]";
112             } elsif ($seen{$ref}) {
113 0         0 $ref = "[As $seen{$ref}]";
114             } else {
115 1         4 $seen{$ref} = $name;
116 1         8 my $parser = Pod::Text->new(@parser_args);
117 1         252 $parser->output_string(\my $out_text);
118 1         101 $parser->parse_string_document("=encoding utf8\n\n$ref");
119 1         3985 $ref = $out_text;
120 1         13 $ref =~ s/\n*$//; # remove last newlines so no append spaces at end
121 1         3 $ref =~ s/\n/"\n".' 'x($max_mod_length+18)/eg;
  0         0  
122 1         31 $ref =~ s/^\s*//;
123             }
124 1 50       13 push @text, sprintf length($name) > 15 ? $LONG_FMT : $NORMAL_FMT, $name, $max_mod_length, $module, $ref;
125             }
126 1 50       8 wantarray ? @text : $text[0];
127             }
128              
129             =head2 apropos
130              
131             =for ref
132              
133             Regex search PDL documentation database
134              
135             =for usage
136              
137             apropos 'text'
138              
139             =for example
140              
141             pdl> apropos 'pic'
142             PDL::IO::Pic P::IO::Pic Module: image I/O for PDL
143             grabpic3d P::G::TriD Grab a 3D image from the screen.
144             rim P::IO::Pic Read images in most formats, with improved RGB handling.
145             rpic P::IO::Pic Read images in many formats with automatic format detection.
146             rpiccan P::IO::Pic Test which image formats can be read/written
147             wim P::IO::Pic Write a pdl to an image file with selected type (or using filename extensions)
148             wmpeg P::IO::Pic Write an image sequence (a (3,x,y,n) byte pdl) as an animation.
149             wpic P::IO::Pic Write images in many formats with automatic format selection.
150             wpiccan P::IO::Pic Test which image formats can be read/written
151              
152             To find all the manuals that come with PDL, try
153              
154             apropos 'manual:'
155              
156             and to get quick info about PDL modules say
157              
158             apropos 'module:'
159              
160             You get more detailed info about a PDL function/module/manual
161             with the C function
162              
163             =cut
164              
165             sub aproposover {
166 0 0   0 0   die "Usage: aproposover \$funcname\n" if !@_;
167 0           my $func = shift;
168 0           $func =~ s:\/:\\\/:g;
169 0           search_docs("m/$func/",['Name','Ref','Module'],1);
170             }
171              
172             sub apropos {
173 0 0   0 1   die "Usage: apropos \$funcname\n" unless $#_>-1;
174 0           my $func = shift;
175 0           printmatch aproposover $func;
176 0           '';
177             }
178              
179             =head2 PDL::Doc::Perldl::search_docs
180              
181             =for ref
182              
183             Internal routine to search docs database and autoload files
184              
185             =cut
186              
187             sub search_docs {
188 0     0 1   my ($func,$types,$sortflag,$exact) = @_;
189 0           my @match;
190              
191 0   0       $PDL::onlinedoc //= PDL::Doc->new(FindStdFile());
192 0           @match = $PDL::onlinedoc->search($func,$types,$sortflag);
193 0           push(@match,find_autodoc( $func, $exact ) );
194              
195 0           @match;
196             }
197              
198              
199              
200             =head2 PDL::Doc::Perldl::finddoc
201              
202             =for ref
203              
204             Internal interface to the PDL documentation searcher
205              
206             =cut
207              
208             sub finddoc {
209 0     0 1   local $SIG{PIPE}= sub {}; # Prevent crashing if user exits the pager
        0      
210              
211 0 0         die 'Usage: doc $topic' unless $#_>-1;
212 0           my $topic = shift;
213              
214             # See if it matches a PDL function name
215              
216 0 0         my $subfield = $1
217             if( $topic =~ s/\[(\d*)\]$// ); #does it end with a number in square brackets?
218              
219 0           (my $t2 = $topic) =~ s/([^a-zA-Z0-9_])/\\$1/g; #$t2 is a copy of $topic with escaped non-word characters
220              
221 0           my @match = search_docs("m/^(PDL::)?".$t2."\$/",['Name'],0) ; #matches: ^PDL::topic$ or ^topic$
222              
223 0 0         unless(@match) {
224 0           print "No PDL docs for '$topic'. Using 'whatis'. (Try 'apropos $topic'?)\n\n";
225 0           whatis($topic);
226 0           return;
227             }
228              
229             # print out the matches
230 0           open my $out, "| pod2text | $PDL::Doc::pager";
231 0           binmode $out, ':encoding(UTF-8)';
232              
233 0 0         if($subfield) {
234 0 0         if($subfield <= @match) {
235 0           @match = ($match[$subfield-1]);
236 0           $subfield = 0;
237             } else {
238 0           print $out "\n\n=head1 PDL HELP: Ignoring out-of-range selector $subfield\n\n=head1\n\n=head1 --------------------------------\n\n";
239 0           $subfield = undef;
240             }
241             }
242              
243 0           my $num_pdl_pod_matches = scalar @match;
244 0           my $pdl_pod_matchnum = 0;
245              
246 0           while (@match) {
247 0           $pdl_pod_matchnum++;
248              
249 0 0 0       if ( @match > 1 and !$subfield and $pdl_pod_matchnum==1 ) {
      0        
250 0           print $out "\n\n=head1 MULTIPLE MATCHES FOR HELP TOPIC '$topic':\n\n=head1\n\n=over 3\n\n";
251 0           my $i=0;
252 0           for my $m ( @match ) {
253 0   0       printf $out "\n=item [%d]\t%-30s %s%s\n\n", ++$i, $m->[0], $m->[2]{Module} && "in ", $m->[2]{CustomFile} || $m->[2]{Module};
      0        
254             }
255 0           print $out "\n=back\n\n=head1\n\n To see item number \$n, use 'help ${topic}\[\$n\]'. \n\n=cut\n\n";
256             }
257              
258 0 0 0       if (@match > 0 and $num_pdl_pod_matches > 1) {
259 0           print $out "\n=head1 Displaying item $pdl_pod_matchnum:\n\n=head1 --------------------------------------\n\n=cut\n\n";
260             }
261              
262 0           my $m = shift @match;
263              
264 0           my $Ref = $m->[2]{Ref};
265 0 0 0       if ( $Ref && $Ref =~ /^(Module|Manual|Script): / ) {
266             # We've got a file name and we have to open it. With the relocatable db, we have to reconstitute the absolute pathname.
267 0           my $relfile = $m->[2]{File};
268 0           my $absfile = undef;
269 0           my @scnd = @{$PDL::onlinedoc->{Scanned}};
  0            
270 0           for my $dbf (@scnd) {
271 0           $dbf = Cwd::abs_path($dbf); # help Debian packaging
272 0           $dbf =~ s:\/[^\/]*$::; # Trim file name off the end of the database file to get just the directory
273 0           $dbf .= "/$relfile";
274 0 0         $absfile = $dbf if( -e $dbf );
275             }
276 0 0         unless ($absfile) {
277 0           die "Documentation error: couldn't find absolute path to $relfile\n";
278             }
279 0           open my $in, "<", $absfile;
280 0           print $out join("",<$in>);
281             } else {
282 0 0         if(defined $m->[2]{CustomFile}) {
283              
284 0           require Pod::Text;
285 0           my $parser = Pod::Text->new;
286 0           print $out "=head1 Autoload file \"".$m->[2]{CustomFile}."\"\n\n";
287 0           $parser->parse_from_file($m->[2]{CustomFile},$out);
288 0           print $out "\n\n=head2 Docs from\n\n".$m->[2]{CustomFile}."\n\n";
289              
290             } else {
291              
292 0           print $out "=encoding utf8\n\n=head1 Module ",$m->[2]{Module}, "\n\n";
293 0           $PDL::onlinedoc->funcdocs(@$m[0,1],$out);
294              
295             }
296              
297             }
298             }
299             }
300              
301              
302             =head2 find_autodoc
303              
304             =for ref
305              
306             Internal routine that finds and returns documentation in the
307             PDL::AutoLoader path, if it exists.
308              
309             You feed in a topic and it searches for the file "${topic}.pdl". If
310             that exists, then the filename gets returned in a match structure
311             appropriate for the rest of finddoc.
312              
313             =cut
314              
315             # Yuck. Sorry. At least it works. -CED
316              
317             sub find_autodoc {
318 0     0 1   my $topic = shift;
319 0           my $exact = shift;
320 0           my $matcher;
321             # Fix up regexps and exact matches for the special case of
322             # searching the autoload dirs...
323 0 0         if($exact) {
324 0           $topic =~ s/\(\)$//; # "func()" -> "func"
325 0 0         $topic .= ".pdl" unless $topic =~ m/\.pdl$/;
326             } else {
327              
328 0           $topic =~ s:([^\$])(.)$:$1\.\*\$$2:; # Include explicit ".*$" at end of
329             # vague matches -- so that we can
330             # make it a ".*\.pdl$" below.
331              
332 0           $topic =~ s:\$(.)$:\.pdl\$$1:; # Force ".pdl" at end of file match
333              
334 0           $matcher = eval "sub { ${topic}i && \$\_ };"; # Avoid multiple compiles
335             }
336              
337 0           my @out;
338              
339 0 0         return unless(@main::PDLLIB);
340 0 0         @main::PDLLIB_EXPANDED = PDL::AutoLoader::expand_path(@main::PDLLIB)
341             unless(@main::PDLLIB_EXPANDED);
342              
343 0           for my $dir(@main::PDLLIB_EXPANDED) {
344 0 0         if($exact) {
345 0           my $file = $dir . "/" . "$topic";
346 0 0         push(@out,
347             [$file, undef, {CustomFile => "$file", Module => "file '$file'"}]
348             )
349             if(-e $file);
350             } else {
351 0 0         opendir(FOO,$dir) || next;
352 0           my @dir = readdir(FOO);
353 0           closedir(FOO);
354 0           for my $file( grep( &$matcher, @dir ) ) {
355 0           push(@out,
356             [$file, undef, {CustomFile => "$dir/$file", Module => "file '$dir/$file'"}]
357             );
358             }
359              
360             }
361             }
362 0           @out;
363             }
364              
365              
366             =head2 usage, badinfo
367              
368             =for ref
369              
370             Prints usage information for a PDL function
371              
372             =for usage
373              
374             Usage: usage func
375              
376             =for example
377              
378             pdl> usage inner
379             inner P::Primitive Inner product over one dimension
380             Signature:
381             (a(n); b(n); [o]c())
382             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
383             float double ldouble cfloat cdouble cldouble)
384             Usage:
385             $c = inner($a, $b);
386             inner($a, $b, $c); # all arguments given
387             $c = $a->inner($b); # method call
388             $a->inner($b, $c);
389             Bad value support:
390             If "a() * b()" contains only bad data,
391             c() is set bad. Otherwise c() will have its bad flag cleared,
392             as it will not contain any bad values.
393              
394             =cut
395              
396             sub usage {
397 0 0   0 1   die 'Usage: usage $funcname' unless $#_>-1;
398 0           print usage_string(@_);
399 0           ''
400             }
401             *badinfo = \&usage;
402             sub usage_string {
403 0     0 0   my $func = shift;
404 0           my $str = "";
405 0 0         return "no match\n" unless
406             my @match = search_docs("m/^(PDL::)?$func\$|\:\:$func\$/",['Name']);
407 0           my $count = @match;
408             #this sorts by namespace depth by counting colons in the name.
409             #PDL::Ufunc::max comes before PDL::GSL::RNG::max, for example.
410 0           foreach my $m (sort { scalar(()=$a->[1]=~/\:/g) <=> scalar(()=$b->[1]=~/\:/g) } @match) {
  0            
411 0           $str .= "\n" . format_ref( $m );
412 0           my ($name,$module,$hash) = @$m;
413 0 0         die "No usage info found for $func\n" if !grep defined, @$hash{qw(Example Sig Usage)};
414 0           for (grep $hash->{$_->[0]},
415             ['Sig','Signature'],['Usage','Usage'],['Opt','Options'],
416             ['Example','Example'],['Bad','Bad value support'],
417             ) {
418 0           $str .= " $_->[1]:\n".allindent($hash->{$_->[0]},4)."\n";
419             }
420 0 0         $str .= '='x20 unless 1==$count--;
421             }
422 0           $str;
423             }
424              
425             =head2 sig
426              
427             =for ref
428              
429             prints signature of PDL function
430              
431             =for usage
432              
433             sig 'func'
434              
435             The signature is the normal dimensionality of the
436             function's arguments. Calling with different dimensions
437             doesn't break -- it causes broadcasting. See L and L for details.
438              
439             =for example
440              
441             pdl> sig 'outer'
442             Signature: outer(a(n); b(m); [o]c(n,m))
443              
444             =cut
445              
446             sub sig {
447 0 0   0 1   die "Usage: sig \$funcname\n" unless $#_>-1;
448 0           my $func = shift;
449 0           my @match = search_docs("m/^(PDL::)?$func\$|\:\:$func\$/",['Name']);
450 0           my $count = @match;
451 0 0         unless (@match) { print "\n no match\n" } else {
  0            
452 0           foreach my $m(sort { scalar(()=$a->[1]=~/\:/g) <=> scalar(()=$b->[1]=~/\:/g) } @match){
  0            
453 0           my ($name,$module,$hash) = @{$m};
  0            
454 0 0         die "No signature info found for $func\n" if !defined $hash->{Sig};
455 0 0         print " Signature: $name($hash->{Sig})\n" if defined $hash->{Sig};
456 0 0         print '='x20 unless 1==$count--;
457             }
458             }
459             }
460              
461             sub allindent {
462 0     0 0   my ($txt,$n) = @_;
463 0           my ($ntxt,$tspc) = ($txt,' 'x8);
464 0           $ntxt =~ s/^\s*$//mg;
465 0           $ntxt =~ s/\t/$tspc/g;
466 0           my $minspc = length $txt;
467 0 0         for (split '\n', $txt) { if (/^(\s*)/)
  0            
468 0 0         { $minspc = length $1 if length $1 < $minspc } }
469 0           $n -= $minspc;
470 0           $tspc = ' 'x abs($n);
471 0 0         $ntxt =~ s/^/$tspc/mg if $n > 0;
472 0           $ntxt;
473             }
474              
475              
476             =head2 whatis
477              
478             =for ref
479              
480             Describe a perl and/or PDL variable or expression. Useful for
481             determining the type of an expression, identifying the keys in a hash
482             or a data structure, or examining WTF an unknown object is.
483              
484             =for usage
485              
486             Usage: whatis $var
487             whatis
488              
489             =cut
490              
491             sub whatis {
492 0     0 1   my $topic;
493              
494 0 0         if(@_ > 1) {
495 0           whatis_r('',0,[@_]);
496             } else {
497 0           whatis_r('',0,shift);
498             }
499             }
500              
501             $PDL::Doc::Perldl::max_strlen = 55;
502             $PDL::Doc::Perldl::max_arraylen = 1;
503             $PDL::Doc::Perldl::max_keylen = 8;
504             $PDL::Doc::Perldl::array_indent=5;
505             $PDL::Doc::Perldl::hash_indent=3;
506              
507             sub whatis_r {
508 0     0 0   my $prefix = shift;
509 0           my $indent = shift;
510 0           my $x = shift;
511              
512 0 0         unless(defined $x) {
513 0           print $prefix,"\n";
514 0           return;
515             }
516              
517 0 0         unless(ref $x) {
518 0   0       print "${prefix}'".
519             substr($x,0,$PDL::Doc::Perldl::max_strlen).
520             "'".((length $x > $PDL::Doc::Perldl::max_strlen) && '...').
521             "\n";
522 0           return;
523             }
524              
525 0 0         if(ref $x eq 'ARRAY') {
526 0           print "${prefix}Array (".scalar(@$x)." elements):\n";
527              
528 0           my($el);
529 0           for $el(0..$#$x) {
530 0           my $pre = sprintf("%s %2d: "," "x$indent,$el);
531 0           whatis_r($pre,$indent + $PDL::Doc::Perldl::array_indent, $x->[$el]);
532 0 0         last if($el == $PDL::Doc::Perldl::max_arraylen);
533             }
534 0 0         printf "%s ... \n"," " x $indent
535             if($#$x > $PDL::Doc::Perldl::max_arraylen);
536              
537 0           return;
538             }
539              
540 0 0         if(ref $x eq 'HASH') {
541 0           print "${prefix}Hash (".scalar(keys %$x)." elements)\n";
542 0           my $key;
543 0           for $key(sort keys %$x) {
544 0           my $pre = " " x $indent .
545             " $key: " .
546             (" "x($PDL::Doc::Perldl::max_keylen - length($key))) ;
547              
548 0           whatis_r($pre,$indent + $PDL::Doc::Perldl::hash_indent, $x->{$key});
549             }
550 0           return;
551             }
552              
553 0 0         if(ref $x eq 'CODE') {
554 0           print "${prefix}Perl CODE ref\n";
555 0           return;
556             }
557              
558 0 0 0       if(ref $x eq 'SCALAR' || ref $x eq 'REF') {
559 0           whatis_r($prefix." Ref -> ",$indent+8,$$x);
560 0           return;
561             }
562              
563 0 0         if(UNIVERSAL::can($x,'px')) {
564 0           my $y;
565 0           local $PDL::debug = 1;
566              
567 0 0 0       $y = ( (UNIVERSAL::isa($x,'PDL') && $x->nelem < 5 && $x->ndims < 2)
568             ?
569             ": $x" :
570             ": *****"
571             );
572              
573 0           $x->px($prefix.(ref $x)." %7T (%D) ".$y);
574              
575             } else {
576              
577 0           print "${prefix}Object: ".ref($x)."\n";
578              
579             }
580             }
581              
582             =head2 help
583              
584             =for ref
585              
586             print documentation about a PDL function or module or show a PDL manual
587              
588             In the case of multiple matches, the first command found is printed out,
589             and the remaining commands listed, along with the names of their modules.
590              
591              
592             =for usage
593              
594             Usage: help 'func'
595              
596             =for example
597              
598             pdl> help 'PDL::Tutorials' # show the guide to PDL tutorials
599             pdl> help 'PDL::Slices' # show the docs in the PDL::Slices module
600             pdl> help 'slice' # show docs on the 'slice' function
601              
602             =cut
603              
604             sub help {
605 0 0   0 1   if (@_) {
606 0           require PDL::Dbg;
607 0           my $topic = shift;
608 0 0 0       if (PDL::Core::blessed($topic) && $topic->can('px')) {
609 0           local $PDL::debug = 1;
610 0           $topic->px('This variable is');
611             } else {
612 0 0         $topic = 'PDL::Doc::Perldl' if $topic =~ /^\s*help\s*$/i;
613 0 0         if ($topic =~ /^\s*vars\s*$/i) {
614 0           PDL->px((caller)[0]);
615             } else {
616 0           finddoc($topic);
617             }
618             }
619             } else {
620 0           print <<'EOH';
621              
622             The following commands support online help in the perldl shell:
623              
624             help 'thing' -- print docs on 'thing' (func, module, manual, autoload-file)
625             help vars -- print information about all current ndarrays
626              
627             whatis -- Describe the type and structure of an expression or ndarray.
628             apropos 'word' -- search for keywords/function names
629             usage -- print usage information for a given PDL function
630             including support for bad values
631             sig -- print signature of PDL function
632              
633             ('?' is an alias for 'help'; '??' is an alias for 'apropos'.)
634              
635             Quick start:
636             apropos 'manual:' -- Find all the manual documents
637             apropos 'module:' -- Quick summary of all PDL modules
638             help 'help' -- details about PDL help system
639             help 'perldl' -- help about this shell
640              
641             EOH
642             }
643 0           ''
644             }
645              
646             1; # OK