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:
parent
7aa6e87952
commit
46dff8f4be
@ -571,6 +571,11 @@ impl Args {
|
||||
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
|
||||
/// writer given.
|
||||
pub fn printer<W: Terminal + Send>(&self, wtr: W) -> Printer<W> {
|
||||
@ -580,7 +585,6 @@ impl Args {
|
||||
.eol(self.eol)
|
||||
.heading(self.heading)
|
||||
.line_per_match(self.line_per_match)
|
||||
.quiet(self.quiet)
|
||||
.null(self.null)
|
||||
.with_filename(self.with_filename);
|
||||
if let Some(ref rep) = self.replace {
|
||||
@ -660,6 +664,7 @@ impl Args {
|
||||
.eol(self.eol)
|
||||
.line_number(self.line_number)
|
||||
.invert_match(self.invert_match)
|
||||
.quiet(self.quiet)
|
||||
.text(self.text)
|
||||
}
|
||||
|
||||
@ -679,6 +684,7 @@ impl Args {
|
||||
.eol(self.eol)
|
||||
.line_number(self.line_number)
|
||||
.invert_match(self.invert_match)
|
||||
.quiet(self.quiet)
|
||||
.text(self.text)
|
||||
}
|
||||
|
||||
|
47
src/main.rs
47
src/main.rs
@ -27,6 +27,7 @@ use std::path::Path;
|
||||
use std::process;
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::thread;
|
||||
use std::cmp;
|
||||
|
||||
@ -102,6 +103,7 @@ fn run(args: Args) -> Result<u64> {
|
||||
}
|
||||
|
||||
let out = Arc::new(Mutex::new(args.out()));
|
||||
let quiet_matched = QuietMatched::new(args.quiet());
|
||||
let mut workers = vec![];
|
||||
|
||||
let workq = {
|
||||
@ -109,6 +111,7 @@ fn run(args: Args) -> Result<u64> {
|
||||
for _ in 0..threads {
|
||||
let worker = MultiWorker {
|
||||
chan_work: stealer.clone(),
|
||||
quiet_matched: quiet_matched.clone(),
|
||||
out: out.clone(),
|
||||
outbuf: Some(args.outbuf()),
|
||||
worker: Worker {
|
||||
@ -124,11 +127,17 @@ fn run(args: Args) -> Result<u64> {
|
||||
};
|
||||
let mut paths_searched: u64 = 0;
|
||||
for p in paths {
|
||||
if quiet_matched.has_match() {
|
||||
break;
|
||||
}
|
||||
if p == Path::new("-") {
|
||||
paths_searched += 1;
|
||||
workq.push(Work::Stdin);
|
||||
} else {
|
||||
for ent in try!(args.walker(p)) {
|
||||
if quiet_matched.has_match() {
|
||||
break;
|
||||
}
|
||||
paths_searched += 1;
|
||||
workq.push(Work::File(ent));
|
||||
}
|
||||
@ -161,6 +170,9 @@ fn run_one_thread(args: Arc<Args>) -> Result<u64> {
|
||||
|
||||
let mut paths_searched: u64 = 0;
|
||||
for p in paths {
|
||||
if args.quiet() && worker.match_count > 0 {
|
||||
break;
|
||||
}
|
||||
if p == Path::new("-") {
|
||||
paths_searched += 1;
|
||||
let mut printer = args.printer(&mut term);
|
||||
@ -175,6 +187,9 @@ fn run_one_thread(args: Arc<Args>) -> Result<u64> {
|
||||
paths_searched += 1;
|
||||
let mut printer = args.printer(&mut term);
|
||||
if worker.match_count > 0 {
|
||||
if args.quiet() {
|
||||
break;
|
||||
}
|
||||
if let Some(sep) = args.file_separator() {
|
||||
printer = printer.file_separator(sep);
|
||||
}
|
||||
@ -240,6 +255,7 @@ enum WorkReady {
|
||||
|
||||
struct MultiWorker {
|
||||
chan_work: Stealer<Work>,
|
||||
quiet_matched: QuietMatched,
|
||||
out: Arc<Mutex<Out>>,
|
||||
#[cfg(not(windows))]
|
||||
outbuf: Option<ColoredTerminal<term::TerminfoTerminal<Vec<u8>>>>,
|
||||
@ -258,6 +274,9 @@ struct Worker {
|
||||
impl MultiWorker {
|
||||
fn run(mut self) -> u64 {
|
||||
loop {
|
||||
if self.quiet_matched.has_match() {
|
||||
break;
|
||||
}
|
||||
let work = match self.chan_work.steal() {
|
||||
Stolen::Empty | Stolen::Abort => continue,
|
||||
Stolen::Data(Work::Quit) => break,
|
||||
@ -276,6 +295,9 @@ impl MultiWorker {
|
||||
outbuf.clear();
|
||||
let mut printer = self.worker.args.printer(outbuf);
|
||||
self.worker.do_work(&mut printer, work);
|
||||
if self.quiet_matched.set_match(self.worker.match_count > 0) {
|
||||
break;
|
||||
}
|
||||
let outbuf = printer.into_inner();
|
||||
if !outbuf.get_ref().is_empty() {
|
||||
let mut out = self.out.lock().unwrap();
|
||||
@ -359,3 +381,28 @@ impl Worker {
|
||||
).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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,6 @@ pub struct Printer<W> {
|
||||
heading: bool,
|
||||
/// Whether to show every match on its own line.
|
||||
line_per_match: bool,
|
||||
/// Whether to suppress all output.
|
||||
quiet: bool,
|
||||
/// Whether to print NUL bytes after a file path instead of new lines
|
||||
/// or `:`.
|
||||
null: bool,
|
||||
@ -42,8 +40,7 @@ pub struct Printer<W> {
|
||||
replace: Option<Vec<u8>>,
|
||||
/// Whether to prefix each match with the corresponding file name.
|
||||
with_filename: bool,
|
||||
|
||||
/// The choice of Colours
|
||||
/// The choice of colors.
|
||||
color_choice: ColorChoice
|
||||
}
|
||||
|
||||
@ -54,7 +51,6 @@ struct ColorChoice {
|
||||
}
|
||||
|
||||
impl ColorChoice {
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn new() -> ColorChoice {
|
||||
ColorChoice {
|
||||
@ -86,7 +82,6 @@ impl<W: Terminal + Send> Printer<W> {
|
||||
file_separator: None,
|
||||
heading: false,
|
||||
line_per_match: false,
|
||||
quiet: false,
|
||||
null: false,
|
||||
replace: None,
|
||||
with_filename: false,
|
||||
@ -141,12 +136,6 @@ impl<W: Terminal + Send> Printer<W> {
|
||||
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
|
||||
/// given.
|
||||
///
|
||||
@ -168,11 +157,6 @@ impl<W: Terminal + Send> Printer<W> {
|
||||
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.
|
||||
pub fn into_inner(mut self) -> W {
|
||||
let _ = self.wtr.flush();
|
||||
@ -222,9 +206,6 @@ impl<W: Terminal + Send> Printer<W> {
|
||||
/// Prints the context separator.
|
||||
pub fn context_separate(&mut self) {
|
||||
// N.B. We can't use `write` here because of borrowing restrictions.
|
||||
if self.quiet {
|
||||
return;
|
||||
}
|
||||
if self.context_separator.is_empty() {
|
||||
return;
|
||||
}
|
||||
@ -398,9 +379,6 @@ impl<W: Terminal + Send> Printer<W> {
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
if self.quiet {
|
||||
return;
|
||||
}
|
||||
self.has_printed = true;
|
||||
let _ = self.wtr.write_all(buf);
|
||||
}
|
||||
@ -411,9 +389,6 @@ impl<W: Terminal + Send> Printer<W> {
|
||||
}
|
||||
|
||||
fn write_file_sep(&mut self) {
|
||||
if self.quiet {
|
||||
return;
|
||||
}
|
||||
if let Some(ref sep) = self.file_separator {
|
||||
self.has_printed = true;
|
||||
let _ = self.wtr.write_all(sep);
|
||||
|
@ -81,6 +81,13 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
|
||||
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.
|
||||
pub fn text(mut self, yes: bool) -> Self {
|
||||
self.opts.text = yes;
|
||||
@ -104,7 +111,7 @@ impl<'a, W: Send + Terminal> BufferSearcher<'a, W> {
|
||||
self.print_match(m.start(), m.end());
|
||||
}
|
||||
last_end = m.end();
|
||||
if self.printer.is_quiet() || self.opts.files_with_matches {
|
||||
if self.opts.stop_after_first_match() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ pub struct Options {
|
||||
pub eol: u8,
|
||||
pub invert_match: bool,
|
||||
pub line_number: bool,
|
||||
pub quiet: bool,
|
||||
pub text: bool,
|
||||
}
|
||||
|
||||
@ -97,6 +98,7 @@ impl Default for Options {
|
||||
eol: b'\n',
|
||||
invert_match: false,
|
||||
line_number: false,
|
||||
quiet: false,
|
||||
text: false,
|
||||
}
|
||||
}
|
||||
@ -104,10 +106,16 @@ impl Default for Options {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Both --count and --files-with-matches options imply that we should not
|
||||
/// display matches at all.
|
||||
/// Several options (--quiet, --count, --files-with-matches) imply that
|
||||
/// we shouldn't ever display matches.
|
||||
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
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn text(mut self, yes: bool) -> Self {
|
||||
self.opts.text = yes;
|
||||
@ -265,8 +280,7 @@ impl<'a, R: io::Read, W: Terminal + Send> Searcher<'a, R, W> {
|
||||
|
||||
#[inline(always)]
|
||||
fn terminate(&self) -> bool {
|
||||
self.match_count > 0
|
||||
&& (self.printer.is_quiet() || self.opts.files_with_matches)
|
||||
self.match_count > 0 && self.opts.stop_after_first_match()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user