package CPAN::Digger::PPI;
use 5.008008;
use Moose;
use PPI::Document;
use PPI::Find;
use Perl::MinimumVersion;
our $VERSION = '0.08';
has 'infile' => ( is => 'rw', isa => 'Str' );
has 'ppi' => ( is => 'rw', isa => 'PPI::Document' );
sub min_perl {
my ($self) = @_;
my $pm = Perl::MinimumVersion->new( $self->ppi );
my @vm = $pm->version_markers;
return ( $pm->minimum_version, \@vm );
}
sub read_file {
my ($self) = @_;
my $file = $self->infile;
my $text = do {
open my $fh, '<', $file or die;
local $/ = undef;
<$fh>;
};
return $text;
}
sub get_ppi {
my ($self) = @_;
if ( not $self->ppi ) {
my $text = $self->read_file;
my $ppi = PPI::Document->new( \$text );
die if not defined $ppi;
$ppi->index_locations;
$self->ppi($ppi);
}
return $self->ppi;
}
sub get_syntax {
my ($self) = @_;
my $ppi = $self->get_ppi;
my $html = <<"END_HTML";
END_HTML
my @tokens = $ppi->tokens;
my $current_row;
foreach my $t (@tokens) {
my ( $row, $rowchar, $col ) = @{ $t->location };
my $css = $self->_css_class($t);
my $content = $t->content;
chomp $content;
# TODO set the width of the rownumber constant
# TODO allow the user to turn on/off row numbers
# (this should be some javascript setting hide/show)
if ( not defined $current_row or $current_row < $row ) {
if ( defined $current_row ) {
$html .= "\n"; #close the row;
}
$current_row = $row;
$html .= qq(
$current_row );
}
# TODO: how handle tabs and indentation in general?? for now we replace TABs by 4 spaces
if ( $t->isa('PPI::Token::Whitespace') ) {
$content =~ s/\t/ /s;
if ( length $content > 1 ) {
$content = qq(
$content
);
}
}
if ( $css eq 'keyword' or $css eq 'core' or $css eq 'pragma' ) {
$content = qq(
$content);
}
$html .= qq(
$content
);
# if ($row > $first and $row < $first + 5) {
# print "$row, $rowchar, ", $t->length, " ", $t->class, " ", $css, " ", $t->content, "\n";
# }
# last if $row > 10;
#my $color = $colors{$css};
#if ( not defined $color ) {
# TRACE("Missing definition for '$css'\n") if DEBUG;
# next;
#}
#next if not $color;
}
$html .= "
\n"; #close the last row;
return $html;
}
sub _css_class {
my $self = shift;
my $Token = shift;
if ( $Token->isa('PPI::Token::Word') ) {
# There are some words we can be very confident are
# being used as keywords
unless ( $Token->snext_sibling and $Token->snext_sibling->content eq '=>' ) {
if ( $Token->content =~ /^(?:sub|return)$/ ) {
return 'keyword';
} elsif ( $Token->content =~ /^(?:undef|shift|defined|bless)$/ ) {
return 'core';
}
}
if ( $Token->previous_sibling and $Token->previous_sibling->content eq '->' ) {
if ( $Token->content =~ /^(?:new)$/ ) {
return 'core';
}
}
if ( $Token->parent->isa('PPI::Statement::Include') ) {
if ( $Token->content =~ /^(?:use|no)$/ ) {
return 'keyword';
}
if ( $Token->content eq $Token->parent->pragma ) {
return 'pragma';
}
} elsif ( $Token->parent->isa('PPI::Statement::Variable') ) {
if ( $Token->content =~ /^(?:my|local|our)$/ ) {
return 'keyword';
}
} elsif ( $Token->parent->isa('PPI::Statement::Compound') ) {
if ( $Token->content =~ /^(?:if|else|elsif|unless|for|foreach|while|my)$/ ) {
return 'keyword';
}
} elsif ( $Token->parent->isa('PPI::Statement::Package') ) {
if ( $Token->content eq 'package' ) {
return 'keyword';
}
} elsif ( $Token->parent->isa('PPI::Statement::Scheduled') ) {
return 'keyword';
}
}
# Normal coloring
my $css = ref $Token;
$css =~ s/^.+:://;
$css;
}
1;