Skip to content

Commit ad69fe6

Browse files
committed
The -g option can now use any of the boolean options, --and, --or or --not. The --not option can be used with either --and or --or.
1 parent 91c6095 commit ad69fe6

File tree

12 files changed

+164
-131
lines changed

12 files changed

+164
-131
lines changed

Changes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
History file for ack 3. https://beyondgrep.com/
22

3+
NEXT
4+
========================================
5+
The --not option can be used with either --and or --or.
6+
7+
The -g option can now use any of the boolean options, --and, --or or --not.
8+
9+
310
v3.8.2 Sun Apr 6 10:45:04 CDT 2025
411
========================================
512
ack now needs YAML::PP to run its tests. Many of ack's tests are stored in

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ t/lowercase.t
112112
t/match-filter.t
113113
t/multiple-captures.t
114114
t/mutex-options.t
115+
t/mutex.yaml
115116
t/named-pipes.t
116117
t/needs-line-scan.t
117118
t/noackrc.t

ack

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,12 @@ sub _compile_file_filter {
440440

441441
return sub {
442442
if ( $opt_g ) {
443-
if ( $File::Next::name =~ /$re_match/o ) {
443+
my $match = ($File::Next::name =~ /$re_match/o);
444+
if ( $match && $re_not ) {
445+
$match = ($File::Next::name !~ /$re_not/o);
446+
}
447+
448+
if ( $match ) {
444449
return 0 if $opt_v;
445450
}
446451
else {
@@ -1320,8 +1325,7 @@ Note that the options that affect "dogs" also affect "cats", so if you have
13201325
then the search for both "dogs" and "cats" will be case-insensitive and be
13211326
word-limited.
13221327
1323-
See also the other two boolean options C<--or> and C<--not>, neither of
1324-
which can be used with C<--and>.
1328+
C<--and> cannot be used with C<--or>.
13251329
13261330
=item B<-A I<NUM>>, B<--after-context=I<NUM>>
13271331
@@ -1580,9 +1584,6 @@ if you have
15801584
then the search for both "dogs" and "cats" will be case-insensitive and be
15811585
word-limited.
15821586
1583-
See also the other two boolean options C<--and> and C<--or>, neither of
1584-
which can be used with C<--not>.
1585-
15861587
=item B<-o>
15871588
15881589
Show only the part of each line matching PATTERN (turns off text
@@ -1604,8 +1605,7 @@ Note that the options that affect "dogs" also affect "cats", so if you have
16041605
then the search for both "dogs" and "cats" will be case-insensitive and be
16051606
word-limited.
16061607
1607-
See also the other two boolean options C<--and> and C<--not>, neither of
1608-
which can be used with C<--or>.
1608+
C<--or> cannot be used with C<--and>.
16091609
16101610
=item B<--output=I<expr>>
16111611

dev/crank-mutex

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ sub invalid_combinations {
1515
my @output = qw( -o --output );
1616
my @fg = qw( -f -g );
1717
my @case = qw( -i -I --smart-case );
18-
my @booleans = qw( --and --or --not );
1918

2019
return (
21-
[qw(--and)] => [qw( --or --not )],
20+
[qw(--and)] => [qw( --or )],
2221
[qw(-l)] => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types)],
2322
[qw(-L)] => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types -c -v)],
2423
[@output] => [@context, @file_lists, @output, qw(-c --column --show-types)],
@@ -31,7 +30,6 @@ sub invalid_combinations {
3130
[qw(--column)] => [@file_lists],
3231
[@context] => [@file_lists],
3332
[qw(-f)] => [qw(-v), @case],
34-
[qw(-g)] => [@booleans],
3533
[@fg] => [@fg, @pretty, qw(-x -c -u --files-from)],
3634
[qw(-p)] => [@context, @file_lists, qw(--passthru -c)],
3735
[qw(-v)] => [qw(--column -o --output --passthru)],

lib/App/Ack.pm

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ sub build_all_regexes {
841841

842842
my @parts;
843843

844-
# AND: alpha and beta
844+
# AND: alpha AND beta
845845
if ( @parts = @{$opt->{and}} ) {
846846
my @match_parts;
847847
my @hilite_parts;
@@ -875,23 +875,21 @@ sub build_all_regexes {
875875
$re_hilite = $re_match;
876876
$re_scan = join( '|', @scan_parts );
877877
}
878-
# NOT: alpha NOT beta
879-
elsif ( @parts = @{$opt->{not}} ) {
878+
else {
880879
($re_match, $re_scan) = build_regex( $opt_regex, $opt );
881880
$re_hilite = $re_match;
881+
}
882882

883+
# The --not does not affect the main regex. It is checked separately.
884+
# NOT: alpha NOT beta
885+
if ( @parts = @{$opt->{not}} ) {
883886
my @not_parts;
884887
for my $part ( @parts ) {
885888
(my $re, undef) = build_regex( $part, $opt );
886889
push @not_parts, $re;
887890
}
888891
$re_not = join( '|', @not_parts );
889892
}
890-
# No booleans.
891-
else {
892-
($re_match, $re_scan) = build_regex( $opt_regex, $opt );
893-
$re_hilite = $re_match;
894-
}
895893

896894
return ($re_match, $re_not, $re_hilite, $re_scan);
897895
}

lib/App/Ack/ConfigLoader.pm

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -933,8 +933,6 @@ sub mutex_options {
933933
'with-filename' => 1,
934934
},
935935
and => {
936-
g => 1,
937-
not => 1,
938936
or => 1,
939937
},
940938
break => {
@@ -1012,7 +1010,6 @@ sub mutex_options {
10121010
C => 1,
10131011
H => 1,
10141012
L => 1,
1015-
and => 1,
10161013
break => 1,
10171014
c => 1,
10181015
column => 1,
@@ -1025,9 +1022,7 @@ sub mutex_options {
10251022
l => 1,
10261023
m => 1,
10271024
match => 1,
1028-
not => 1,
10291025
o => 1,
1030-
or => 1,
10311026
output => 1,
10321027
p => 1,
10331028
passthru => 1,
@@ -1094,10 +1089,6 @@ sub mutex_options {
10941089
L => 1,
10951090
l => 1,
10961091
},
1097-
not => {
1098-
and => 1,
1099-
g => 1,
1100-
},
11011092
o => {
11021093
A => 1,
11031094
B => 1,
@@ -1117,7 +1108,6 @@ sub mutex_options {
11171108
},
11181109
or => {
11191110
and => 1,
1120-
g => 1,
11211111
},
11221112
output => {
11231113
A => 1,

t/Util.pm

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ our @EXPORT = qw(
5959
sets_match
6060
ack_lists_match
6161
ack_sets_match
62-
ack_error_matches
62+
ack_stderr_matches
6363
6464
untaint
6565
@@ -653,21 +653,24 @@ sub ack_sets_match {
653653
}
654654

655655

656-
sub ack_error_matches {
656+
sub ack_stderr_matches {
657657
local $Test::Builder::Level = $Test::Builder::Level + 1;
658658

659659
my $args = shift;
660660
my $expected = shift;
661661
my $msg = shift;
662662

663663
return subtest subtest_name( $msg, $args, $expected ) => sub {
664-
plan tests => 4;
664+
plan tests => 2;
665665

666666
my ( $stdout, $stderr ) = run_ack_with_stderr( @{$args} );
667-
isnt( get_rc(), 0, 'Nonzero error' );
668667
is_empty_array( $stdout, 'No normal output' );
669-
is( scalar @{$stderr}, 1, 'Just one error' );
670-
like( $stderr->[0], $expected, 'Error matches' );
668+
669+
# Sometimes we run as ack, sometimes as ack-standalone.
670+
for ( @{$stderr} ) {
671+
s/^ack-standalone/ack/g;
672+
}
673+
return lists_match( $stderr, $expected, 'stderr matches' );
671674
};
672675
}
673676

@@ -1286,7 +1289,11 @@ sub read_tests {
12861289
my @tests = $ypp->load_file( $filename );
12871290

12881291
for my $test ( @tests ) {
1289-
$test->{stdout} = _lineify( $test->{stdout} );
1292+
for my $i ( qw( stdout stderr ) ) {
1293+
if ( exists $test->{$i} ) {
1294+
$test->{$i} = _lineify( $test->{$i} );
1295+
}
1296+
}
12901297

12911298
if ( my $n = $test->{'indent-stdout'} ) {
12921299
my $indent = ' ' x $n;
@@ -1352,6 +1359,9 @@ sub _validate_test {
13521359
for my $key ( keys %{$test} ) {
13531360
die "Invalid key $key" unless _in( $key, \@valid_keys );
13541361
}
1362+
if ( not exists $test->{stdout} ) {
1363+
die "stdout must always be specified.";
1364+
}
13551365

13561366
return;
13571367
}

t/ack-g.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,32 @@ stdout: |
119119
t/text/numbered-text.txt
120120
t/text/ozymandias.txt
121121
t/text/raven.txt
122+
123+
---
124+
name: -g and --and
125+
args: -g foo --and test t/
126+
stdout: |
127+
t/swamp/foo_test.py
128+
t/swamp/test_foo.py
129+
130+
---
131+
name: -g and --not
132+
args: -g foo --not test t/
133+
stdout: |
134+
t/etc/shebang.foobar.xxx
135+
t/swamp/file.foo
136+
137+
---
138+
name: -g and --or
139+
args: -g foo --or ample t/
140+
stdout: |
141+
t/etc/shebang.foobar.xxx
142+
t/swamp/example.R
143+
t/swamp/file.foo
144+
t/swamp/foo_test.py
145+
t/swamp/Sample.ascx
146+
t/swamp/Sample.asmx
147+
t/swamp/sample.asp
148+
t/swamp/sample.aspx
149+
t/swamp/sample.rake
150+
t/swamp/test_foo.py

t/mutex-options.t

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use strict;
44
use warnings;
55

6-
use Test::More tests => 196;
6+
use Test::More tests => 178;
77
use lib 't';
88
use Util;
99

@@ -13,20 +13,6 @@ my $file = 't/text/raven.txt';
1313
my $word = 'nevermore';
1414

1515

16-
# Order doesn't matter. They are reported in alphabetical order.
17-
for my $opt ( qw( -p --proximate ) ) {
18-
are_mutually_exclusive( '-f', $opt, ['-f', $opt] );
19-
are_mutually_exclusive( '-f', $opt, [$opt, '-f'] );
20-
}
21-
22-
# Check for abbreviations. https://github.com/beyondgrep/ack3/issues/57
23-
for my $opt ( qw( --pro --prox --proxima --proximat --proximate ) ) {
24-
are_mutually_exclusive( '-f', '--proximate',
25-
['-f', $opt, '4'],
26-
['-f', "$opt=4"],
27-
);
28-
}
29-
3016
# XXX Should also handle --files-with-matches and --files-without-matches. See https://github.com/beyondgrep/ack3/issues/57
3117
are_mutually_exclusive('-l', '-L', ['-l', '-L', $word, $file]);
3218
for my $opt ( qw( -l -L ) ) {
@@ -115,14 +101,6 @@ are_mutually_exclusive('--output', '--after-context', ['--output=$&', '--after-c
115101
are_mutually_exclusive('--output', '--before-context', ['--output=$&', '--before-context=2', $word, $file]);
116102
are_mutually_exclusive('--output', '--context', ['--output=$&', '--context=2', $word, $file]);
117103

118-
# --match
119-
for my $opt ( qw( -f -g ) ) {
120-
are_mutually_exclusive('--match', $opt,
121-
['--match', $word, $opt, $file],
122-
['--match=science', $opt, $file],
123-
);
124-
}
125-
126104
# --max-count
127105
for my $opt ( qw( -1 -c -f -g ) ) {
128106
are_mutually_exclusive( '-m', $opt, ['-m', 1, $opt, $word, $file] );
@@ -182,6 +160,7 @@ for my $opt ( qw( -f -g ) ) {
182160
are_mutually_exclusive( $opt, '--files-from', [$opt, '--files-from', $word, $file] );
183161
}
184162

163+
185164
subtest q{Verify that "options" that follow -- aren't factored into the mutual exclusivity} => sub {
186165
my ( $stdout, $stderr ) = run_ack_with_stderr('-A', 5, $word, $file, '--', '-l');
187166
ok(@{$stdout} > 0, 'Some lines should appear on standard output');

t/mutex.yaml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
# Order doesn't matter. They are reported in alphabetical order.
3+
name: -f and -g
4+
args:
5+
- -f -g WORD
6+
- -g WORD -f
7+
exitcode: 255
8+
stdout:
9+
stderr: |
10+
ack: Options '-f' and '-g' can't be used together.
11+
12+
---
13+
name: -f and -p
14+
args:
15+
- -f -p
16+
- -f -p 2
17+
exitcode: 255
18+
stdout:
19+
stderr: |
20+
ack: Options '-f' and '-p' can't be used together.
21+
22+
---
23+
name: -f and --proximate
24+
args:
25+
- -f --proximate
26+
- -f --proximate 2
27+
exitcode: 255
28+
stdout:
29+
stderr: |
30+
ack: Options '-f' and '--proximate' can't be used together.
31+
32+
---
33+
# Check for abbreviations. https://github.com/beyondgrep/ack3/issues/57
34+
name: -f and --proximate abbreviations
35+
args:
36+
- -f --pro
37+
- -f --prox
38+
- -f --proxi
39+
- -f --proxim
40+
- -f --proxima
41+
- -f --proximat
42+
- -f --proximate
43+
exitcode: 255
44+
stdout:
45+
stderr: |
46+
ack: Options '-f' and '--proximate' can't be used together.
47+
48+
---
49+
name: -f and --match
50+
args:
51+
- -f --match WORD
52+
exitcode: 255
53+
stdout:
54+
stderr: |
55+
ack: Options '-f' and '--match' can't be used together.
56+
57+
---
58+
name: -g and --match
59+
args:
60+
- -g WORD --match WORD
61+
exitcode: 255
62+
stdout:
63+
stderr: |
64+
ack: Options '-g' and '--match' can't be used together.
65+
66+
---
67+
name: --or and --and
68+
args:
69+
- this --or that --and other
70+
exitcode: 255
71+
stdout:
72+
stderr: |
73+
ack: Options '--and' and '--or' can't be used together.

0 commit comments

Comments
 (0)