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::JSON<W> {
|
||||||
grep::printer::JSONBuilder::new()
|
grep::printer::JSONBuilder::new()
|
||||||
.pretty(false)
|
.pretty(false)
|
||||||
.max_matches(self.max_count)
|
|
||||||
.always_begin_end(false)
|
.always_begin_end(false)
|
||||||
.replacement(self.replace.clone().map(|r| r.into()))
|
.replacement(self.replace.clone().map(|r| r.into()))
|
||||||
.build(wtr)
|
.build(wtr)
|
||||||
@@ -656,7 +655,6 @@ impl HiArgs {
|
|||||||
.exclude_zero(!self.include_zero)
|
.exclude_zero(!self.include_zero)
|
||||||
.hyperlink(self.hyperlink_config.clone())
|
.hyperlink(self.hyperlink_config.clone())
|
||||||
.kind(kind)
|
.kind(kind)
|
||||||
.max_matches(self.max_count)
|
|
||||||
.path(self.with_filename)
|
.path(self.with_filename)
|
||||||
.path_terminator(self.path_terminator.clone())
|
.path_terminator(self.path_terminator.clone())
|
||||||
.separator_field(b":".to_vec())
|
.separator_field(b":".to_vec())
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ use std::{
|
|||||||
|
|
||||||
use {
|
use {
|
||||||
grep_matcher::{Match, Matcher},
|
grep_matcher::{Match, Matcher},
|
||||||
grep_searcher::{
|
grep_searcher::{Searcher, Sink, SinkContext, SinkFinish, SinkMatch},
|
||||||
Searcher, Sink, SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
|
||||||
},
|
|
||||||
serde_json as json,
|
serde_json as json,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,7 +24,6 @@ use crate::{
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Config {
|
struct Config {
|
||||||
pretty: bool,
|
pretty: bool,
|
||||||
max_matches: Option<u64>,
|
|
||||||
always_begin_end: bool,
|
always_begin_end: bool,
|
||||||
replacement: Arc<Option<Vec<u8>>>,
|
replacement: Arc<Option<Vec<u8>>>,
|
||||||
}
|
}
|
||||||
@@ -35,7 +32,6 @@ impl Default for Config {
|
|||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config {
|
Config {
|
||||||
pretty: false,
|
pretty: false,
|
||||||
max_matches: None,
|
|
||||||
always_begin_end: false,
|
always_begin_end: false,
|
||||||
replacement: Arc::new(None),
|
replacement: Arc::new(None),
|
||||||
}
|
}
|
||||||
@@ -85,16 +81,6 @@ impl JSONBuilder {
|
|||||||
self
|
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 enabled, the `begin` and `end` messages are always emitted, even
|
||||||
/// when no match is found.
|
/// when no match is found.
|
||||||
///
|
///
|
||||||
@@ -526,7 +512,6 @@ impl<W: io::Write> JSON<W> {
|
|||||||
path: None,
|
path: None,
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
match_count: 0,
|
match_count: 0,
|
||||||
after_context_remaining: 0,
|
|
||||||
binary_byte_offset: None,
|
binary_byte_offset: None,
|
||||||
begin_printed: false,
|
begin_printed: false,
|
||||||
stats: Stats::new(),
|
stats: Stats::new(),
|
||||||
@@ -553,7 +538,6 @@ impl<W: io::Write> JSON<W> {
|
|||||||
path: Some(path.as_ref()),
|
path: Some(path.as_ref()),
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
match_count: 0,
|
match_count: 0,
|
||||||
after_context_remaining: 0,
|
|
||||||
binary_byte_offset: None,
|
binary_byte_offset: None,
|
||||||
begin_printed: false,
|
begin_printed: false,
|
||||||
stats: Stats::new(),
|
stats: Stats::new(),
|
||||||
@@ -616,7 +600,6 @@ pub struct JSONSink<'p, 's, M: Matcher, W> {
|
|||||||
path: Option<&'p Path>,
|
path: Option<&'p Path>,
|
||||||
start_time: Instant,
|
start_time: Instant,
|
||||||
match_count: u64,
|
match_count: u64,
|
||||||
after_context_remaining: u64,
|
|
||||||
binary_byte_offset: Option<u64>,
|
binary_byte_offset: Option<u64>,
|
||||||
begin_printed: bool,
|
begin_printed: bool,
|
||||||
stats: Stats,
|
stats: Stats,
|
||||||
@@ -721,32 +704,6 @@ impl<'p, 's, M: Matcher, W: io::Write> JSONSink<'p, 's, M, W> {
|
|||||||
Ok(())
|
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.
|
/// Write the "begin" message.
|
||||||
fn write_begin_message(&mut self) -> io::Result<()> {
|
fn write_begin_message(&mut self) -> io::Result<()> {
|
||||||
if self.begin_printed {
|
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,
|
searcher: &Searcher,
|
||||||
mat: &SinkMatch<'_>,
|
mat: &SinkMatch<'_>,
|
||||||
) -> Result<bool, io::Error> {
|
) -> Result<bool, io::Error> {
|
||||||
self.write_begin_message()?;
|
|
||||||
|
|
||||||
self.match_count += 1;
|
self.match_count += 1;
|
||||||
// When we've exceeded our match count, then the remaining context
|
self.write_begin_message()?;
|
||||||
// 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.record_matches(
|
self.record_matches(
|
||||||
searcher,
|
searcher,
|
||||||
@@ -806,7 +749,7 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
|||||||
submatches: submatches.as_slice(),
|
submatches: submatches.as_slice(),
|
||||||
});
|
});
|
||||||
self.json.write_message(&msg)?;
|
self.json.write_message(&msg)?;
|
||||||
Ok(!self.should_quit())
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn context(
|
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.write_begin_message()?;
|
||||||
self.json.matches.clear();
|
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() {
|
let submatches = if searcher.invert_match() {
|
||||||
self.record_matches(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
|
self.record_matches(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
|
||||||
self.replace(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(),
|
submatches: submatches.as_slice(),
|
||||||
});
|
});
|
||||||
self.json.write_message(&msg)?;
|
self.json.write_message(&msg)?;
|
||||||
Ok(!self.should_quit())
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary_data(
|
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.json.wtr.reset_count();
|
||||||
self.start_time = Instant::now();
|
self.start_time = Instant::now();
|
||||||
self.match_count = 0;
|
self.match_count = 0;
|
||||||
self.after_context_remaining = 0;
|
|
||||||
self.binary_byte_offset = None;
|
self.binary_byte_offset = None;
|
||||||
if self.json.config.max_matches == Some(0) {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.json.config.always_begin_end {
|
if !self.json.config.always_begin_end {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
@@ -1015,9 +950,9 @@ and exhibited clearly, with a label attached.\
|
|||||||
#[test]
|
#[test]
|
||||||
fn max_matches() {
|
fn max_matches() {
|
||||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
let mut printer =
|
let mut printer = JSONBuilder::new().build(vec![]);
|
||||||
JSONBuilder::new().max_matches(Some(1)).build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
|
.max_matches(Some(1))
|
||||||
.build()
|
.build()
|
||||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1042,10 +977,10 @@ d
|
|||||||
e
|
e
|
||||||
";
|
";
|
||||||
let matcher = RegexMatcher::new(r"d").unwrap();
|
let matcher = RegexMatcher::new(r"d").unwrap();
|
||||||
let mut printer =
|
let mut printer = JSONBuilder::new().build(vec![]);
|
||||||
JSONBuilder::new().max_matches(Some(1)).build(vec![]);
|
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
.after_context(2)
|
.after_context(2)
|
||||||
|
.max_matches(Some(1))
|
||||||
.build()
|
.build()
|
||||||
.search_reader(
|
.search_reader(
|
||||||
&matcher,
|
&matcher,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ struct Config {
|
|||||||
hyperlink: HyperlinkConfig,
|
hyperlink: HyperlinkConfig,
|
||||||
stats: bool,
|
stats: bool,
|
||||||
path: bool,
|
path: bool,
|
||||||
max_matches: Option<u64>,
|
|
||||||
exclude_zero: bool,
|
exclude_zero: bool,
|
||||||
separator_field: Arc<Vec<u8>>,
|
separator_field: Arc<Vec<u8>>,
|
||||||
separator_path: Option<u8>,
|
separator_path: Option<u8>,
|
||||||
@@ -47,7 +46,6 @@ impl Default for Config {
|
|||||||
hyperlink: HyperlinkConfig::default(),
|
hyperlink: HyperlinkConfig::default(),
|
||||||
stats: false,
|
stats: false,
|
||||||
path: true,
|
path: true,
|
||||||
max_matches: None,
|
|
||||||
exclude_zero: true,
|
exclude_zero: true,
|
||||||
separator_field: Arc::new(b":".to_vec()),
|
separator_field: Arc::new(b":".to_vec()),
|
||||||
separator_path: None,
|
separator_path: None,
|
||||||
@@ -282,18 +280,6 @@ impl SummaryBuilder {
|
|||||||
self
|
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.
|
/// Exclude count-related summary results with no matches.
|
||||||
///
|
///
|
||||||
/// When enabled and the mode is either `Count` or `CountMatches`, then
|
/// 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)
|
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
|
/// 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.
|
/// 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
|
/// (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() {
|
} else if self.summary.config.kind.quit_early() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
Ok(!self.should_quit())
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary_data(
|
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.start_time = Instant::now();
|
||||||
self.match_count = 0;
|
self.match_count = 0;
|
||||||
self.binary_byte_offset = None;
|
self.binary_byte_offset = None;
|
||||||
if self.summary.config.max_matches == Some(0) {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1027,9 +996,9 @@ and exhibited clearly, with a label attached.
|
|||||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||||
let mut printer = SummaryBuilder::new()
|
let mut printer = SummaryBuilder::new()
|
||||||
.kind(SummaryKind::Count)
|
.kind(SummaryKind::Count)
|
||||||
.max_matches(Some(1))
|
|
||||||
.build_no_color(vec![]);
|
.build_no_color(vec![]);
|
||||||
SearcherBuilder::new()
|
SearcherBuilder::new()
|
||||||
|
.max_matches(Some(1))
|
||||||
.build()
|
.build()
|
||||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user