App-File-Grepper

 view release on metacpan or  search on metacpan

lib/App/File/Grepper.pm  view on Meta::CPAN


Version control directories C<RCS>, C<CVS>, C<.svn>, C<.git> and
C<.hg> are always excluded, as are common editor backups.

=back

=cut

use File::Find;
use Term::ANSIColor;

sub main {

    my $self = shift;
    unshift(@_, $self) unless UNIVERSAL::isa( $self, __PACKAGE__ );
    my $opts = shift;
    my @dirs = @_;

    my $pat = $opts->{pattern};
    my $edit = "";
    if ( $opts->{'edit-with-emacs'} ) {
	$edit = 'emacs';
    }
    elsif ( $opts->{'edit-with-vi'} ) {
	$edit = 'vi';
    }
    elsif ( $opts->{'view'} ) {
	$edit = 'less';
    }

    my $ignorecase =
      defined($opts->{ignorecase})
      ? $opts->{ignorecase}
      : $pat !~ /[A-Z]/;

    my $opat = $pat;

    $pat = $pat =~ m;^/(.*);
      ? $ignorecase ? qr/$1/i : qr/$1/
      : $ignorecase ? qr/\Q$pat\E/i : qr/\Q$pat\E/;
    warn("PAT:  $pat\n") if $opts->{debug};

    *hilite = ( !$edit && -t STDOUT )
      ? sub { color('red bold').$_[0].color('reset') }
	: sub { $_[0] };

    my $filter;
    if ( defined $opts->{filter} ) {
	$filter = $opts->{filter};
	$filter = qr/$filter/;
    }
    my $exclude;
    if ( defined $opts->{exclude} ) {
	$exclude = $opts->{exclude};
	$exclude = qr/$exclude/;
    }
    else {
	$exclude = qr{ (?: ^\# | ^\.\.?(?!/) | ~$ ) }x;
    }

    binmode( STDOUT, ":utf8" );

    my $grepper = sub {

	# Prune VC dirs. Always.
	if ( -d $_ && $_ =~ /^(RCS|CVS|\.svn|\.git|\.hg)$/ ) {
	    $File::Find::prune = 1;
	    return;
	}

	# Files only.
	return unless -f $_;

	# Handle include/exclude filters.
	if ( $exclude && ( $_ =~ $exclude ) ) {
	    warn("EXCL: $_\n") if $opts->{debug};
	    return;
	}
	if ( $filter && ( $_ !~ $filter ) ) {
	    warn("FLTR: $_\n") if $opts->{debug};
	    return;
	}
	warn("FILE: $_\n") if $opts->{debug};

	my $file = $_;

	# Okay, we've got one.
	open( my $fh, '<', $file )
	  or warn("$File::Find::name: $!\n"), return;

	unless ( -T $fh ) {
	    warn("[Binary: $File::Find::name]\n") if $opts->{verbose};
	    return;
	}

	binmode( $fh, 'raw' );

	use Encode qw(decode);

	while ( <$fh> ) {

	    eval {
		$_ = decode( 'UTF-8', $_, 1 );
	    }
	    or $_ = decode( 'iso-8859-1', $_ );

	    next unless s/^(.*?)($pat)/"$1".hilite($2)/ge;

	    if ( $edit eq 'vi') {
		system( "vi",
			$ignorecase ? ( "+set ignorecase" ) : (),
			"+/$opat",
			$file );
	    }
	    elsif ( $edit eq 'emacs') {
		system( "emacsclient",
			"+$.:" . (1+length($1)),
			$file );
	    }
	    elsif ( $edit eq 'view') {
		system( "less",
			$ignorecase ? ( "-i" ) : (),
			"+/$opat",
			$file );
	    }
	    last if $edit;

	    print( $File::Find::name, ':',
		   $., ':',
		   1+length($1), ':',
		   $_
		 );
	}

	close($fh);
    };

    find( { wanted => $grepper,
	    no_chdir => 1,
	  }, @dirs );
}

=head1 AUTHOR

Johan Vromans, C<< <jv at cpan.org> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-app-file-grepper at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=App-File-Grepper>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 SUPPORT

Development of this module takes place on GitHub:
L<https://github.com/sciurius/afg>.



( run in 1.887 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )