@ 11d698f

241216.1 · 286e234edd47f95f86a9abc3a640a47b2fd461b3

Add --and and --or options

reading declarations

Changes

@@ -1,9 +1,12 @@ History file for ack 3.  https://beyondgrep.com/  -NEXT+v3.8.0 ======================================== [FEATURES]+Add --and and --or options to allow combinations of search terms. This is+in addition to the --not that was added in v3.7.0.+ Add support for Starlark and Pytest filetypes.  Add support for Terraform. Thanks, Thiago Perrotta. (GH #377)

ack

@@ -55,14 +55,17 @@ our $opt_v; # Flag if we need any context tracking. our $is_tracking_context; -# The regex that we search for in each file.-our $search_re;+# The regex that we use to match each line in the file.+our $re_match;++# Regex for matching for highlighting in matched lines.+our $re_hilite;  # The regex that matches for things we want to exclude via the --not option.-our $search_not_re;+our $re_not; -# Special /m version of our $search_re.-our $scan_re;+# Version of the match regex for checking to see if the file should be scanned line-by-line.+our $re_scan;  our @special_vars_used_by_opt_output; @@ -206,16 +209,18 @@ MAIN: {         }     } +     # Set up file filters.     my $files;     if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x         $files     = App::Ack::Files->from_stdin();         $opt_regex //= shift @ARGV;-        ($search_re, $scan_re) = App::Ack::build_regex( $opt_regex, $opt );-        $search_not_re = App::Ack::build_search_not_re( $opt );-        $stats{search_re} = $search_re;-        $stats{scan_re} = $scan_re;-        $stats{search_not_re} = $search_not_re;+        defined $opt_regex or App::Ack::die( 'No regular expression found.' );+        ($re_match, $re_not, $re_hilite, $re_scan) = App::Ack::build_all_regexes( $opt_regex, $opt );+        $stats{re_match}  = $re_match;+        $stats{re_not}    = $re_not;+        $stats{re_hilite} = $re_hilite;+        $stats{re_scan}   = $re_scan;     }     else {         if ( $opt_f ) {@@ -223,11 +228,12 @@ MAIN: {         }         else {             $opt_regex //= shift @ARGV;-            ($search_re, $scan_re) = App::Ack::build_regex( $opt_regex, $opt );-            $search_not_re = App::Ack::build_search_not_re( $opt );-            $stats{search_re} = $search_re;-            $stats{scan_re} = $scan_re;-            $stats{search_not_re} = $search_not_re;+            defined $opt_regex or App::Ack::die( 'No regular expression found.' );+            ($re_match, $re_not, $re_hilite, $re_scan) = App::Ack::build_all_regexes( $opt_regex, $opt );+            $stats{re_match}  = $re_match;+            $stats{re_not}    = $re_not;+            $stats{re_hilite} = $re_hilite;+            $stats{re_scan}   = $re_scan;         }         my @start;         if ( not defined $opt->{files_from} ) {@@ -275,7 +281,7 @@ MAIN: {      if ( $opt_debug ) {         require List::Util;-        my @stats = qw( search_re scan_re search_not_re prescans linescans filematches linematches );+        my @stats = qw( re_match re_scan re_not prescans linescans filematches linematches );         my $width = List::Util::max( map { length } @stats );          for my $stat ( @stats ) {@@ -429,7 +435,7 @@ sub _compile_file_filter {      return sub {         if ( $opt_g ) {-            if ( $File::Next::name =~ /$search_re/o ) {+            if ( $File::Next::name =~ /$re_match/o ) {                 return 0 if $opt_v;             }             else {@@ -578,7 +584,7 @@ sub file_loop_normal {         my $needs_line_scan = 1;         if ( !$opt_passthru && !$opt_v ) {             $stats{prescans}++;-            if ( $file->may_be_present( $scan_re ) ) {+            if ( $file->may_be_present( $re_scan ) ) {                 $file->reset();             }             else {@@ -655,10 +661,10 @@ sub pmif_context {          my $does_match;         if ( $in_range ) {-            $does_match = /$search_re/o;-            if ( $does_match && defined($search_not_re) ) {+            $does_match = /$re_match/o;+            if ( $does_match && defined($re_not) ) {                 local @-;-                $does_match = !/$search_not_re/o;+                $does_match = !/$re_not/o;             }             if ( $opt_v ) {                 $does_match = !$does_match;@@ -728,10 +734,10 @@ sub pmif_passthru {         $in_range = 1 if ( $using_ranges && !$in_range && defined($opt_range_start) && /$opt_range_start/o );          $match_colno = undef;-        my $does_match = /$search_re/o;-        if ( $does_match && defined($search_not_re) ) {+        my $does_match = /$re_match/o;+        if ( $does_match && defined($re_not) ) {             local @-;-            $does_match = !/$search_not_re/o;+            $does_match = !/$re_not/o;         }         if ( $in_range && $does_match ) {             $match_colno = $-[0] + 1;@@ -784,10 +790,10 @@ sub pmif_opt_v {         $in_range = 1 if ( $using_ranges && !$in_range && defined($opt_range_start) && /$opt_range_start/o );          if ( $in_range ) {-            my $does_match = /$search_re/o;-            if ( $does_match && defined($search_not_re) ) {+            my $does_match = /$re_match/o;+            if ( $does_match && defined($re_not) ) {                 # local @-; No need to localize this because we don't use @-.-                $does_match = !/$search_not_re/o;+                $does_match = !/$re_not/o;             }             if ( !$does_match ) {                 if ( !$has_printed_from_this_file ) {@@ -834,10 +840,10 @@ sub pmif_normal {          if ( $in_range ) {             $match_colno = undef;-            my $is_match = /$search_re/o;-            if ( $is_match && defined($search_not_re) ) {+            my $is_match = /$re_match/o;+            if ( $is_match && defined($re_not) ) {                 local @-;-                $is_match = !/$search_not_re/o;+                $is_match = !/$re_not/o;             }             if ( $is_match ) {                 $match_colno = $-[0] + 1;@@ -905,7 +911,7 @@ sub print_line_with_options {     }      if ( $opt_output ) {-        while ( $line =~ /$search_re/og ) {+        while ( $line =~ /$re_match/og ) {             my $output = $opt_output;             if ( @special_vars_used_by_opt_output ) {                 no strict;@@ -927,7 +933,7 @@ sub print_line_with_options {          # We have to do underlining before any highlighting because highlighting modifies string length.         if ( $opt_underline && !$skip_coloring ) {-            while ( $line =~ /$search_re/og ) {+            while ( $line =~ /$re_hilite/og ) {                 my $match_start = $-[0] // next;                 my $match_end = $+[0];                 my $match_length = $match_end - $match_start;@@ -942,7 +948,7 @@ sub print_line_with_options {         if ( $opt_color && !$skip_coloring ) {             my $highlighted = 0; # If highlighted, need to escape afterwards. -            while ( $line =~ /$search_re/og ) {+            while ( $line =~ /$re_hilite/og ) {                 my $match_start = $-[0] // next;                 my $match_end = $+[0];                 my $match_length = $match_end - $match_start;@@ -1054,7 +1060,7 @@ sub count_matches_in_file {     }     else {         if ( !$opt_v ) {-            if ( !$file->may_be_present( $scan_re ) ) {+            if ( !$file->may_be_present( $re_scan ) ) {                 $do_scan = 0;             }         }@@ -1071,9 +1077,9 @@ sub count_matches_in_file {                 chomp;                 $in_range = 1 if ( !$in_range && defined($opt_range_start) && /$opt_range_start/o );                 if ( $in_range ) {-                    my $is_match = /$search_re/o;-                    if ( $is_match && defined($search_not_re) ) {-                        $is_match = !/$search_not_re/o;+                    my $is_match = /$re_match/o;+                    if ( $is_match && defined($re_not) ) {+                        $is_match = !/$re_not/o;                     }                     if ( $is_match xor $opt_v ) {                         ++$nmatches;@@ -1086,9 +1092,9 @@ sub count_matches_in_file {         else {             while ( <$fh> ) {                 chomp;-                my $is_match = /$search_re/o;-                if ( $is_match && defined($search_not_re) ) {-                    $is_match = !/$search_not_re/o;+                my $is_match = /$re_match/o;+                if ( $is_match && defined($re_not) ) {+                    $is_match = !/$re_not/o;                 }                 if ( $is_match xor $opt_v ) {                     ++$nmatches;@@ -1293,6 +1299,24 @@ matches for lines within the range.  Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">. +=item B<--and=PATTERN>++Specifies a I<PATTERN> that MUST ALSO be found on a given line for a match to+occur. This option can be repeated.++If you want to find all the lines with both "dogs" or "cats", use:++    ack dogs --and cats++Note that the options that affect "dogs" also affect "cats", so if you have++    ack -i -w dogs --and cats++then the search for both "dogs" and "cats" will be case-insensitive and be+word-limited.++See also C<--or> and C<--not>.+ =item B<-A I<NUM>>, B<--after-context=I<NUM>>  Print I<NUM> lines of trailing context after matching lines.@@ -1547,14 +1571,34 @@ if you have      ack -i -w dogs --not cats -the the search for both "dogs" and "cats" will be case-insensitive and be+then the search for both "dogs" and "cats" will be case-insensitive and be word-limited. +See also C<--and> and C<--or>.+ =item B<-o>  Show only the part of each line matching PATTERN (turns off text highlighting).  This is exactly the same as C<--output=$&>. +=item B<--or=PATTERN>++Specifies a I<PATTERN> that MAY be found on a given line for a match to+occur. This option can be repeated.++If you want to find all the lines with "dogs" or "cats", use:++    ack dogs --or cats++Note that the options that affect "dogs" also affect "cats", so if you have++    ack -i -w dogs --or cats++then the search for both "dogs" and "cats" will be case-insensitive and be+word-limited.++See also C<--and> and C<--not>.+ =item B<--output=I<expr>>  Output the evaluation of I<expr> for each line (turns off text@@ -1585,7 +1629,7 @@ The number of the line in the file.  =item C<$&>, C<$`> and C<$'> -C<$&> is the the string matched by the pattern, C<$`> is what+C<$&> is the string matched by the pattern, C<$`> is what precedes the match, and C<$'> is what follows it.  If the pattern is C<gra(ph|nd)> and the string is "lexicographic", then C<$&> is "graph", C<$`> is "lexico" and C<$'> is "ic".@@ -1844,7 +1888,7 @@ an F<.ackrc> file - then you do not have to define your types over and over again. In the following examples the options will always be shown on one command line so that they can be easily copy & pasted. -File types can be specified both with the the I<--type=xxx> option,+File types can be specified both with the I<--type=xxx> option, or the file type as an option itself.  For example, if you create a filetype of "cobol", you can specify I<--type=cobol> or simply I<--cobol>.  File types must be at least two characters long.  This

dev/crank-mutex

@@ -15,8 +15,10 @@ sub invalid_combinations {     my @output     = qw( -o --output );     my @fg         = qw( -f -g );     my @case       = qw( -i -I --smart-case );+    my @booleans   = qw( --and --or --not );      return (+        [qw(--and)]      => [qw( --or --not )],         [qw(-l)]         => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types)],         [qw(-L)]         => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types -c -v)],         [@output]        => [@context, @file_lists, @output, qw(-c --column --show-types)],@@ -29,7 +31,7 @@ sub invalid_combinations {         [qw(--column)]   => [@file_lists],         [@context]       => [@file_lists],         [qw(-f)]         => [qw(-v), @case],-        [qw(-g)]         => [qw(--not)],+        [qw(-g)]         => [@booleans],         [@fg]            => [@fg, @pretty, qw(-x -c -u --files-from)],         [qw(-p)]         => [@context, @file_lists, qw(--passthru -c)],         [qw(-v)]         => [qw(--column -o --output --passthru)],

lib/App/Ack.pm

@@ -246,7 +246,11 @@ Searching:   --range-start PATTERN         Specify PATTERN as the start of a match range.   --range-end PATTERN           Specify PATTERN as the end of a match range.   --match PATTERN               Specify PATTERN explicitly. Typically omitted.-  --not PATTERN                 Specifies PATTERN that must not be found on+  --and PATTERN                 Specifies PATTERN that MUST also be found on+                                the line for a match to occur. Repeatable.+  --or PATTERN                  Specifies PATTERN that MAY also be found on+                                the line for a match to occur. Repeatable.+  --not PATTERN                 Specifies PATTERN that must NOT be found on                                 the line for a match to occur. Repeatable.  Search output:@@ -741,8 +745,6 @@ sub build_regex {     my $str = shift;     my $opt = shift; -    defined $str or App::Ack::die( 'No regular expression found.' );-     # Check for lowercaseness before we do any modifications.     my $regex_is_lc = App::Ack::is_lowercase( $str ); @@ -828,23 +830,65 @@ sub build_regex { }  -sub build_search_not_re {+sub build_all_regexes {+    my $opt_regex = shift;     my $opt = shift; -    my @not = @{$opt->{not}};+    my $re_match;+    my $re_not;+    my $re_hilite;+    my $re_scan;++    my @parts; -    if ( @not ) {-        my @built;-        for my $re ( @not ) {-            my ($built,undef) = App::Ack::build_regex( $re, $opt );-            push( @built, $built );+    # AND: alpha and beta+    if ( @parts = @{$opt->{and}} ) {+        my @match_parts;+        my @hilite_parts;++        for my $part ( $opt_regex, @parts ) {+            my ($match, $scan) = build_regex( $part, $opt );+            push @match_parts, "(?=.*$match)";+            push @hilite_parts, $match;+            $re_scan //= $scan;         }-        return join( '|', @built );+        $re_match  = join( '', @match_parts );      # All required, not optional.+        $re_hilite = join( '|', @hilite_parts );     }+    # OR: alpha OR beta+    elsif ( @parts = @{$opt->{or}} ) {+        my @match_parts;+        my @scan_parts;++        for my $part ( $opt_regex, @parts ) {+            my ($match, $scan) = build_regex( $part, $opt );+            push @match_parts, $match;+            push @scan_parts, $scan;+        } -    return;-}+        $re_match = join( '|', @match_parts );+        $re_hilite = $re_match;+        $re_scan = join( '|', @scan_parts );+    }+    # NOT: alpha NOT beta+    elsif ( @parts = @{$opt->{not}} ) {+        ($re_match, $re_scan) = build_regex( $opt_regex, $opt );++        my @not_parts;+        for my $part ( @parts ) {+            (my $re, undef) = build_regex( $part, $opt );+            push @not_parts, $re;+        }+        $re_not = join( '|', @not_parts );+    }+    # No booleans.+    else {+        ($re_match, $re_scan) = build_regex( $opt_regex, $opt );+        $re_hilite = $re_match;+    } +    return ($re_match, $re_not, $re_hilite, $re_scan);+}   1; # End of App::Ack

lib/App/Ack/ConfigLoader.pm

@@ -267,10 +267,13 @@ sub get_arg_spec {         return;     } +    $opt->{and} = [];+    $opt->{or} = [];     $opt->{not} = [];      return {         1                   => sub { $opt->{1} = $opt->{m} = 1 },+        'and=s'             => $opt->{and},         'A|after-context:-1'  => sub { shift; $opt->{A} = _context_value(shift) },         'B|before-context:-1' => sub { shift; $opt->{B} = _context_value(shift) },         'C|context:-1'        => sub { shift; $opt->{B} = $opt->{A} = _context_value(shift) },@@ -333,6 +336,7 @@ sub get_arg_spec {         'noignore-directory|noignore-dir=s' => _generate_ignore_dir('--noignore-dir', $opt),         'nopager'           => sub { $opt->{pager} = undef },         'not=s'             => $opt->{not},+        'or=s'              => $opt->{or},         'passthru'          => \$opt->{passthru},         'print0'            => \$opt->{print0},         'p|proximate:1'     => \$opt->{p},@@ -928,6 +932,11 @@ sub mutex_options {             v => 1,             'with-filename' => 1,         },+        and => {+            g => 1,+            not => 1,+            or => 1,+        },         break => {             L => 1,             c => 1,@@ -1003,6 +1012,7 @@ sub mutex_options {             C => 1,             H => 1,             L => 1,+            and => 1,             break => 1,             c => 1,             column => 1,@@ -1017,6 +1027,7 @@ sub mutex_options {             match => 1,             not => 1,             o => 1,+            or => 1,             output => 1,             p => 1,             passthru => 1,@@ -1084,6 +1095,7 @@ sub mutex_options {             l => 1,         },         not => {+            and => 1,             g => 1,         },         o => {@@ -1103,6 +1115,10 @@ sub mutex_options {             'show-types' => 1,             v => 1,         },+        or => {+            and => 1,+            g => 1,+        },         output => {             A => 1,             B => 1,

t/Util.pm

@@ -75,6 +75,8 @@ our @EXPORT = qw(      msg     subtest_name++    permutate );  my $orig_wd;@@ -1255,4 +1257,22 @@ sub regex_eq { }  +# From https://stackoverflow.com/questions/635768/how-can-i-generate-all-permutations-of-an-array-in-perl+sub permutate {+    return [@_] if @_ <= 1;++    return map {+        my ($f, @r) = list_with_x_first($_, @_);+        map [$f, @$_], permutate(@r);+    } 0..$#_;+}+++sub list_with_x_first {+    return if @_ == 1;+    my $i = shift;+    return @_[$i, 0..$i-1, $i+1..$#_];+}++ 1;

t/ack-c.t

@@ -16,6 +16,7 @@ DASH_C: {         t/text/bill-of-rights.txt:0         t/text/constitution.txt:0         t/text/gettysburg.txt:1+        t/text/movies.txt:0         t/text/number.txt:0         t/text/numbered-text.txt:0         t/text/ozymandias.txt:0@@ -38,6 +39,7 @@ WITH_DASH_V: {         t/text/bill-of-rights.txt:45         t/text/constitution.txt:259         t/text/gettysburg.txt:15+        t/text/movies.txt:25         t/text/number.txt:1         t/text/numbered-text.txt:20         t/text/ozymandias.txt:9

t/ack-g.t

@@ -234,6 +234,7 @@ subtest '-v works on -g' => sub {     my @expected = qw(         t/text/bill-of-rights.txt         t/text/gettysburg.txt+        t/text/movies.txt     );     my $file_regex = 'n'; @@ -291,6 +292,7 @@ subtest 'test -g on a path' => sub {         t/text/bill-of-rights.txt         t/text/constitution.txt         t/text/gettysburg.txt+        t/text/movies.txt         t/text/number.txt         t/text/numbered-text.txt         t/text/ozymandias.txt@@ -311,6 +313,7 @@ t/(text)/amontillado.txt t/(text)/bill-of-rights.txt t/(text)/constitution.txt t/(text)/gettysburg.txt+t/(text)/movies.txt t/(text)/number.txt t/(text)/numbered-(text).txt t/(text)/ozymandias.txt@@ -343,6 +346,7 @@ t/text/amontillado.txt t/text/bill-of-rights.txt t/text/constitution.txt t/text/gettysburg.txt+t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt

t/ack-l.t

@@ -21,6 +21,7 @@ my @matching = qw( my @nonmatching = qw(     t/text/amontillado.txt     t/text/gettysburg.txt+    t/text/movies.txt     t/text/number.txt     t/text/numbered-text.txt     t/text/ozymandias.txt@@ -59,6 +60,7 @@ DASH_CAPITAL_L: {     my @expected = qw(         t/text/bill-of-rights.txt         t/text/constitution.txt+        t/text/movies.txt         t/text/number.txt         t/text/numbered-text.txt         t/text/ozymandias.txt@@ -83,6 +85,7 @@ DASH_LV: {         t/text/bill-of-rights.txt         t/text/constitution.txt         t/text/gettysburg.txt+        t/text/movies.txt         t/text/number.txt         t/text/numbered-text.txt         t/text/ozymandias.txt

t/ack-print0.t

@@ -15,6 +15,7 @@ G_NO_PRINT0: {         t/text/amontillado.txt         t/text/bill-of-rights.txt         t/text/constitution.txt+        t/text/movies.txt         t/text/ozymandias.txt     ); @@ -32,6 +33,7 @@ G_PRINT0: {         t/text/amontillado.txt         t/text/bill-of-rights.txt         t/text/constitution.txt+        t/text/movies.txt         t/text/ozymandias.txt     ) ) . "\0"; # string of filenames separated and concluded with null byte 

t/boolean.t

@@ -9,7 +9,7 @@ use Test::More; use lib 't'; use Util; -plan tests => 4;+plan tests => 90;  prep_environment(); @@ -83,6 +83,192 @@ HERE }  -done_testing();+NOT: {+    _movies_are(+        [qw( Murray )],+        [+            'Caddyshack',+            'Ghostbusters',+            'Groundhog Day',+            'Little Shop of Horrors',+            'Stripes',+        ]+    );++    _movies_are(+        [qw( Murray --not Ramis )],+        [+            'Caddyshack',+            'Groundhog Day',+            'Little Shop of Horrors',+        ]+    );+}+++AND: {+    _movies_are(+        [qw( Aykroyd )],+        [+            '1941',+            'The Blues Brothers',+            'Dragnet',+            'Ghostbusters',+            'Neighbors',+        ]+    );++    _movies_are(+        [qw( Aykroyd --and Belushi )],+        [+            '1941',+            'The Blues Brothers',+            'Neighbors',+        ]+    );++    _movies_are(+        [qw( Belushi --and Aykroyd )],+        [+            '1941',+            'The Blues Brothers',+            'Neighbors',+        ]+    );++    # Permute through all combinations.+    my @who = qw( Aykroyd Belushi Candy );+    for my $permutation ( permutate( @who ) ) {+        my @args = _argjoin( '--and', $permutation );+        _movies_are(+            [ @args ],+            [+                'The Blues Brothers',+            ]+        );+    }+} ++OR: {+    _movies_are(+        [qw( Fisher )],+        [+            'The Blues Brothers',+            'When Harry Met Sally',+        ]+    );++    _movies_are(+        [qw( Crystal )],+        [+            'This is Spinal Tap',+            'When Harry Met Sally',+        ]+    );++    _movies_are(+        [qw( Crystal --or Fisher )],+        [+            'The Blues Brothers',+            'This is Spinal Tap',+            'When Harry Met Sally',+        ]+    );++    _movies_are(+        [qw( Fisher --or Crystal )],+        [+            'The Blues Brothers',+            'This is Spinal Tap',+            'When Harry Met Sally',+        ]+    );++    _movies_are(    # Note this is an --and.+        [qw( Fisher --and Crystal )],+        [+            'When Harry Met Sally',+        ]+    );++    # One of the OR options doesn't exist.+    _movies_are(+        [qw( Crystal --or Hemsworth )],+        [+            'This is Spinal Tap',+            'When Harry Met Sally',+        ]+    );+    _movies_are(+        [qw( Hemsworth --or Crystal )],+        [+            'This is Spinal Tap',+            'When Harry Met Sally',+        ]+    );++    _movies_are(+        [qw( Crystal --or Fisher --or Hemsworth )],+        [+            'The Blues Brothers',+            'This is Spinal Tap',+            'When Harry Met Sally',+        ]+    );++    # Permute through all combinations.+    my @who = qw( Crystal Fisher Hemsworth Moranis );+    for my $permutation ( permutate( @who ) ) {+        my @args = _argjoin( '--or', $permutation );+        _movies_are(+            [ @args ],+            [+                'The Blues Brothers',+                'Ghostbusters',+                'Little Shop of Horrors',+                'My Blue Heaven',+                'Spaceballs',+                'This is Spinal Tap',+                'When Harry Met Sally',+            ]+        );+    }+}++++# XXX  We need tests on highlighting, too.+#+# XXX And tests on none of them matching.+# XXX And tests that --and themselves out of matching.+done_testing(); exit 0;+++sub _movies_are {+    local $Test::Builder::Level = $Test::Builder::Level + 1;++    my $args = shift;+    my $exp  = shift;++    my @base_args = qw( -w -i --noenv );+    my @files = reslash( 't/text/movies.txt' );+    my @got = run_ack( @base_args, @{$args}, @files );++    s/:.+// for @got;++    return is_deeply( \@got, $exp, join( ' ', @{$args} ) );+}+++sub _argjoin {+    my $opt  = shift;+    my $args = shift;++    my @args = map { ($_, $opt) } @{$args};++    pop @args;++    return @args;+}

t/config-loader.t

@@ -13,6 +13,7 @@ use App::Ack::ConfigLoader; delete @ENV{qw( PAGER ACK_PAGER ACK_PAGER_COLOR )};  my %defaults = (+    and          => [],     'break'      => undef,     c            => undef,     color        => undef,@@ -31,6 +32,7 @@ my %defaults = (     m            => undef,     n            => undef,     not          => [],+    or           => [],     output       => undef,     p            => undef,     pager        => undef,

t/text/movies.txt

new file mode 100644@@ -0,0 +1,27 @@+1941: Aykroyd, Belushi+Animal House: Belushi, Matheson, Sutherland+Blazing Saddles: Brooks, Kahn, Korman, Little, Pickens, Wilder+The Blues Brothers: Aykroyd, Belushi, Candy, Fisher+Caddyshack: Chase, Dangerfield, Murray+Dragnet: Aykroyd, Hanks+Elf: Asner, Caan, Deschanel, Ferrell, Newhart, Steenburgen+Ghostbusters: Aykroyd, Murray, Moranis, Ramis+Groundhog Day: Elliott, Murray+Little Shop of Horrors: Guest, Martin, Moranis, Murray+My Blue Heaven: Martin, Moranis+Neighbors: Aykroyd, Belushi+Parenthood: Martin, Steenburgen+Planes, Trains and Automobiles: Candy, Martin, McKean+The Princess Bride: Elwes, Falk, Guest, Patinkin, Shawn+Roxanne: Martin, Willard+Silver Streak: Pryor, Wilder, Willard+Spaceballs: Candy, Moranis, Pullman, Rivers+Stir Crazy: Pryor, Willard+Stripes: Candy, Murray, Ramis+This is Spinal Tap: Crystal, Guest, Hendra, Kirby, McKean, Reiner, Shearer, Willard+Three Amigos: Chase, Martin, Short+Trading Places: Akyroyd, Murphy+Vacation: Candy, Chase, D'Angelo+Waiting For Guffman: Guest, Levy, O'Hara, Willard+When Harry Met Sally: Crystal, Fisher, Kirby, Ryan+Young Frankenstein: Feldman, Garr, Kahn, Leachmann, Wilder

t/trailing-whitespace.t

@@ -47,6 +47,7 @@ t/text/amontillado.txt t/text/bill-of-rights.txt t/text/constitution.txt t/text/gettysburg.txt+t/text/movies.txt t/text/number.txt t/text/numbered-text.txt t/text/ozymandias.txt

tags

@@ -28,6 +28,7 @@ !_TAG_PROGRAM_VERSION	6.1.0	// ADJACENT_CAPTURE_COLORING	t/ack-color.t	/^ADJACENT_CAPTURE_COLORING: {$/;"	l AFTER	t/context.t	/^AFTER: {$/;"	l+AND	t/boolean.t	/^AND: {$/;"	l ARG_MULTIPLE_FILES	t/ack-output.t	/^ARG_MULTIPLE_FILES: {$/;"	l App::Ack	lib/App/Ack.pm	/^package App::Ack;$/;"	p App::Ack::ConfigDefault	lib/App/Ack/ConfigDefault.pm	/^package App::Ack::ConfigDefault;$/;"	p@@ -160,6 +161,7 @@ NOIGNORE_SUBDIR_WINS	t/ack-ignore-dir.t	/^NOIGNORE_SUBDIR_WINS: {$/;"	l NORMAL_CASE	t/ack-v.t	/^NORMAL_CASE: {$/;"	l NORMAL_COLOR	t/ack-color.t	/^NORMAL_COLOR: {$/;"	l NOT	t/ack-c.t	/^NOT: {$/;"	l+NOT	t/boolean.t	/^NOT: {$/;"	l NO_BOOLEANS	t/boolean.t	/^NO_BOOLEANS: {$/;"	l NO_GROUPING	t/ack-group.t	/^NO_GROUPING: {$/;"	l NO_O	t/ack-o.t	/^NO_O: {$/;"	l@@ -172,6 +174,7 @@ NO_SWITCHES_ONE_FILE	t/ack-h.t	/^NO_SWITCHES_ONE_FILE: {$/;"	l NO_SWITCHES_ONE_FILE	t/basic.t	/^NO_SWITCHES_ONE_FILE: {$/;"	l NTESTS	t/file-permission.t	/^use constant NTESTS => 5;$/;"	c NUMERIC_SUBSTITUTIONS	t/ack-output.t	/^NUMERIC_SUBSTITUTIONS: {$/;"	l+OR	t/boolean.t	/^OR: {$/;"	l PAGER	t/ack-pager.t	/^PAGER: {$/;"	l PAGER_ACKRC	t/ack-pager.t	/^PAGER_ACKRC: {$/;"	l PAGER_ACKRC_OVERRIDE	t/ack-pager.t	/^PAGER_ACKRC_OVERRIDE: {$/;"	l@@ -228,6 +231,7 @@ WITH_SWITCHES_MULTIPLE_FILES	t/ack-h.t	/^WITH_SWITCHES_MULTIPLE_FILES: {$/;"	l WITH_SWITCHES_MULTIPLE_FILES	t/basic.t	/^WITH_SWITCHES_MULTIPLE_FILES: {$/;"	l WITH_SWITCHES_ONE_FILE	t/ack-h.t	/^WITH_SWITCHES_ONE_FILE: {$/;"	l WITH_SWITCHES_ONE_FILE	t/basic.t	/^WITH_SWITCHES_ONE_FILE: {$/;"	l+_argjoin	t/boolean.t	/^sub _argjoin {$/;"	s _big_split	t/lowercase.t	/^sub _big_split {$/;"	s _check	t/build_regex.t	/^sub _check {$/;"	s _check_command_for_taintedness	t/Util.pm	/^sub _check_command_for_taintedness {$/;"	s@@ -248,6 +252,7 @@ _generate_error_handler	lib/App/Ack/Files.pm	/^sub _generate_error_handler {$/;" _generate_ignore_dir	lib/App/Ack/ConfigLoader.pm	/^sub _generate_ignore_dir {$/;"	s _get_code_from_file	squash	/^sub _get_code_from_file {$/;"	s _get_good_and_bad	t/ack-w.t	/^sub _get_good_and_bad {$/;"	s+_movies_are	t/boolean.t	/^sub _movies_are {$/;"	s _my_program	lib/App/Ack.pm	/^sub _my_program {$/;"	s _options_block	lib/App/Ack/ConfigDefault.pm	/^sub _options_block {$/;"	s _options_used	lib/App/Ack/ConfigLoader.pm	/^sub _options_used {$/;"	s@@ -289,8 +294,8 @@ bar	t/range/rangefile.pm	/^sub bar {$/;"	s basename	lib/App/Ack/File.pm	/^sub basename {$/;"	s bisect	dev/find-minimum-dep-versions.pl	/^sub bisect (&@) {$/;"	s build_ack_invocation	t/Util.pm	/^sub build_ack_invocation {$/;"	s+build_all_regexes	lib/App/Ack.pm	/^sub build_all_regexes {$/;"	s build_regex	lib/App/Ack.pm	/^sub build_regex {$/;"	s-build_search_not_re	lib/App/Ack.pm	/^sub build_search_not_re {$/;"	s caret_X	t/Util.pm	/^sub caret_X {$/;"	s cathy	lib/App/Ack.pm	/^sub cathy {$/;"	s check_for_option_in_man_output	xt/man.t	/^sub check_for_option_in_man_output {$/;"	s@@ -382,6 +387,7 @@ is_lowercase	lib/App/Ack.pm	/^sub is_lowercase {$/;"	s is_nonempty_array	t/Util.pm	/^sub is_nonempty_array {$/;"	s is_windows	t/Util.pm	/^sub is_windows {$/;"	s line_split	t/Util.pm	/^sub line_split {$/;"	s+list_with_x_first	t/Util.pm	/^sub list_with_x_first {$/;"	s lists_match	t/Util.pm	/^sub lists_match {$/;"	s main	Makefile.PL	/^package main;$/;"	p make_unreadable	t/Util.pm	/^sub make_unreadable {$/;"	s@@ -413,6 +419,7 @@ option_in_usage	t/ack-help.t	/^sub option_in_usage {$/;"	s options	lib/App/Ack/ConfigDefault.pm	/^sub options {$/;"	s options_clean	lib/App/Ack/ConfigDefault.pm	/^sub options_clean {$/;"	s output_to_pipe	lib/App/Ack.pm	/^sub output_to_pipe {$/;"	s+permutate	t/Util.pm	/^sub permutate {$/;"	s pipe_into_ack	t/Util.pm	/^sub pipe_into_ack {$/;"	s pipe_into_ack_with_stderr	t/Util.pm	/^sub pipe_into_ack_with_stderr {$/;"	s pmif_context	ack	/^sub pmif_context {$/;"	s@@ -430,6 +437,7 @@ process_args	lib/App/Ack/ConfigLoader.pm	/^sub process_args {$/;"	s range_setup	ack	/^sub range_setup {$/;"	s read_file	t/Util.pm	/^sub read_file {$/;"	s read_rcfile	lib/App/Ack/ConfigLoader.pm	/^sub read_rcfile {$/;"	s+regex_eq	t/Util.pm	/^sub regex_eq {$/;"	s register_filter	lib/App/Ack/Filter.pm	/^sub register_filter {$/;"	s remove_defaults_and_globals	t/noenv.t	/^sub remove_defaults_and_globals {$/;"	s reset	lib/App/Ack/File.pm	/^sub reset {$/;"	s