#!/usr/local/bin/perl -w
###########################################
# ptags - A PPI-based ctags for Perl
# Mike Schilli, 2005 (m@perlmeister.com)
###########################################
use strict;

use PPI::Document;
use File::Find;
use Sysadm::Install qw(:all);
use Log::Log4perl qw(:easy);

my $outfile = "$ENV{HOME}/.ptags.txt";
my %dirs  = ();
my @found = ();

find \&file_wanted, grep {$_ ne "."} @INC;

blurt join("\n", sort @found), $outfile;

###########################################
sub file_wanted {
###########################################
    my $abs = $File::Find::name;

        # Avoid dupe dirs
    $File::Find::prune = 1 if -d and 
        $dirs{$abs}++;

        # Only Perl modules
    return unless /\.pm$/;

    my $d = PPI::Document->load($abs);

    unless($d) {
        WARN "Cannot load $abs ($! $@)";
        return;
    }
        # Find packages and 
        # all named subroutines
    $d->find(\&document_wanted);
}

###########################################
sub document_wanted {
###########################################
    our $package;
    my  $tag;

    if(ref($_[1]) eq 
           'PPI::Statement::Package') {
        $tag = $_[1]->child(2)->content();
        $package = $tag;

    } elsif(ref($_[1]) eq 
            'PPI::Statement::Sub' and 
            $_[1]->name()) {
        $tag = "$package\::" . 
               $_[1]->name();
    }

    return 1 unless defined $tag;

    push @found, $tag . "\t" . 
                 $File::Find::name . "\t" .
                 regex_from_node($_[1]);

    return 1;
}

###########################################
sub regex_from_node {
###########################################
    my($node) = @_;

    my $regex = $node->content();
   
    $regex =~ s/\n.*//gs;

    while(my $prev = 
             $node->previous_sibling()) {
        last if $prev =~ /\n/;
        $regex = $prev->content() . 
                 $regex;
        $node  = $prev;
    }

    $regex =~ s#[/.*[\]^\$]#\\$&#g;
    return "/^$regex/";
}
