Skip to content

Commit

Permalink
Add --files-with-matches flag.
Browse files Browse the repository at this point in the history
Closes BurntSushi#26.

Acts like --count but emits only the paths of files with matches,
suitable for piping to xargs. Both mmap and no-mmap searches terminate
after the first match is found. Documentation updated and tests added.
  • Loading branch information
andyleejordan committed Sep 25, 2016
1 parent 1595f0f commit a8f3d9e
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 9 deletions.
1 change: 1 addition & 0 deletions ci/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ disable_cross_doctests() {
}

run_test_suite() {
cargo clean --target $TARGET --verbose
cargo build --target $TARGET --verbose
cargo test --target $TARGET --verbose

Expand Down
5 changes: 5 additions & 0 deletions doc/rg.1
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ Only show count of line matches for each file.
.RS
.RE
.TP
.B \-l, \-\-files\-with\-matches
Only show path of each file with matches.
.RS
.RE
.TP
.B \-\-color \f[I]WHEN\f[]
Whether to use coloring in match.
Valid values are never, always or auto.
Expand Down
3 changes: 3 additions & 0 deletions doc/rg.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ the raw speed of grep.
-c, --count
: Only show count of line matches for each file.

-l, --files-with-matches
: Only show path of each file with matches.

--color *WHEN*
: Whether to use coloring in match. Valid values are never, always or auto.
[default: auto]
Expand Down
8 changes: 7 additions & 1 deletion src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ rg recursively searches your current directory for a regex pattern.
Common options:
-a, --text Search binary files as if they were text.
-c, --count Only show count of line matches for each file.
-l, --files-with-matches Only show path of each file with matches.
--color WHEN Whether to use coloring in match.
Valid values are never, always or auto.
[default: auto]
Expand Down Expand Up @@ -199,6 +200,7 @@ pub struct RawArgs {
flag_context: usize,
flag_context_separator: String,
flag_count: bool,
flag_files_with_matches: bool,
flag_debug: bool,
flag_files: bool,
flag_follow: bool,
Expand Down Expand Up @@ -246,6 +248,7 @@ pub struct Args {
column: bool,
context_separator: Vec<u8>,
count: bool,
files_with_matches: bool,
eol: u8,
files: bool,
follow: bool,
Expand Down Expand Up @@ -370,6 +373,7 @@ impl RawArgs {
column: self.flag_column,
context_separator: unescape(&self.flag_context_separator),
count: self.flag_count,
files_with_matches: self.flag_files_with_matches,
eol: eol,
files: self.flag_files,
follow: self.flag_follow,
Expand Down Expand Up @@ -554,7 +558,7 @@ impl Args {
/// to the writer given.
pub fn out(&self) -> Out {
let mut out = Out::new(self.color);
if self.heading && !self.count {
if self.heading && !self.count && !self.files_with_matches {
out = out.file_separator(b"".to_vec());
} else if self.before_context > 0 || self.after_context > 0 {
out = out.file_separator(self.context_separator.clone());
Expand Down Expand Up @@ -608,6 +612,7 @@ impl Args {
.after_context(self.after_context)
.before_context(self.before_context)
.count(self.count)
.files_with_matches(self.files_with_matches)
.eol(self.eol)
.line_number(self.line_number)
.invert_match(self.invert_match)
Expand All @@ -626,6 +631,7 @@ impl Args {
) -> BufferSearcher<'a, W> {
BufferSearcher::new(printer, grep, path, buf)
.count(self.count)
.files_with_matches(self.files_with_matches)
.eol(self.eol)
.line_number(self.line_number)
.invert_match(self.invert_match)
Expand Down
24 changes: 23 additions & 1 deletion src/search_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
self
}

/// If enabled, searching will print the path instead of each match.
///
/// Disabled by default.
pub fn files_with_matches(mut self, yes: bool) -> Self {
self.opts.files_with_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;
Expand Down Expand Up @@ -96,6 +104,9 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
self.print_match(m.start(), m.end());
}
last_end = m.end();
if self.opts.files_with_matches {
break;
}
}
if self.opts.invert_match {
let upto = self.buf.len();
Expand All @@ -104,13 +115,16 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
if self.opts.count && self.match_count > 0 {
self.printer.path_count(self.path, self.match_count);
}
if self.opts.files_with_matches && self.match_count > 0 {
self.printer.path(self.path);
}
self.match_count
}

#[inline(always)]
pub fn print_match(&mut self, start: usize, end: usize) {
self.match_count += 1;
if self.opts.count {
if self.opts.skip_matches() {
return;
}
self.count_lines(start);
Expand Down Expand Up @@ -237,6 +251,14 @@ and exhibited clearly, with a label attached.\
assert_eq!(out, "/baz.rs:2\n");
}

#[test]
fn files_with_matches() {
let (count, out) = search(
"Sherlock", SHERLOCK, |s| s.files_with_matches(true));
assert_eq!(1, count);
assert_eq!(out, "/baz.rs\n");
}

#[test]
fn invert_match() {
let (count, out) = search(
Expand Down
50 changes: 43 additions & 7 deletions src/search_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub struct Options {
pub after_context: usize,
pub before_context: usize,
pub count: bool,
pub files_with_matches: bool,
pub eol: u8,
pub invert_match: bool,
pub line_number: bool,
Expand All @@ -92,12 +93,22 @@ impl Default for Options {
after_context: 0,
before_context: 0,
count: false,
files_with_matches: false,
eol: b'\n',
invert_match: false,
line_number: false,
text: false,
}
}

}

impl Options {
/// Both --count and --files-with-matches options imply that we should not
/// display matches at all.
pub fn skip_matches(&self) -> bool {
return self.count || self.files_with_matches;
}
}

impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
Expand Down Expand Up @@ -158,6 +169,14 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
self
}

/// If enabled, searching will print the path instead of each match.
///
/// Disabled by default.
pub fn files_with_matches(mut self, yes: bool) -> Self {
self.opts.files_with_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;
Expand Down Expand Up @@ -193,7 +212,7 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
self.line_count = if self.opts.line_number { Some(0) } else { None };
self.last_match = Match::default();
self.after_context_remaining = 0;
loop {
while !self.terminate() {
let upto = self.inp.lastnl;
self.print_after_context(upto);
if !try!(self.fill()) {
Expand All @@ -202,7 +221,7 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
if !self.opts.text && self.inp.is_binary {
break;
}
while self.inp.pos < self.inp.lastnl {
while !self.terminate() && self.inp.pos < self.inp.lastnl {
let matched = self.grep.read_match(
&mut self.last_match,
&mut self.inp.buf[..self.inp.lastnl],
Expand Down Expand Up @@ -234,12 +253,21 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
}
}
}
if self.opts.count && self.match_count > 0 {
self.printer.path_count(self.path, self.match_count);
if self.match_count > 0 {
if self.opts.count {
self.printer.path_count(self.path, self.match_count);
} else if self.opts.files_with_matches {
self.printer.path(self.path);
}
}
Ok(self.match_count)
}

#[inline(always)]
fn terminate(&self) -> bool {
return self.opts.files_with_matches && self.match_count > 0;
}

#[inline(always)]
fn fill(&mut self) -> Result<bool, Error> {
let mut keep = self.inp.lastnl;
Expand Down Expand Up @@ -281,7 +309,7 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {

#[inline(always)]
fn print_before_context(&mut self, upto: usize) {
if self.opts.count || self.opts.before_context == 0 {
if self.opts.skip_matches() || self.opts.before_context == 0 {
return;
}
let start = self.last_printed;
Expand All @@ -304,7 +332,7 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {

#[inline(always)]
fn print_after_context(&mut self, upto: usize) {
if self.opts.count || self.after_context_remaining == 0 {
if self.opts.skip_matches() || self.after_context_remaining == 0 {
return;
}
let start = self.last_printed;
Expand All @@ -322,7 +350,7 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
#[inline(always)]
fn print_match(&mut self, start: usize, end: usize) {
self.match_count += 1;
if self.opts.count {
if self.opts.skip_matches() {
return;
}
self.print_separator(start);
Expand Down Expand Up @@ -992,6 +1020,14 @@ fn main() {
assert_eq!(out, "/baz.rs:2\n");
}

#[test]
fn files_with_matches() {
let (count, out) = search_smallcap(
"Sherlock", SHERLOCK, |s| s.files_with_matches(true));
assert_eq!(1, count);
assert_eq!(out, "/baz.rs\n");
}

#[test]
fn invert_match() {
let (count, out) = search_smallcap(
Expand Down
14 changes: 14 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,20 @@ sherlock!(glob_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
assert_eq!(lines, "file.py:Sherlock\n");
});

sherlock!(count, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
cmd.arg("--count");
let lines: String = wd.stdout(&mut cmd);
let expected = "sherlock:2\n";
assert_eq!(lines, expected);
});

sherlock!(files_with_matches, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
cmd.arg("--files-with-matches");
let lines: String = wd.stdout(&mut cmd);
let expected = "sherlock\n";
assert_eq!(lines, expected);
});

sherlock!(after_context, |wd: WorkDir, mut cmd: Command| {
cmd.arg("-A").arg("1");
let lines: String = wd.stdout(&mut cmd);
Expand Down

0 comments on commit a8f3d9e

Please sign in to comment.