rgs: added multiline window limit and in-file result indexing (work in progress)

This commit is contained in:
2025-12-23 04:01:55 -05:00
parent cd1f981bea
commit ad6ec1b4c5
9 changed files with 599 additions and 8 deletions

View File

@@ -39,6 +39,7 @@ struct Config {
stats: bool,
heading: bool,
path: bool,
in_file_index: bool,
only_matching: bool,
per_match: bool,
per_match_one_line: bool,
@@ -64,6 +65,7 @@ impl Default for Config {
stats: false,
heading: false,
path: true,
in_file_index: false,
only_matching: false,
per_match: false,
per_match_one_line: false,
@@ -231,6 +233,12 @@ impl StandardBuilder {
self
}
/// When enabled, prefix matching lines with a per-file match index.
pub fn in_file_index(&mut self, yes: bool) -> &mut StandardBuilder {
self.config.in_file_index = yes;
self
}
/// Only print the specific matches instead of the entire line containing
/// each match. Each match is printed on its own line. When multi line
/// search is enabled, then matches spanning multiple lines are printed
@@ -528,6 +536,7 @@ impl<W: WriteColor> Standard<W> {
path: None,
start_time: Instant::now(),
match_count: 0,
in_file_index: 0,
binary_byte_offset: None,
stats,
needs_match_granularity,
@@ -564,6 +573,7 @@ impl<W: WriteColor> Standard<W> {
path: Some(ppath),
start_time: Instant::now(),
match_count: 0,
in_file_index: 0,
binary_byte_offset: None,
stats,
needs_match_granularity,
@@ -644,6 +654,7 @@ pub struct StandardSink<'p, 's, M: Matcher, W> {
path: Option<PrinterPath<'p>>,
start_time: Instant,
match_count: u64,
in_file_index: u64,
binary_byte_offset: Option<u64>,
stats: Option<Stats>,
needs_match_granularity: bool,
@@ -769,6 +780,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
mat: &SinkMatch<'_>,
) -> Result<bool, io::Error> {
self.match_count += 1;
self.in_file_index += 1;
self.record_matches(
searcher,
@@ -842,6 +854,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
self.standard.wtr.borrow_mut().reset_count();
self.start_time = Instant::now();
self.match_count = 0;
self.in_file_index = 0;
self.binary_byte_offset = None;
Ok(true)
}
@@ -956,6 +969,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.absolute_byte_offset(),
self.sunk.line_number(),
None,
self.in_file_index(),
)?;
self.write_line(self.sunk.bytes())
}
@@ -981,6 +995,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
absolute_byte_offset,
self.sunk.line_number().map(|n| n + i as u64),
None,
self.in_file_index(),
)?;
absolute_byte_offset += line.len() as u64;
@@ -1001,6 +1016,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.absolute_byte_offset() + m.start() as u64,
self.sunk.line_number(),
Some(m.start() as u64 + 1),
self.in_file_index(),
)?;
let buf = &self.sunk.bytes()[m];
@@ -1012,6 +1028,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.absolute_byte_offset() + m.start() as u64,
self.sunk.line_number(),
Some(m.start() as u64 + 1),
self.in_file_index(),
)?;
self.write_colored_line(&[m], self.sunk.bytes())?;
}
@@ -1020,6 +1037,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.absolute_byte_offset(),
self.sunk.line_number(),
Some(self.sunk.matches()[0].start() as u64 + 1),
self.in_file_index(),
)?;
self.write_colored_line(self.sunk.matches(), self.sunk.bytes())?;
}
@@ -1048,6 +1066,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.absolute_byte_offset() + line.start() as u64,
self.sunk.line_number().map(|n| n + count),
Some(matches[0].start() as u64 + 1),
self.in_file_index(),
)?;
count += 1;
self.trim_ascii_prefix(bytes, &mut line);
@@ -1093,6 +1112,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.absolute_byte_offset() + m.start() as u64,
self.sunk.line_number().map(|n| n + count),
Some(m.start() as u64 + 1),
self.in_file_index(),
)?;
let this_line = line.with_end(upto);
@@ -1131,6 +1151,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.absolute_byte_offset() + line.start() as u64,
self.sunk.line_number().map(|n| n + count),
Some(m.start().saturating_sub(line.start()) as u64 + 1),
self.in_file_index(),
)?;
count += 1;
self.trim_line_terminator(bytes, &mut line);
@@ -1178,10 +1199,11 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
absolute_byte_offset: u64,
line_number: Option<u64>,
column: Option<u64>,
in_file_index: Option<u64>,
) -> io::Result<()> {
let mut prelude = PreludeWriter::new(self);
prelude.start(line_number, column)?;
prelude.write_path()?;
prelude.write_path(in_file_index)?;
prelude.write_line_number(line_number)?;
prelude.write_column_number(column)?;
prelude.write_byte_offset(absolute_byte_offset)?;
@@ -1532,6 +1554,14 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
self.sunk.context_kind().is_some()
}
fn in_file_index(&self) -> Option<u64> {
if self.is_context() || !self.config().in_file_index {
None
} else {
Some(self.sink.in_file_index)
}
}
/// Return the underlying configuration for this printer.
fn config(&self) -> &'a Config {
&self.sink.standard.config
@@ -1657,16 +1687,27 @@ impl<'a, M: Matcher, W: WriteColor> PreludeWriter<'a, M, W> {
/// separator. (If a path terminator is set, then that is used instead of
/// the field separator.)
#[inline(always)]
fn write_path(&mut self) -> io::Result<()> {
fn write_path(&mut self, in_file_index: Option<u64>) -> io::Result<()> {
// The prelude doesn't handle headings, only what comes before a match
// on the same line. So if we are emitting paths in headings, we should
// not do it here on each line.
if self.config().heading {
if self.config().heading && in_file_index.is_none() {
return Ok(());
}
let path = self.std.path();
if path.is_none() && in_file_index.is_none() {
return Ok(());
}
let Some(path) = self.std.path() else { return Ok(()) };
self.write_separator()?;
self.std.write_path(path)?;
if let Some(path) = path {
self.std.write_path(path)?;
}
if let Some(index) = in_file_index {
self.std.write_spec(self.config().colors.path(), b"[")?;
let n = DecimalFormatter::new(index);
self.std.write_spec(self.config().colors.path(), n.as_bytes())?;
self.std.write_spec(self.config().colors.path(), b"]")?;
}
self.next_separator = if self.config().path_terminator.is_some() {
PreludeSeparator::PathTerminator