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
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
src/main.rs
47
src/main.rs
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user