1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-06-04 05:57:39 +02:00

Be better with short circuiting with --quiet.

It didn't make sense for --quiet to be part of the printer, because --quiet
doesn't just mean "don't print," it also means, "stop after the first
match is found." This needs to be wired all the way up through directory
traversal, and it also needs to cause all of the search workers to quit
as well. We do it with an atomic that is only checked with --quiet is
given.

Fixes #116.
This commit is contained in:
Andrew Gallant 2016-09-28 20:50:50 -04:00
parent 7aa6e87952
commit 46dff8f4be
5 changed files with 82 additions and 33 deletions

View File

@ -571,6 +571,11 @@ impl Args {
self.mmap self.mmap
} }
/// Whether ripgrep should be quiet or not.
pub fn quiet(&self) -> bool {
self.quiet
}
/// Create a new printer of individual search results that writes to the /// Create a new printer of individual search results that writes to the
/// writer given. /// writer given.
pub fn printer<W: Terminal + Send>(&self, wtr: W) -> Printer<W> { pub fn printer<W: Terminal + Send>(&self, wtr: W) -> Printer<W> {
@ -580,7 +585,6 @@ impl Args {
.eol(self.eol) .eol(self.eol)
.heading(self.heading) .heading(self.heading)
.line_per_match(self.line_per_match) .line_per_match(self.line_per_match)
.quiet(self.quiet)
.null(self.null) .null(self.null)
.with_filename(self.with_filename); .with_filename(self.with_filename);
if let Some(ref rep) = self.replace { if let Some(ref rep) = self.replace {
@ -660,6 +664,7 @@ impl Args {
.eol(self.eol) .eol(self.eol)
.line_number(self.line_number) .line_number(self.line_number)
.invert_match(self.invert_match) .invert_match(self.invert_match)
.quiet(self.quiet)
.text(self.text) .text(self.text)
} }
@ -679,6 +684,7 @@ impl Args {
.eol(self.eol) .eol(self.eol)
.line_number(self.line_number) .line_number(self.line_number)
.invert_match(self.invert_match) .invert_match(self.invert_match)
.quiet(self.quiet)
.text(self.text) .text(self.text)
} }

View File

@ -27,6 +27,7 @@ use std::path::Path;
use std::process; use std::process;
use std::result; use std::result;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread; use std::thread;
use std::cmp; use std::cmp;
@ -102,6 +103,7 @@ fn run(args: Args) -> Result<u64> {
} }
let out = Arc::new(Mutex::new(args.out())); let out = Arc::new(Mutex::new(args.out()));
let quiet_matched = QuietMatched::new(args.quiet());
let mut workers = vec![]; let mut workers = vec![];
let workq = { let workq = {
@ -109,6 +111,7 @@ fn run(args: Args) -> Result<u64> {
for _ in 0..threads { for _ in 0..threads {
let worker = MultiWorker { let worker = MultiWorker {
chan_work: stealer.clone(), chan_work: stealer.clone(),
quiet_matched: quiet_matched.clone(),
out: out.clone(), out: out.clone(),
outbuf: Some(args.outbuf()), outbuf: Some(args.outbuf()),
worker: Worker { worker: Worker {
@ -124,11 +127,17 @@ fn run(args: Args) -> Result<u64> {
}; };
let mut paths_searched: u64 = 0; let mut paths_searched: u64 = 0;
for p in paths { for p in paths {
if quiet_matched.has_match() {
break;
}
if p == Path::new("-") { if p == Path::new("-") {
paths_searched += 1; paths_searched += 1;
workq.push(Work::Stdin); workq.push(Work::Stdin);
} else { } else {
for ent in try!(args.walker(p)) { for ent in try!(args.walker(p)) {
if quiet_matched.has_match() {
break;
}
paths_searched += 1; paths_searched += 1;
workq.push(Work::File(ent)); workq.push(Work::File(ent));
} }
@ -161,6 +170,9 @@ fn run_one_thread(args: Arc<Args>) -> Result<u64> {
let mut paths_searched: u64 = 0; let mut paths_searched: u64 = 0;
for p in paths { for p in paths {
if args.quiet() && worker.match_count > 0 {
break;
}
if p == Path::new("-") { if p == Path::new("-") {
paths_searched += 1; paths_searched += 1;
let mut printer = args.printer(&mut term); let mut printer = args.printer(&mut term);
@ -175,6 +187,9 @@ fn run_one_thread(args: Arc<Args>) -> Result<u64> {
paths_searched += 1; paths_searched += 1;
let mut printer = args.printer(&mut term); let mut printer = args.printer(&mut term);
if worker.match_count > 0 { if worker.match_count > 0 {
if args.quiet() {
break;
}
if let Some(sep) = args.file_separator() { if let Some(sep) = args.file_separator() {
printer = printer.file_separator(sep); printer = printer.file_separator(sep);
} }
@ -240,6 +255,7 @@ enum WorkReady {
struct MultiWorker { struct MultiWorker {
chan_work: Stealer<Work>, chan_work: Stealer<Work>,
quiet_matched: QuietMatched,
out: Arc<Mutex<Out>>, out: Arc<Mutex<Out>>,
#[cfg(not(windows))] #[cfg(not(windows))]
outbuf: Option<ColoredTerminal<term::TerminfoTerminal<Vec<u8>>>>, outbuf: Option<ColoredTerminal<term::TerminfoTerminal<Vec<u8>>>>,
@ -258,6 +274,9 @@ struct Worker {
impl MultiWorker { impl MultiWorker {
fn run(mut self) -> u64 { fn run(mut self) -> u64 {
loop { loop {
if self.quiet_matched.has_match() {
break;
}
let work = match self.chan_work.steal() { let work = match self.chan_work.steal() {
Stolen::Empty | Stolen::Abort => continue, Stolen::Empty | Stolen::Abort => continue,
Stolen::Data(Work::Quit) => break, Stolen::Data(Work::Quit) => break,
@ -276,6 +295,9 @@ impl MultiWorker {
outbuf.clear(); outbuf.clear();
let mut printer = self.worker.args.printer(outbuf); let mut printer = self.worker.args.printer(outbuf);
self.worker.do_work(&mut printer, work); self.worker.do_work(&mut printer, work);
if self.quiet_matched.set_match(self.worker.match_count > 0) {
break;
}
let outbuf = printer.into_inner(); let outbuf = printer.into_inner();
if !outbuf.get_ref().is_empty() { if !outbuf.get_ref().is_empty() {
let mut out = self.out.lock().unwrap(); let mut out = self.out.lock().unwrap();
@ -359,3 +381,28 @@ impl Worker {
).run()) ).run())
} }
} }
#[derive(Clone, Debug)]
struct QuietMatched(Arc<Option<AtomicBool>>);
impl QuietMatched {
fn new(quiet: bool) -> QuietMatched {
let atomic = if quiet { Some(AtomicBool::new(false)) } else { None };
QuietMatched(Arc::new(atomic))
}
fn has_match(&self) -> bool {
match *self.0 {
None => false,
Some(ref matched) => matched.load(Ordering::SeqCst),
}
}
fn set_match(&self, yes: bool) -> bool {
match *self.0 {
None => false,
Some(_) if !yes => false,
Some(ref m) => { m.store(true, Ordering::SeqCst); true }
}
}
}

View File

@ -33,8 +33,6 @@ pub struct Printer<W> {
heading: bool, heading: bool,
/// Whether to show every match on its own line. /// Whether to show every match on its own line.
line_per_match: bool, line_per_match: bool,
/// Whether to suppress all output.
quiet: bool,
/// Whether to print NUL bytes after a file path instead of new lines /// Whether to print NUL bytes after a file path instead of new lines
/// or `:`. /// or `:`.
null: bool, null: bool,
@ -42,8 +40,7 @@ pub struct Printer<W> {
replace: Option<Vec<u8>>, replace: Option<Vec<u8>>,
/// Whether to prefix each match with the corresponding file name. /// Whether to prefix each match with the corresponding file name.
with_filename: bool, with_filename: bool,
/// The choice of colors.
/// The choice of Colours
color_choice: ColorChoice color_choice: ColorChoice
} }
@ -54,7 +51,6 @@ struct ColorChoice {
} }
impl ColorChoice { impl ColorChoice {
#[cfg(unix)] #[cfg(unix)]
pub fn new() -> ColorChoice { pub fn new() -> ColorChoice {
ColorChoice { ColorChoice {
@ -86,7 +82,6 @@ impl<W: Terminal + Send> Printer<W> {
file_separator: None, file_separator: None,
heading: false, heading: false,
line_per_match: false, line_per_match: false,
quiet: false,
null: false, null: false,
replace: None, replace: None,
with_filename: false, with_filename: false,
@ -141,12 +136,6 @@ impl<W: Terminal + Send> Printer<W> {
self self
} }
/// When set, all output is suppressed.
pub fn quiet(mut self, yes: bool) -> Printer<W> {
self.quiet = yes;
self
}
/// Replace every match in each matching line with the replacement string /// Replace every match in each matching line with the replacement string
/// given. /// given.
/// ///
@ -168,11 +157,6 @@ impl<W: Terminal + Send> Printer<W> {
self.has_printed self.has_printed
} }
/// Returns true if the printer has been configured to be quiet.
pub fn is_quiet(&self) -> bool {
self.quiet
}
/// Flushes the underlying writer and returns it. /// Flushes the underlying writer and returns it.
pub fn into_inner(mut self) -> W { pub fn into_inner(mut self) -> W {
let _ = self.wtr.flush(); let _ = self.wtr.flush();
@ -222,9 +206,6 @@ impl<W: Terminal + Send> Printer<W> {
/// Prints the context separator. /// Prints the context separator.
pub fn context_separate(&mut self) { pub fn context_separate(&mut self) {
// N.B. We can't use `write` here because of borrowing restrictions. // N.B. We can't use `write` here because of borrowing restrictions.
if self.quiet {
return;
}
if self.context_separator.is_empty() { if self.context_separator.is_empty() {
return; return;
} }
@ -398,9 +379,6 @@ impl<W: Terminal + Send> Printer<W> {
} }
fn write(&mut self, buf: &[u8]) { fn write(&mut self, buf: &[u8]) {
if self.quiet {
return;
}
self.has_printed = true; self.has_printed = true;
let _ = self.wtr.write_all(buf); let _ = self.wtr.write_all(buf);
} }
@ -411,9 +389,6 @@ impl<W: Terminal + Send> Printer<W> {
} }
fn write_file_sep(&mut self) { fn write_file_sep(&mut self) {
if self.quiet {
return;
}
if let Some(ref sep) = self.file_separator { if let Some(ref sep) = self.file_separator {
self.has_printed = true; self.has_printed = true;
let _ = self.wtr.write_all(sep); let _ = self.wtr.write_all(sep);

View File

@ -81,6 +81,13 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
self self
} }
/// If enabled, don't show any output and quit searching after the first
/// match is found.
pub fn quiet(mut self, yes: bool) -> Self {
self.opts.quiet = yes;
self
}
/// If enabled, search binary files as if they were text. /// If enabled, search binary files as if they were text.
pub fn text(mut self, yes: bool) -> Self { pub fn text(mut self, yes: bool) -> Self {
self.opts.text = yes; self.opts.text = yes;
@ -104,7 +111,7 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
self.print_match(m.start(), m.end()); self.print_match(m.start(), m.end());
} }
last_end = m.end(); last_end = m.end();
if self.printer.is_quiet() || self.opts.files_with_matches { if self.opts.stop_after_first_match() {
break; break;
} }
} }

View File

@ -84,6 +84,7 @@ pub struct Options {
pub eol: u8, pub eol: u8,
pub invert_match: bool, pub invert_match: bool,
pub line_number: bool, pub line_number: bool,
pub quiet: bool,
pub text: bool, pub text: bool,
} }
@ -97,6 +98,7 @@ impl Default for Options {
eol: b'\n', eol: b'\n',
invert_match: false, invert_match: false,
line_number: false, line_number: false,
quiet: false,
text: false, text: false,
} }
} }
@ -104,10 +106,16 @@ impl Default for Options {
} }
impl Options { impl Options {
/// Both --count and --files-with-matches options imply that we should not /// Several options (--quiet, --count, --files-with-matches) imply that
/// display matches at all. /// we shouldn't ever display matches.
pub fn skip_matches(&self) -> bool { pub fn skip_matches(&self) -> bool {
return self.count || self.files_with_matches; self.count || self.files_with_matches || self.quiet
}
/// Some options (--quiet, --files-with-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
} }
} }
@ -197,6 +205,13 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
self self
} }
/// If enabled, don't show any output and quit searching after the first
/// match is found.
pub fn quiet(mut self, yes: bool) -> Self {
self.opts.quiet = yes;
self
}
/// If enabled, search binary files as if they were text. /// If enabled, search binary files as if they were text.
pub fn text(mut self, yes: bool) -> Self { pub fn text(mut self, yes: bool) -> Self {
self.opts.text = yes; self.opts.text = yes;
@ -265,8 +280,7 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
#[inline(always)] #[inline(always)]
fn terminate(&self) -> bool { fn terminate(&self) -> bool {
self.match_count > 0 self.match_count > 0 && self.opts.stop_after_first_match()
&& (self.printer.is_quiet() || self.opts.files_with_matches)
} }
#[inline(always)] #[inline(always)]