diff --git a/CHANGELOG.md b/CHANGELOG.md index e37735e2..77a2f6a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ Bug fixes: Fix a bug where the "bytes searched" in `--stats` output could be incorrect. * [BUG #2990](https://github.com/BurntSushi/ripgrep/issues/2990): Fix a bug where ripgrep would mishandle globs that ended with a `.`. +* [BUG #3108](https://github.com/BurntSushi/ripgrep/issues/3108): + Fix a bug where `-q --files-without-match` inverted the exit code. Feature enhancements: diff --git a/crates/core/flags/hiargs.rs b/crates/core/flags/hiargs.rs index 53490a6d..ccb4cb31 100644 --- a/crates/core/flags/hiargs.rs +++ b/crates/core/flags/hiargs.rs @@ -562,7 +562,16 @@ impl HiArgs { wtr: W, ) -> Printer { let summary_kind = if self.quiet { - SummaryKind::Quiet + match search_mode { + SearchMode::FilesWithMatches + | SearchMode::Count + | SearchMode::CountMatches + | SearchMode::JSON + | SearchMode::Standard => SummaryKind::QuietWithMatch, + SearchMode::FilesWithoutMatch => { + SummaryKind::QuietWithoutMatch + } + } } else { match search_mode { SearchMode::FilesWithMatches => SummaryKind::PathWithMatch, diff --git a/crates/printer/src/summary.rs b/crates/printer/src/summary.rs index 275419d4..c4313556 100644 --- a/crates/printer/src/summary.rs +++ b/crates/printer/src/summary.rs @@ -87,7 +87,13 @@ pub enum SummaryKind { /// /// Note that if `stats` is enabled, then searching continues in order to /// compute statistics. - Quiet, + QuietWithMatch, + /// Don't show any output and the stop the search once a non-matching file + /// is found. + /// + /// Note that if `stats` is enabled, then searching continues in order to + /// compute statistics. + QuietWithoutMatch, } impl SummaryKind { @@ -101,7 +107,7 @@ impl SummaryKind { match *self { PathWithMatch | PathWithoutMatch => true, - Count | CountMatches | Quiet => false, + Count | CountMatches | QuietWithMatch | QuietWithoutMatch => false, } } @@ -112,7 +118,8 @@ impl SummaryKind { match *self { CountMatches => true, - Count | PathWithMatch | PathWithoutMatch | Quiet => false, + Count | PathWithMatch | PathWithoutMatch | QuietWithMatch + | QuietWithoutMatch => false, } } @@ -122,8 +129,10 @@ impl SummaryKind { use self::SummaryKind::*; match *self { - PathWithMatch | Quiet => true, - Count | CountMatches | PathWithoutMatch => false, + PathWithMatch | QuietWithMatch => true, + Count | CountMatches | PathWithoutMatch | QuietWithoutMatch => { + false + } } } } @@ -246,9 +255,9 @@ impl SummaryBuilder { /// /// When this is enabled, this printer may need to do extra work in order /// to compute certain statistics, which could cause the search to take - /// longer. For example, in `Quiet` mode, a search can quit after finding - /// the first match, but if `stats` is enabled, then the search will - /// continue after the first match in order to compute statistics. + /// longer. For example, in `QuietWithMatch` mode, a search can quit after + /// finding the first match, but if `stats` is enabled, then the search + /// will continue after the first match in order to compute statistics. /// /// For a complete description of available statistics, see [`Stats`]. /// @@ -505,7 +514,9 @@ impl<'p, 's, M: Matcher, W: WriteColor> SummarySink<'p, 's, M, W> { /// search. pub fn has_match(&self) -> bool { match self.summary.config.kind { - SummaryKind::PathWithoutMatch => self.match_count == 0, + SummaryKind::PathWithoutMatch | SummaryKind::QuietWithoutMatch => { + self.match_count == 0 + } _ => self.match_count > 0, } } @@ -749,14 +760,14 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> { // don't quit and therefore search the entire contents of the file. // // There is an unfortunate inconsistency here. Namely, when using - // Quiet or PathWithMatch, then the printer can quit after the first - // match seen, which could be long before seeing binary data. This - // means that using PathWithMatch can print a path where as using + // QuietWithMatch or PathWithMatch, then the printer can quit after the + // first match seen, which could be long before seeing binary data. + // This means that using PathWithMatch can print a path where as using // Count might not print it at all because of binary data. // // It's not possible to fix this without also potentially significantly - // impacting the performance of Quiet or PathWithMatch, so we accept - // the bug. + // impacting the performance of QuietWithMatch or PathWithMatch, so we + // accept the bug. if self.binary_byte_offset.is_some() && searcher.binary_detection().quit_byte().is_some() { @@ -798,7 +809,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> { self.write_path_line(searcher)?; } } - SummaryKind::Quiet => {} + SummaryKind::QuietWithMatch | SummaryKind::QuietWithoutMatch => {} } Ok(()) } @@ -1122,7 +1133,7 @@ and exhibited clearly, with a label attached. fn quiet() { let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap(); let mut printer = SummaryBuilder::new() - .kind(SummaryKind::Quiet) + .kind(SummaryKind::QuietWithMatch) .build_no_color(vec![]); let match_count = { let mut sink = printer.sink_with_path(&matcher, "sherlock"); @@ -1144,7 +1155,7 @@ and exhibited clearly, with a label attached. fn quiet_with_stats() { let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap(); let mut printer = SummaryBuilder::new() - .kind(SummaryKind::Quiet) + .kind(SummaryKind::QuietWithMatch) .stats(true) .build_no_color(vec![]); let match_count = { diff --git a/tests/regression.rs b/tests/regression.rs index 22c59a09..51c8bdb7 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -1476,3 +1476,44 @@ rgtest!(r2990_trip_over_trailing_dot, |dir: Dir, _cmd: TestCommand| { let got = dir.command().args(&["--files", "-g", "!asdf./"]).stdout(); eqnice!("asdf/foo\n", got); }); + +// See: https://github.com/BurntSushi/ripgrep/issues/3108 +rgtest!(r3108_files_without_match_quiet_exit, |dir: Dir, _: TestCommand| { + dir.create("yes-match", "abc"); + dir.create("non-match", "xyz"); + + dir.command().args(&["-q", "abc", "non-match"]).assert_exit_code(1); + dir.command().args(&["-q", "abc", "yes-match"]).assert_exit_code(0); + dir.command() + .args(&["--files-with-matches", "-q", "abc", "non-match"]) + .assert_exit_code(1); + dir.command() + .args(&["--files-with-matches", "-q", "abc", "yes-match"]) + .assert_exit_code(0); + + dir.command() + .args(&["--files-without-match", "abc", "non-match"]) + .assert_exit_code(0); + dir.command() + .args(&["--files-without-match", "abc", "yes-match"]) + .assert_exit_code(1); + + let got = dir + .command() + .args(&["--files-without-match", "abc", "non-match"]) + .stdout(); + eqnice!("non-match\n", got); + + dir.command() + .args(&["--files-without-match", "-q", "abc", "non-match"]) + .assert_exit_code(0); + dir.command() + .args(&["--files-without-match", "-q", "abc", "yes-match"]) + .assert_exit_code(1); + + let got = dir + .command() + .args(&["--files-without-match", "-q", "abc", "non-match"]) + .stdout(); + eqnice!("", got); +});