package PBS::Documentation::Indexer ;
use strict ;
use warnings ;
require Exporter ;
use AutoLoader qw(AUTOLOAD) ;
use Pod::Parser;
use PBS::ProgressBar ;
our @ISA = qw(Exporter Pod::Parser) ;
our %EXPORT_TAGS = ('all' => [ qw() ]) ;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ) ;
our @EXPORT = qw() ;
our $VERSION = '0.02' ;
#-------------------------------------------------------------------------------------------------------------------------------
sub GetIndex
{
return($_[0]->{__INDEX}) ;
}
#-------------------------------------------------------------------------------------------------------------------------------
sub GetCapturedText
{
return($_[0]->{__CAPTURED_TEXT}) ;
}
#-------------------------------------------------------------------------------------------------------------------------------
sub command
{
my ($parser, $command, $paragraph, $line_num, $pod_para) = @_ ;
my $ptree = $parser->parse_text({}, $paragraph) ;
my $file = $parser->input_file() ;
my $original_paragraph = $paragraph ;
$paragraph =~ s/\s+$// ;
$parser->{__CAPTURE_LEVEL} = 0 unless defined $parser->{__CAPTURE_LEVEL} ;
if($command =~ /^head([0-9]+)/)
{
my $current_level = $1 ;
$parser->{__CAPTURE_LEVEL} = 0 if $parser->{__CAPTURE_LEVEL} >= $current_level ;
$parser->{__CAPTURED_TEXT} .= "=$command $original_paragraph" if $parser->{__CAPTURE_LEVEL} ;
if(defined $parser->{__CAPTURE_REGEX} && $paragraph =~ $parser->{__CAPTURE_REGEX})
{
$parser->{__CAPTURE_LEVEL} = $current_level ;
$parser->{__CAPTURED_TEXT} .= "=$command $original_paragraph" ;
}
push @{$parser->{__INDEX}{$file}}, {TEXT => $paragraph, LEVEL => $current_level} ;
}
else
{
$parser->{__CAPTURED_TEXT} .= "=$command $original_paragraph" if($parser->{__CAPTURE_LEVEL}) ;
}
}
#-------------------------------------------------------------------------------------------------------------------------------
sub verbatim
{
my ($parser, $paragraph) = @_;
$parser->{__CAPTURED_TEXT} .= $paragraph if($parser->{__CAPTURE_LEVEL}) ;
}
#-------------------------------------------------------------------------------------------------------------------------------
sub textblock
{
my ($parser, $paragraph) = @_;
$parser->{__CAPTURED_TEXT} .= $paragraph if($parser->{__CAPTURE_LEVEL}) ;
}
#-------------------------------------------------------------------------------------------------------------------------------
#~ sub interior_sequence
#~ {
#~ my ($parser, $paragraph) = @_;
#~ $parser->{__CAPTURED_TEXT} .= $paragraph if($parser->{__CAPTURE_LEVEL}) ;
#~ }
#-------------------------------------------------------------------------------------------------------------------------------
1 ;
#-------------------------------------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------------------------------------
package PBS::Documentation ;
use strict ;
use warnings ;
require Exporter ;
use AutoLoader qw(AUTOLOAD) ;
our @ISA = qw(Exporter) ;
our %EXPORT_TAGS =
(
'all' => [ qw() ]
) ;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ) ;
our @EXPORT = qw() ;
our $VERSION = '0.02' ;
use PBS::Output ;
use Term::ReadLine ;
use IO::String ;
use Pod::Text; ;
use Pod::Simple::Search ;
#-------------------------------------------------------------------------------
our $use_pager = 0 ;
sub DisplayPodDocumentation
{
my $pbs_config = shift ;
my $command = shift || '' ;
eval { use Pod::Simple::Search ;} ;
die $@ if $@ ;
my @extra_paths = (@{$pbs_config->{LIB_PATH}}, @{$pbs_config->{PLUGIN_PATH}}) ;
my ($index_by_file, $index_by_section) = Generateindexes(@extra_paths) ;
my $terminal = new Term::ReadLine 'Pbs documentation search' ;
$terminal->addhistory('index') ;
do
{
chomp $command ;
return if $command =~ /^q$/i || $command =~ /^quit$/i ;
$terminal->addhistory($command) if $command =~ /\S/;
if($command =~ s/^pager\s+// && defined $ENV{PAGER})
{
$use_pager = 1 ;
}
else
{
$use_pager = 0 ;
}
for ($command)
{
/^(?:p|print)\s+(.*)\s*/ and do
{
my $module = $1 || '' ;
if(defined $module && $module ne '')
{
PrintModule($module, @extra_paths) ;
last ;
}
} ;
/^(?:o|outline)\s+(.*)\s*/ and do
{
my $module = $1 || '' ;
if(defined $module && $module ne '')
{
OutlineModule($module, @extra_paths) ;
last ;
}
} ;
(/^(?:i|index)\s+(.*)\s*/ || /^(?:i|index)\s*/) and do
{
my $regex = $1 || '.' ;
print join("\n", grep {/$regex/i} sort {uc($a) cmp uc($b)} keys %$index_by_section) . "\n" ;
last ;
} ;
/^(?:s|search)\s+(.*)\s*/ and do
{
my $section = $1 || '.' ;
SearchPodDocumentation($section, $index_by_section) ;
last ;
} ;
(/^h\s*$/ || /^help\s*$/) and do
{
DisplayDocumentationHelp() ;
last ;
}
}
}
while(defined ($command = $terminal->readline('Documentation command > ')))
}
#-------------------------------------------------------------------------------
sub SearchPodDocumentation
{
my ($section, $index) = @_ ;
my (@found_at, $file, $section_full_name) ;
for my $matching_section (keys %{$index})
{
if($matching_section=~ /$section/i)
{
for my $section_entry (@{$index->{$matching_section}})
{
push @found_at, {SECTION => $matching_section, FILE => $section_entry->{FILE}} ;
#~ print "Found '$matching_section' in file '$section_entry->{FILE}'.\n" ;
$file = $section_entry->{FILE} ;
$section_full_name = $matching_section ;
}
}
}
@found_at = sort {uc($a->{SECTION}) cmp uc($b->{SECTION})} @found_at ;
my $selected_entry = SelectEntry(map{"'$_->{SECTION}' => $_->{FILE}"} @found_at) ;
if(defined $selected_entry)
{
$section_full_name = $found_at[$selected_entry]{SECTION} ;
$file = $found_at[$selected_entry]{FILE} ;
}
else
{
undef $section ;
undef $file ;
}
if(defined $file)
{
my $parser = PBS::Documentation::Indexer->new(__CAPTURE_REGEX => $section_full_name);
$parser->parse_from_file($file) ;
my $fh = IO::String->new($parser->GetCapturedText() || 'none!') ;
if($use_pager)
{
open my $out, "| $ENV{PAGER}" or die "Can't redirect to system pager: $!\n";
print $out "In file '$file':\n" ;
Pod::Text->new (alt => 1, sentence => 0, width => 78)->parse_from_filehandle($fh, $out) ;
}
else
{
my $textified_pod = IO::String->new() ;
Pod::Text->new (alt => 1, sentence => 0, width => 78)->parse_from_filehandle($fh, $textified_pod) ;
print ("In file '$file':\n", ${$textified_pod->string_ref()}) ;
}
}
else
{
print "No documentation found for section '$section'.\n" if defined $section ;
}
}
#-------------------------------------------------------------------------------
sub OutlineModule
{
my $module_regex = shift ;
my @extra_paths = @_ ;
my $parser = PBS::Documentation::Indexer->new();
my @files = values %{Pod::Simple::Search->new->shadows(1)->limit_glob($module_regex)->survey()} ;
push @files, grep {/$module_regex/} values %{Pod::Simple::Search->new->shadows(1)->inc(0)->survey(@extra_paths)} ;
@files = sort @files ;
my $selected_entry = SelectEntry(@files) ;
if(defined $selected_entry)
{
my $file = $files[$selected_entry] ;
print "In '$file'\n" ;
eval {$parser->parse_from_file($file) ; } ;
print "Error in file'$file': $@" if $@ ;
my ($index_by_file) = $parser->GetIndex() ;
for my $level_data (@{$index_by_file->{$file}})
{
print ' ' x ($level_data->{LEVEL} - 1) ;
print $level_data->{TEXT} . "\n" ;
}
}
}
#-------------------------------------------------------------------------------
sub PrintModule
{
my $module_regex = shift ;
my @extra_paths = @_ ;
my @files = values %{Pod::Simple::Search->new->shadows(1)->limit_glob($module_regex)->survey()} ;
push @files, grep {/$module_regex/} values %{Pod::Simple::Search->new->shadows(1)->inc(0)->survey(@extra_paths)} ;
@files = sort @files ;
my $selected_entry = SelectEntry(@files) ;
if(defined $selected_entry)
{
my $file = $files[$selected_entry] ;
open my $fh, '<', $file or die "Can't open '$file': $!\n";
if($use_pager)
{
open my $out, "| $ENV{PAGER}" or die "Can't redirect to system pager$!\n";
print $out "In file '$file':\n" ;
Pod::Text->new (alt => 1, sentence => 0, width => 78)->parse_from_filehandle($fh, $out) ;
}
else
{
open my $out, '>', \my $textified_pod or die "Can't redirect to scalar output: $!\n";
Pod::Text->new (alt => 1, sentence => 0, width => 78)->parse_from_filehandle($fh, $out) ;
print ("In file '$file':\n", $textified_pod) ;
}
}
}
#-------------------------------------------------------------------------------
sub DisplayDocumentationHelp
{
print <<EOH ;
[pager] s[earch] section => search for a section.
'section_name' is a perl regex.
i[ndex] [regex] => display an index of all the PBS help sections.
'regex' is a perl regex.
o[utline] module => prints an outline of a module documentation.
ex: 'o PBS::Build::Forked'.
[pager] p[rint] module => prints a module pod.
q[uit] => to quit.
* optional 'pager' redirects output to the system pager.
EOH
}
#-------------------------------------------------------------------------------
sub SelectEntry
{
my @entries = @_ ;
my $selection ;
my $terminal ;
{
local $SIG{'__WARN__'} = sub {} ;
$terminal = new Term::ReadLine 'Pbs documentation search' ;
}
if(@entries > 1)
{
my $index = 0 ;
for my $entry (@entries)
{
printf "%3d: $entry\n", $index ;
$index++ ;
}
if(defined ($selection = $terminal->readline('Select section > ')))
{
chomp $selection ;
if($selection =~ /^[0-9]+$/)
{
if($selection < @entries)
{
return($selection) ;
}
else
{
return(undef) ;
}
}
else
{
return(undef) ;
}
}
else
{
return(undef) ;
}
}
else
{
if(@entries)
{
return(0) ;
}
else
{
return(undef) ;
}
}
}
#-------------------------------------------------------------------------------
sub Generateindexes
{
my @extra_paths = @_ ;
my $parser = PBS::Documentation::Indexer->new();
print "Searching and indexing pod sections.\n" ;
my (undef, $path2name_pbs) = Pod::Simple::Search->new->limit_glob('PBS::*')->survey() ;
my (undef, $path2name_extra) = Pod::Simple::Search->new->shadows(1)->inc(0)->survey(@extra_paths) ;
my @files = (keys %$path2name_pbs, keys %$path2name_extra) ;
my $progress_bar = PBS::ProgressBar->new
({
count => scalar(grep {/\.pm$/} @files)
});
my $file_index = 1 ;
for my $file (sort grep {/\.pm$/} @files)
{
eval {$parser->parse_from_file($file) ; } ;
print "Skipping '$file': $@" if $@ ;
$progress_bar->update($file_index) ;
$file_index++ ;
}
print "\n" ;
my $index_by_file = $parser->GetIndex() ;
my $index_by_section ;
for my $file_entry (keys %$index_by_file)
{
for my $level_data (@{$index_by_file->{$file_entry}})
{
push @{$index_by_section->{$level_data->{TEXT}}},
{
LEVEL => $level_data->{LEVEL}
, FILE => $file_entry
} ;
}
}
return($index_by_file, $index_by_section) ;
}
#-------------------------------------------------------------------------------
1 ;
__END__
=head1 NAME
PBS::Documentation -
=head1 SYNOPSIS
pbs -d
=head1 DESCRIPTION
=head2 EXPORT
Nothing.
=head1 AUTHOR
Khemir Nadim ibn Hamouda. nadim@khemir.net
=cut