diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.rs | 3 | ||||
-rw-r--r-- | src/args.rs | 5 | ||||
-rw-r--r-- | src/search_buffer.rs | 20 | ||||
-rw-r--r-- | src/search_stream.rs | 33 | ||||
-rw-r--r-- | src/worker.rs | 12 |
5 files changed, 66 insertions, 7 deletions
@@ -124,6 +124,7 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static> .value_name("FILE").takes_value(true) .multiple(true).number_of_values(1)) .arg(flag("files-with-matches").short("l")) + .arg(flag("files-without-matches")) .arg(flag("with-filename").short("H")) .arg(flag("no-filename")) .arg(flag("heading")) @@ -304,6 +305,8 @@ lazy_static! { lines, and the newline is not counted as part of the pattern."); doc!(h, "files-with-matches", "Only show the path of each file with at least one match."); + doc!(h, "files-without-matches", + "Only show the path of each file that contains zero matches."); doc!(h, "with-filename", "Show file name for each match.", "Prefix each match with the file name that contains it. This is \ diff --git a/src/args.rs b/src/args.rs index a4955d5f..58cdee61 100644 --- a/src/args.rs +++ b/src/args.rs @@ -44,6 +44,7 @@ pub struct Args { context_separator: Vec<u8>, count: bool, files_with_matches: bool, + files_without_matches: bool, eol: u8, files: bool, follow: bool, @@ -158,7 +159,7 @@ impl Args { /// Retrieve the configured file separator. pub fn file_separator(&self) -> Option<Vec<u8>> { - if self.heading && !self.count && !self.files_with_matches { + if self.heading && !self.count && !self.files_with_matches && !self.files_without_matches { Some(b"".to_vec()) } else if self.before_context > 0 || self.after_context > 0 { Some(self.context_separator.clone()) @@ -217,6 +218,7 @@ impl Args { .before_context(self.before_context) .count(self.count) .files_with_matches(self.files_with_matches) + .files_without_matches(self.files_without_matches) .eol(self.eol) .line_number(self.line_number) .invert_match(self.invert_match) @@ -314,6 +316,7 @@ impl<'a> ArgMatches<'a> { context_separator: self.context_separator(), count: self.is_present("count"), files_with_matches: self.is_present("files-with-matches"), + files_without_matches: self.is_present("files-without-matches"), eol: b'\n', files: self.is_present("files"), follow: self.is_present("follow"), diff --git a/src/search_buffer.rs b/src/search_buffer.rs index c7c3bca0..16a161e1 100644 --- a/src/search_buffer.rs +++ b/src/search_buffer.rs @@ -61,6 +61,15 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> { self } + /// If enabled, searching will print the path of files that *don't* match + /// the given pattern. + /// + /// Disabled by default. + pub fn files_without_matches(mut self, yes: bool) -> Self { + self.opts.files_without_matches = yes; + self + } + /// Set the end-of-line byte used by this searcher. pub fn eol(mut self, eol: u8) -> Self { self.opts.eol = eol; @@ -133,6 +142,9 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> { if self.opts.files_with_matches && self.match_count > 0 { self.printer.path(self.path); } + if self.opts.files_without_matches && self.match_count == 0 { + self.printer.path(self.path); + } self.match_count } @@ -278,6 +290,14 @@ and exhibited clearly, with a label attached.\ } #[test] + fn files_without_matches() { + let (count, out) = search( + "zzzz", SHERLOCK, |s| s.files_without_matches(true)); + assert_eq!(0, count); + assert_eq!(out, "/baz.rs\n"); + } + + #[test] fn max_count() { let (count, out) = search( "Sherlock", SHERLOCK, |s| s.max_count(Some(1))); diff --git a/src/search_stream.rs b/src/search_stream.rs index 6ef8c451..b92fa806 100644 --- a/src/search_stream.rs +++ b/src/search_stream.rs @@ -82,6 +82,7 @@ pub struct Options { pub before_context: usize, pub count: bool, pub files_with_matches: bool, + pub files_without_matches: bool, pub eol: u8, pub invert_match: bool, pub line_number: bool, @@ -97,6 +98,7 @@ impl Default for Options { before_context: 0, count: false, files_with_matches: false, + files_without_matches: false, eol: b'\n', invert_match: false, line_number: false, @@ -109,16 +111,17 @@ impl Default for Options { } impl Options { - /// Several options (--quiet, --count, --files-with-matches) imply that - /// we shouldn't ever display matches. + /// Several options (--quiet, --count, --files-with-matches, + /// --files-without-matches) imply that we shouldn't ever display matches. pub fn skip_matches(&self) -> bool { - self.count || self.files_with_matches || self.quiet + self.count || self.files_with_matches || self.files_without_matches + || self.quiet } - /// Some options (--quiet, --files-with-matches) imply that we can stop - /// searching after the first match. + /// Some options (--quiet, --files-with-matches, --files-without-matches) + /// imply that we can stop searching after the first match. pub fn stop_after_first_match(&self) -> bool { - self.files_with_matches || self.quiet + self.files_with_matches || self.files_without_matches || self.quiet } /// Returns true if the search should terminate based on the match count. @@ -199,6 +202,14 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> { self } + /// If enabled, searching will print the path of files without any matches. + /// + /// Disabled by default. + pub fn files_without_matches(mut self, yes: bool) -> Self { + self.opts.files_without_matches = yes; + self + } + /// Set the end-of-line byte used by this searcher. pub fn eol(mut self, eol: u8) -> Self { self.opts.eol = eol; @@ -296,6 +307,8 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> { } else if self.opts.files_with_matches { self.printer.path(self.path); } + } else if self.match_count == 0 && self.opts.files_without_matches { + self.printer.path(self.path); } Ok(self.match_count) } @@ -987,6 +1000,14 @@ fn main() { } #[test] + fn files_without_matches() { + let (count, out) = search_smallcap( + "zzzz", SHERLOCK, |s| s.files_without_matches(true)); + assert_eq!(0, count); + assert_eq!(out, "/baz.rs\n"); + } + + #[test] fn max_count() { let (count, out) = search_smallcap( "Sherlock", SHERLOCK, |s| s.max_count(Some(1))); diff --git a/src/worker.rs b/src/worker.rs index 0ade140a..23ed7549 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -31,6 +31,7 @@ struct Options { before_context: usize, count: bool, files_with_matches: bool, + files_without_matches: bool, eol: u8, invert_match: bool, line_number: bool, @@ -48,6 +49,7 @@ impl Default for Options { before_context: 0, count: false, files_with_matches: false, + files_without_matches: false, eol: b'\n', invert_match: false, line_number: false, @@ -112,6 +114,14 @@ impl WorkerBuilder { self } + /// If enabled, searching will print the path of files without any matches. + /// + /// Disabled by default. + pub fn files_without_matches(mut self, yes: bool) -> Self { + self.opts.files_without_matches = yes; + self + } + /// Set the end-of-line byte used by this searcher. pub fn eol(mut self, eol: u8) -> Self { self.opts.eol = eol; @@ -230,6 +240,7 @@ impl Worker { .before_context(self.opts.before_context) .count(self.opts.count) .files_with_matches(self.opts.files_with_matches) + .files_without_matches(self.opts.files_without_matches) .eol(self.opts.eol) .line_number(self.opts.line_number) .invert_match(self.opts.invert_match) @@ -260,6 +271,7 @@ impl Worker { Ok(searcher .count(self.opts.count) .files_with_matches(self.opts.files_with_matches) + .files_without_matches(self.opts.files_without_matches) .eol(self.opts.eol) .line_number(self.opts.line_number) .invert_match(self.opts.invert_match) |