our
@ISA
=
qw(Exporter)
;
our
@EXPORT
=
qw( apropos aproposover usage help sig badinfo whatis )
;
$PDL::onlinedoc
= PDL::Doc->new(FindStdFile());
sub
FindStdFile {
my
(
$f
) = PDL::Doc::_find_inc([
qw(PDL pdldoc.db)
], 0);
warn
(
"Unable to find PDL/pdldoc.db in "
.
join
(
":"
,
@INC
).
"\n"
),
return
if
!
defined
$f
;
print
"Found docs database $f\n"
if
$PDL::verbose
;
print
"Type 'help' for online help\n"
if
$PDL::verbose
;
return
$f
;
}
sub
screen_width {
local
$@;
eval
{
( Term::ReadKey::GetTerminalSize(\
*STDOUT
) )[0];
} // 72;
}
sub
printmatch {
my
@match
=
@_
;
if
(
@match
) {
foreach
my
$t
( format_ref(
@_
) ) {
print
$t
; }
}
else
{
print
"no match\n\n"
;
}
}
sub
shortmod {
my
$module
=
shift
;
$module
=~ s/::$//;
unless
(
$PERLDL::long_mod_names
&&
$PERLDL::long_mod_names
){
$module
=~ s/^PDL::/P::/;
$module
=~ s/^P::Graphics::/P::G::/;
}
return
$module
;
}
sub
format_ref {
my
@match
=
@_
;
my
@text
= ();
my
@module_shorthands
=
map
{ shortmod(
$_
->[1]) }
@match
;
my
$max_mod_length
= -1;
map
{
$max_mod_length
=
length
if
(
length
>
$max_mod_length
) }
@module_shorthands
;
my
$width
= screen_width()-17-1-
$max_mod_length
;
my
$parser
= Pod::PlainText->new(
width
=>
$width
,
indent
=> 0,
sentence
=> 0 );
for
my
$m
(
@match
) {
my
$ref
=
$m
->[2]{Ref} ||
( (
defined
$m
->[2]{CustomFile})
?
"[No ref avail. for `"
.
$m
->[2]{CustomFile}.
"']"
:
"[No reference available]"
);
my
$name
=
$m
->[0];
my
$module
= shortmod(
$m
->[1]);
$ref
=
$parser
->interpolate(
$ref
);
$ref
=
$parser
->reformat(
$ref
);
$ref
=~ s/\n*$//;
$ref
=~ s/\n/
"\n "
.
' '
x(
$max_mod_length
+2)/eg;
$ref
=~ s/^\s*//;
if
(
length
(
$name
) > 15 ) {
push
@text
,
sprintf
"%s ...\n "
.
' '
x15 .
"%-*s %s\n"
,
$name
,
$max_mod_length
,
$module
,
$ref
;
}
else
{
push
@text
,
sprintf
"%-15s %-*s %s\n"
,
$name
,
$max_mod_length
,
$module
,
$ref
;
}
}
return
wantarray
?
@text
:
$text
[0];
}
sub
aproposover {
die
"Usage: aproposover \$funcname\n"
if
!
@_
;
die
"no online doc database"
unless
defined
$PDL::onlinedoc
;
my
$func
=
shift
;
$func
=~ s:\/:\\\/:g;
search_docs(
"m/$func/"
,[
'Name'
,
'Ref'
,
'Module'
],1);
}
sub
apropos {
die
"Usage: apropos \$funcname\n"
unless
$#_
>-1;
die
"no online doc database"
unless
defined
$PDL::onlinedoc
;
my
$func
=
shift
;
printmatch aproposover
$func
;
}
sub
search_docs {
my
(
$func
,
$types
,
$sortflag
,
$exact
) =
@_
;
my
@match
;
@match
=
$PDL::onlinedoc
->search(
$func
,
$types
,
$sortflag
);
push
(
@match
,find_autodoc(
$func
,
$exact
) );
@match
;
}
sub
finddoc {
local
$SIG
{PIPE}=
sub
{};
die
'Usage: doc $topic'
unless
$#_
>-1;
die
"no online doc database"
unless
defined
$PDL::onlinedoc
;
my
$topic
=
shift
;
my
$subfield
= $1
if
(
$topic
=~ s/\[(\d*)\]$// );
(
my
$t2
=
$topic
) =~ s/([^a-zA-Z0-9_])/\\$1/g;
my
@match
= search_docs(
"m/^(PDL::)?"
.
$t2
.
"\$/"
,[
'Name'
],0) ;
unless
(
@match
) {
print
"No PDL docs for '$topic'. Using 'whatis'. (Try 'apropos $topic'?)\n\n"
;
whatis(
$topic
);
return
;
}
open
my
$out
,
"| pod2text | $PDL::Doc::pager"
;
if
(
$subfield
) {
if
(
$subfield
<=
@match
) {
@match
= (
$match
[
$subfield
-1]);
$subfield
= 0;
}
else
{
print
$out
"\n\n=head1 PDL HELP: Ignoring out-of-range selector $subfield\n\n=head1\n\n=head1 --------------------------------\n\n"
;
$subfield
=
undef
;
}
}
my
$num_pdl_pod_matches
=
scalar
@match
;
my
$pdl_pod_matchnum
= 0;
while
(
@match
) {
$pdl_pod_matchnum
++;
if
(
@match
> 1 and !
$subfield
and
$pdl_pod_matchnum
==1 ) {
print
$out
"\n\n=head1 MULTIPLE MATCHES FOR HELP TOPIC '$topic':\n\n=head1\n\n=over 3\n\n"
;
my
$i
=0;
for
my
$m
(
@match
) {
printf
$out
"\n=item [%d]\t%-30s %s%s\n\n"
, ++
$i
,
$m
->[0],
$m
->[2]{Module} &&
"in "
,
$m
->[2]{CustomFile} ||
$m
->[2]{Module};
}
print
$out
"\n=back\n\n=head1\n\n To see item number \$n, use 'help ${topic}\[\$n\]'. \n\n=cut\n\n"
;
}
if
(
@match
> 0 and
$num_pdl_pod_matches
> 1) {
print
$out
"\n=head1 Displaying item $pdl_pod_matchnum:\n\n=head1 --------------------------------------\n\n=cut\n\n"
;
}
my
$m
=
shift
@match
;
my
$Ref
=
$m
->[2]{Ref};
if
(
$Ref
&&
$Ref
=~ /^(Module|Manual|Script): / ) {
my
$relfile
=
$m
->[2]{File};
my
$absfile
=
undef
;
my
@scnd
= @{
$PDL::onlinedoc
->{Scanned}};
for
my
$dbf
(
@scnd
) {
$dbf
= Cwd::abs_path(
$dbf
);
$dbf
=~ s:\/[^\/]*$::;
$dbf
.=
"/$relfile"
;
$absfile
=
$dbf
if
( -e
$dbf
);
}
unless
(
$absfile
) {
die
"Documentation error: couldn't find absolute path to $relfile\n"
;
}
open
my
$in
,
"<"
,
$absfile
;
print
$out
join
(
""
,<
$in
>);
}
else
{
if
(
defined
$m
->[2]{CustomFile}) {
my
$parser
= Pod::Select->new;
print
$out
"=head1 Autoload file \""
.
$m
->[2]{CustomFile}.
"\"\n\n"
;
$parser
->parse_from_file(
$m
->[2]{CustomFile},
$out
);
print
$out
"\n\n=head2 Docs from\n\n"
.
$m
->[2]{CustomFile}.
"\n\n"
;
}
else
{
print
$out
"=head1 Module "
,
$m
->[2]{Module},
"\n\n"
;
$PDL::onlinedoc
->funcdocs(
$m
->[0],
$m
->[1],
$out
);
}
}
}
}
sub
find_autodoc {
my
$topic
=
shift
;
my
$exact
=
shift
;
my
$matcher
;
if
(
$exact
) {
$topic
=~ s/\(\)$//;
$topic
.=
".pdl"
unless
$topic
=~ m/\.pdl$/;
}
else
{
$topic
=~ s:([^\$])(.)$:$1\.\*\$$2:;
$topic
=~ s:\$(.)$:\.pdl\$$1:;
$matcher
=
eval
"sub { ${topic}i && \$\_ };"
;
}
my
@out
;
return
unless
(
@main::PDLLIB
);
@main::PDLLIB_EXPANDED
= PDL::AutoLoader::expand_path(
@main::PDLLIB
)
unless
(
@main::PDLLIB_EXPANDED
);
for
my
$dir
(
@main::PDLLIB_EXPANDED
) {
if
(
$exact
) {
my
$file
=
$dir
.
"/"
.
"$topic"
;
push
(
@out
,
[
$file
,
undef
, {
CustomFile
=>
"$file"
,
Module
=>
"file '$file'"
}]
)
if
(-e
$file
);
}
else
{
opendir
(FOO,
$dir
) ||
next
;
my
@dir
=
readdir
(FOO);
closedir
(FOO);
for
my
$file
(
grep
(
&$matcher
,
@dir
) ) {
push
(
@out
,
[
$file
,
undef
, {
CustomFile
=>
"$dir/$file"
,
Module
=>
"file '$dir/$file'"
}]
);
}
}
}
@out
;
}
sub
usage {
die
'Usage: usage $funcname'
unless
$#_
>-1;
die
"no online doc database"
unless
defined
$PDL::onlinedoc
;
print
usage_string(
@_
);
}
sub
usage_string{
my
$func
=
shift
;
my
$str
=
""
;
my
@match
= search_docs(
"m/^(PDL::)?$func\$|\:\:$func\$/"
,[
'Name'
]);
my
$count
=
@match
;
unless
(
$count
) {
$str
=
"\n no match\n"
}
else
{
foreach
my
$m
(
sort
{
scalar
(()=
$a
->[1]=~/\:/g) <=>
scalar
(()=
$b
->[1]=~/\:/g) }
@match
){
$str
.=
"\n"
. format_ref(
$m
);
my
(
$name
,
$module
,
$hash
) = @{
$m
};
$str
.=
"\n"
;
die
"No usage info found for $func\n"
if
(!
defined
$hash
->{Example} && !
defined
$hash
->{Sig} &&
!
defined
$hash
->{Usage});
$str
.=
" Signature: $name($hash->{Sig})\n\n"
if
defined
$hash
->{Sig};
for
([
'Usage'
,
'Usage'
],[
'Opt'
,
'Options'
],[
'Example'
,
'Example'
]) {
$str
.=
" $_->[1]:\n"
.
&allindent
(
$hash
->{
$_
->[0]},10).
"\n"
if
defined
$hash
->{
$_
->[0]};
}
$str
.=
'='
x20
unless
1==
$count
--;
}
}
return
$str
;
}
sub
sig {
die
"Usage: sig \$funcname\n"
unless
$#_
>-1;
die
"no online doc database"
unless
defined
$PDL::onlinedoc
;
my
$func
=
shift
;
my
@match
= search_docs(
"m/^(PDL::)?$func\$|\:\:$func\$/"
,[
'Name'
]);
my
$count
=
@match
;
unless
(
@match
) {
print
"\n no match\n"
}
else
{
foreach
my
$m
(
sort
{
scalar
(()=
$a
->[1]=~/\:/g) <=>
scalar
(()=
$b
->[1]=~/\:/g) }
@match
){
my
(
$name
,
$module
,
$hash
) = @{
$m
};
die
"No signature info found for $func\n"
if
!
defined
$hash
->{Sig};
print
" Signature: $name($hash->{Sig})\n"
if
defined
$hash
->{Sig};
print
'='
x20
unless
1==
$count
--;
}
}
}
sub
allindent {
my
(
$txt
,
$n
) =
@_
;
my
(
$ntxt
,
$tspc
) = (
$txt
,
' '
x8);
$ntxt
=~ s/^\s*$//mg;
$ntxt
=~ s/\t/
$tspc
/g;
my
$minspc
=
length
$txt
;
for
(
split
'\n'
,
$txt
) {
if
(/^(\s*)/)
{
$minspc
=
length
$1
if
length
$1 <
$minspc
} }
$n
-=
$minspc
;
$tspc
=
' '
x
abs
(
$n
);
$ntxt
=~ s/^/
$tspc
/mg
if
$n
> 0;
return
$ntxt
;
}
sub
whatis {
my
$topic
;
if
(
@_
> 1) {
whatis_r(
''
,0,[
@_
]);
}
else
{
whatis_r(
''
,0,
shift
);
}
}
$PDL::Doc::Perldl::max_strlen
= 55;
$PDL::Doc::Perldl::max_arraylen
= 1;
$PDL::Doc::Perldl::max_keylen
= 8;
$PDL::Doc::Perldl::array_indent
=5;
$PDL::Doc::Perldl::hash_indent
=3;
sub
whatis_r {
my
$prefix
=
shift
;
my
$indent
=
shift
;
my
$x
=
shift
;
unless
(
defined
$x
) {
print
$prefix
,
"<undef>\n"
;
return
;
}
unless
(
ref
$x
) {
print
"${prefix}'"
.
substr
(
$x
,0,
$PDL::Doc::Perldl::max_strlen
).
"'"
.((
length
$x
>
$PDL::Doc::Perldl::max_strlen
) && '...').
"\n"
;
return
;
}
if
(
ref
$x
eq
'ARRAY'
) {
print
"${prefix}Array ("
.
scalar
(
@$x
).
" elements):\n"
;
my
(
$el
);
for
$el
(0..
$#$x
) {
my
$pre
=
sprintf
(
"%s %2d: "
,
" "
x
$indent
,
$el
);
whatis_r(
$pre
,
$indent
+
$PDL::Doc::Perldl::array_indent
,
$x
->[
$el
]);
last
if
(
$el
==
$PDL::Doc::Perldl::max_arraylen
);
}
printf
"%s ... \n"
,
" "
x
$indent
if
(
$#$x
>
$PDL::Doc::Perldl::max_arraylen
);
return
;
}
if
(
ref
$x
eq
'HASH'
) {
print
"${prefix}Hash ("
.
scalar
(
keys
%$x
).
" elements)\n"
;
my
$key
;
for
$key
(
sort
keys
%$x
) {
my
$pre
=
" "
x
$indent
.
" $key: "
.
(
" "
x(
$PDL::Doc::Perldl::max_keylen
-
length
(
$key
))) ;
whatis_r(
$pre
,
$indent
+
$PDL::Doc::Perldl::hash_indent
,
$x
->{
$key
});
}
return
;
}
if
(
ref
$x
eq
'CODE'
) {
print
"${prefix}Perl CODE ref\n"
;
return
;
}
if
(
ref
$x
eq
'SCALAR'
||
ref
$x
eq
'REF'
) {
whatis_r(
$prefix
.
" Ref -> "
,
$indent
+8,
$$x
);
return
;
}
if
(UNIVERSAL::can(
$x
,
'px'
)) {
my
$y
;
local
$PDL::debug
= 1;
$y
= ( (UNIVERSAL::isa(
$x
,
'PDL'
) &&
$x
->nelem < 5 &&
$x
->ndims < 2)
?
": $x"
:
": *****"
);
$x
->px(
$prefix
.(
ref
$x
).
" %7T (%D) "
.
$y
);
}
else
{
print
"${prefix}Object: "
.
ref
(
$x
).
"\n"
;
}
}
sub
help {
if
(
@_
) {
my
$topic
=
shift
;
if
(PDL::Core::blessed(
$topic
) &&
$topic
->can(
'px'
)) {
local
$PDL::debug
= 1;
$topic
->px(
'This variable is'
);
}
else
{
$topic
=
'PDL::Doc::Perldl'
if
$topic
=~ /^\s
*help
\s*$/i;
if
(
$topic
=~ /^\s
*vars
\s*$/i) {
PDL->px((
caller
)[0]);
}
else
{
finddoc(
$topic
);
}
}
}
else
{
print
<<'EOH';
The following commands support online help in the perldl shell:
help 'thing' -- print docs on 'thing' (func, module, manual, autoload-file)
help vars -- print information about all current ndarrays
whatis <expr> -- Describe the type and structure of an expression or ndarray.
apropos 'word' -- search for keywords/function names
usage -- print usage information for a given PDL function
sig -- print signature of PDL function
badinfo -- information on the support for bad values
('?' is an alias for 'help'; '??' is an alias for 'apropos'.)
Quick start:
apropos 'manual:' -- Find all the manual documents
apropos 'module:' -- Quick summary of all PDL modules
help 'help' -- details about PDL help system
help 'perldl' -- help about this shell
EOH
}
''
}
sub
badinfo {
my
$func
=
shift
;
die
"Usage: badinfo \$funcname\n"
unless
defined
$func
;
die
"no online doc database"
unless
defined
$PDL::onlinedoc
;
local
$SIG
{PIPE}=
sub
{};
my
@match
= search_docs(
"m/^(PDL::)?$func\$|\:\:$func\$/"
,[
'Name'
]);
my
$count
=
@match
;
if
(
$count
) {
my
(
$pagerstr
,
$noinfostr
) = (
''
,
''
);
foreach
my
$m
(
@match
) {
my
(
$name
,
$module
,
$hash
) = @{
$m
};
my
$info
=
$hash
->{Bad};
if
(
defined
$info
) {
$name
=~s/^(.*)\:\:(\w*)$/$2/;
$pagerstr
.=
"=head1 Bad value support for $name (in module $module)\n\n$info\n"
;
}
else
{
$noinfostr
.=
"\n No information on bad-value support found for $func (in module $module)\n"
;
}
}
if
(
$pagerstr
){
open
my
$out
,
"| pod2text | $PDL::Doc::pager"
;
print
$out
$pagerstr
,
$noinfostr
;
}
else
{
print
$noinfostr
;
}
}
else
{
print
"\n no match\n"
;
}
}
1;