mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-11-23 21:54:45 +02:00
printer: finish removal of max_matches
This finishes what I started in commit
a6e0be3c90.
Specifically, the `max_matches` configuration has been moved to the
`grep-searcher` crate and *removed* from the `grep-printer` crate. The
commit message has the details for why we're doing this, but the short
story is to fix #3076.
Note that this is a breaking change for `grep-printer`, so this will
require a semver incompatible release.
This commit is contained in:
@@ -596,7 +596,6 @@ impl HiArgs {
|
||||
) -> grep::printer::JSON<W> {
|
||||
grep::printer::JSONBuilder::new()
|
||||
.pretty(false)
|
||||
.max_matches(self.max_count)
|
||||
.always_begin_end(false)
|
||||
.replacement(self.replace.clone().map(|r| r.into()))
|
||||
.build(wtr)
|
||||
@@ -656,7 +655,6 @@ impl HiArgs {
|
||||
.exclude_zero(!self.include_zero)
|
||||
.hyperlink(self.hyperlink_config.clone())
|
||||
.kind(kind)
|
||||
.max_matches(self.max_count)
|
||||
.path(self.with_filename)
|
||||
.path_terminator(self.path_terminator.clone())
|
||||
.separator_field(b":".to_vec())
|
||||
|
||||
@@ -7,9 +7,7 @@ use std::{
|
||||
|
||||
use {
|
||||
grep_matcher::{Match, Matcher},
|
||||
grep_searcher::{
|
||||
Searcher, Sink, SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
||||
},
|
||||
grep_searcher::{Searcher, Sink, SinkContext, SinkFinish, SinkMatch},
|
||||
serde_json as json,
|
||||
};
|
||||
|
||||
@@ -26,7 +24,6 @@ use crate::{
|
||||
#[derive(Debug, Clone)]
|
||||
struct Config {
|
||||
pretty: bool,
|
||||
max_matches: Option<u64>,
|
||||
always_begin_end: bool,
|
||||
replacement: Arc<Option<Vec<u8>>>,
|
||||
}
|
||||
@@ -35,7 +32,6 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
pretty: false,
|
||||
max_matches: None,
|
||||
always_begin_end: false,
|
||||
replacement: Arc::new(None),
|
||||
}
|
||||
@@ -85,16 +81,6 @@ impl JSONBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum amount of matches that are printed.
|
||||
///
|
||||
/// If multi line search is enabled and a match spans multiple lines, then
|
||||
/// that match is counted exactly once for the purposes of enforcing this
|
||||
/// limit, regardless of how many lines it spans.
|
||||
pub fn max_matches(&mut self, limit: Option<u64>) -> &mut JSONBuilder {
|
||||
self.config.max_matches = limit;
|
||||
self
|
||||
}
|
||||
|
||||
/// When enabled, the `begin` and `end` messages are always emitted, even
|
||||
/// when no match is found.
|
||||
///
|
||||
@@ -526,7 +512,6 @@ impl<W: io::Write> JSON<W> {
|
||||
path: None,
|
||||
start_time: Instant::now(),
|
||||
match_count: 0,
|
||||
after_context_remaining: 0,
|
||||
binary_byte_offset: None,
|
||||
begin_printed: false,
|
||||
stats: Stats::new(),
|
||||
@@ -553,7 +538,6 @@ impl<W: io::Write> JSON<W> {
|
||||
path: Some(path.as_ref()),
|
||||
start_time: Instant::now(),
|
||||
match_count: 0,
|
||||
after_context_remaining: 0,
|
||||
binary_byte_offset: None,
|
||||
begin_printed: false,
|
||||
stats: Stats::new(),
|
||||
@@ -616,7 +600,6 @@ pub struct JSONSink<'p, 's, M: Matcher, W> {
|
||||
path: Option<&'p Path>,
|
||||
start_time: Instant,
|
||||
match_count: u64,
|
||||
after_context_remaining: u64,
|
||||
binary_byte_offset: Option<u64>,
|
||||
begin_printed: bool,
|
||||
stats: Stats,
|
||||
@@ -721,32 +704,6 @@ impl<'p, 's, M: Matcher, W: io::Write> JSONSink<'p, 's, M, W> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if this printer should quit.
|
||||
///
|
||||
/// This implements the logic for handling quitting after seeing a certain
|
||||
/// amount of matches. In most cases, the logic is simple, but we must
|
||||
/// permit all "after" contextual lines to print after reaching the limit.
|
||||
fn should_quit(&self) -> bool {
|
||||
let limit = match self.json.config.max_matches {
|
||||
None => return false,
|
||||
Some(limit) => limit,
|
||||
};
|
||||
if self.match_count < limit {
|
||||
return false;
|
||||
}
|
||||
self.after_context_remaining == 0
|
||||
}
|
||||
|
||||
/// Returns whether the current match count exceeds the configured limit.
|
||||
/// If there is no limit, then this always returns false.
|
||||
fn match_more_than_limit(&self) -> bool {
|
||||
let limit = match self.json.config.max_matches {
|
||||
None => return false,
|
||||
Some(limit) => limit,
|
||||
};
|
||||
self.match_count > limit
|
||||
}
|
||||
|
||||
/// Write the "begin" message.
|
||||
fn write_begin_message(&mut self) -> io::Result<()> {
|
||||
if self.begin_printed {
|
||||
@@ -767,22 +724,8 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
||||
searcher: &Searcher,
|
||||
mat: &SinkMatch<'_>,
|
||||
) -> Result<bool, io::Error> {
|
||||
self.write_begin_message()?;
|
||||
|
||||
self.match_count += 1;
|
||||
// When we've exceeded our match count, then the remaining context
|
||||
// lines should not be reset, but instead, decremented. This avoids a
|
||||
// bug where we display more matches than a configured limit. The main
|
||||
// idea here is that 'matched' might be called again while printing
|
||||
// an after-context line. In that case, we should treat this as a
|
||||
// contextual line rather than a matching line for the purposes of
|
||||
// termination.
|
||||
if self.match_more_than_limit() {
|
||||
self.after_context_remaining =
|
||||
self.after_context_remaining.saturating_sub(1);
|
||||
} else {
|
||||
self.after_context_remaining = searcher.after_context() as u64;
|
||||
}
|
||||
self.write_begin_message()?;
|
||||
|
||||
self.record_matches(
|
||||
searcher,
|
||||
@@ -806,7 +749,7 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
||||
submatches: submatches.as_slice(),
|
||||
});
|
||||
self.json.write_message(&msg)?;
|
||||
Ok(!self.should_quit())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn context(
|
||||
@@ -817,10 +760,6 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
||||
self.write_begin_message()?;
|
||||
self.json.matches.clear();
|
||||
|
||||
if ctx.kind() == &SinkContextKind::After {
|
||||
self.after_context_remaining =
|
||||
self.after_context_remaining.saturating_sub(1);
|
||||
}
|
||||
let submatches = if searcher.invert_match() {
|
||||
self.record_matches(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
|
||||
self.replace(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
|
||||
@@ -840,7 +779,7 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
||||
submatches: submatches.as_slice(),
|
||||
});
|
||||
self.json.write_message(&msg)?;
|
||||
Ok(!self.should_quit())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn binary_data(
|
||||
@@ -864,11 +803,7 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
||||
self.json.wtr.reset_count();
|
||||
self.start_time = Instant::now();
|
||||
self.match_count = 0;
|
||||
self.after_context_remaining = 0;
|
||||
self.binary_byte_offset = None;
|
||||
if self.json.config.max_matches == Some(0) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !self.json.config.always_begin_end {
|
||||
return Ok(true);
|
||||
@@ -1015,9 +950,9 @@ and exhibited clearly, with a label attached.\
|
||||
#[test]
|
||||
fn max_matches() {
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer =
|
||||
JSONBuilder::new().max_matches(Some(1)).build(vec![]);
|
||||
let mut printer = JSONBuilder::new().build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.max_matches(Some(1))
|
||||
.build()
|
||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||
.unwrap();
|
||||
@@ -1042,10 +977,10 @@ d
|
||||
e
|
||||
";
|
||||
let matcher = RegexMatcher::new(r"d").unwrap();
|
||||
let mut printer =
|
||||
JSONBuilder::new().max_matches(Some(1)).build(vec![]);
|
||||
let mut printer = JSONBuilder::new().build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.after_context(2)
|
||||
.max_matches(Some(1))
|
||||
.build()
|
||||
.search_reader(
|
||||
&matcher,
|
||||
|
||||
@@ -32,7 +32,6 @@ struct Config {
|
||||
hyperlink: HyperlinkConfig,
|
||||
stats: bool,
|
||||
path: bool,
|
||||
max_matches: Option<u64>,
|
||||
exclude_zero: bool,
|
||||
separator_field: Arc<Vec<u8>>,
|
||||
separator_path: Option<u8>,
|
||||
@@ -47,7 +46,6 @@ impl Default for Config {
|
||||
hyperlink: HyperlinkConfig::default(),
|
||||
stats: false,
|
||||
path: true,
|
||||
max_matches: None,
|
||||
exclude_zero: true,
|
||||
separator_field: Arc::new(b":".to_vec()),
|
||||
separator_path: None,
|
||||
@@ -282,18 +280,6 @@ impl SummaryBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum amount of matches that are printed.
|
||||
///
|
||||
/// If multi line search is enabled and a match spans multiple lines, then
|
||||
/// that match is counted exactly once for the purposes of enforcing this
|
||||
/// limit, regardless of how many lines it spans.
|
||||
///
|
||||
/// This is disabled by default.
|
||||
pub fn max_matches(&mut self, limit: Option<u64>) -> &mut SummaryBuilder {
|
||||
self.config.max_matches = limit;
|
||||
self
|
||||
}
|
||||
|
||||
/// Exclude count-related summary results with no matches.
|
||||
///
|
||||
/// When enabled and the mode is either `Count` or `CountMatches`, then
|
||||
@@ -555,19 +541,6 @@ impl<'p, 's, M: Matcher, W: WriteColor> SummarySink<'p, 's, M, W> {
|
||||
searcher.multi_line_with_matcher(&self.matcher)
|
||||
}
|
||||
|
||||
/// Returns true if this printer should quit.
|
||||
///
|
||||
/// This implements the logic for handling quitting after seeing a certain
|
||||
/// amount of matches. In most cases, the logic is simple, but we must
|
||||
/// permit all "after" contextual lines to print after reaching the limit.
|
||||
fn should_quit(&self) -> bool {
|
||||
let limit = match self.summary.config.max_matches {
|
||||
None => return false,
|
||||
Some(limit) => limit,
|
||||
};
|
||||
self.match_count >= limit
|
||||
}
|
||||
|
||||
/// If this printer has a file path associated with it, then this will
|
||||
/// write that path to the underlying writer followed by a line terminator.
|
||||
/// (If a path terminator is set, then that is used instead of the line
|
||||
@@ -700,7 +673,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
||||
} else if self.summary.config.kind.quit_early() {
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(!self.should_quit())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn binary_data(
|
||||
@@ -731,10 +704,6 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
||||
self.start_time = Instant::now();
|
||||
self.match_count = 0;
|
||||
self.binary_byte_offset = None;
|
||||
if self.summary.config.max_matches == Some(0) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -1027,9 +996,9 @@ and exhibited clearly, with a label attached.
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.max_matches(Some(1))
|
||||
.build_no_color(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.max_matches(Some(1))
|
||||
.build()
|
||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||
.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user