1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-01-03 05:10:12 +02:00

making search work (finally)

This commit is contained in:
Andrew Gallant 2016-09-03 21:48:23 -04:00
parent c2b5577cba
commit 0bf278e72f
4 changed files with 331 additions and 224 deletions

View File

@ -230,7 +230,7 @@ impl Grep {
fn find_line_end(&self, buf: &[u8], pos: usize) -> usize { fn find_line_end(&self, buf: &[u8], pos: usize) -> usize {
memchr(self.opts.line_terminator, &buf[pos..]) memchr(self.opts.line_terminator, &buf[pos..])
.map_or(buf.len(), |i| pos + i) .map_or(buf.len(), |i| pos + i + 1)
} }
} }
@ -281,7 +281,7 @@ mod tests {
let start = memrchr(b'\n', &haystack[..s]) let start = memrchr(b'\n', &haystack[..s])
.map_or(0, |i| i + 1); .map_or(0, |i| i + 1);
let end = memchr(b'\n', &haystack[e..]) let end = memchr(b'\n', &haystack[e..])
.map_or(haystack.len(), |i| e + i); .map_or(haystack.len(), |i| e + i + 1);
lines.push(Match { lines.push(Match {
start: start, start: start,
end: end, end: end,

View File

@ -4,6 +4,9 @@ extern crate crossbeam;
extern crate docopt; extern crate docopt;
extern crate env_logger; extern crate env_logger;
extern crate grep; extern crate grep;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate memchr; extern crate memchr;
@ -136,7 +139,7 @@ fn run(mut args: Args) -> Result<()> {
} }
let args = Arc::new(args); let args = Arc::new(args);
let mut workers = vec![]; let mut workers = vec![];
let stdout = Arc::new(Mutex::new(io::BufWriter::new(io::stdout()))); let out = Arc::new(Mutex::new(Out::new(args.clone(), io::stdout())));
let mut chan_work_send = { let mut chan_work_send = {
let (worker, stealer) = chase_lev::deque(); let (worker, stealer) = chase_lev::deque();
@ -146,7 +149,7 @@ fn run(mut args: Args) -> Result<()> {
.case_insensitive(args.flag_ignore_case); .case_insensitive(args.flag_ignore_case);
let worker = Worker { let worker = Worker {
args: args.clone(), args: args.clone(),
stdout: stdout.clone(), out: out.clone(),
chan_work: stealer.clone(), chan_work: stealer.clone(),
inpbuf: InputBuffer::new(), inpbuf: InputBuffer::new(),
outbuf: Some(vec![]), outbuf: Some(vec![]),
@ -200,6 +203,26 @@ impl Args {
ig.ignore_hidden(!self.flag_hidden); ig.ignore_hidden(!self.flag_hidden);
walk::Iter::new(ig, wd) walk::Iter::new(ig, wd)
} }
fn before_context(&self) -> usize {
if self.flag_context > 0 {
self.flag_context
} else {
self.flag_before_context
}
}
fn after_context(&self) -> usize {
if self.flag_context > 0 {
self.flag_context
} else {
self.flag_after_context
}
}
fn has_context(&self) -> bool {
self.before_context() > 0 || self.after_context() > 0
}
} }
enum Message<T> { enum Message<T> {
@ -209,7 +232,7 @@ enum Message<T> {
struct Worker { struct Worker {
args: Arc<Args>, args: Arc<Args>,
stdout: Arc<Mutex<io::BufWriter<io::Stdout>>>, out: Arc<Mutex<Out<io::Stdout>>>,
chan_work: Stealer<Message<PathBuf>>, chan_work: Stealer<Message<PathBuf>>,
inpbuf: InputBuffer, inpbuf: InputBuffer,
outbuf: Option<Vec<u8>>, outbuf: Option<Vec<u8>>,
@ -245,21 +268,43 @@ impl Worker {
searcher = searcher.count(self.args.flag_count); searcher = searcher.count(self.args.flag_count);
searcher = searcher.line_number(self.args.flag_line_number); searcher = searcher.line_number(self.args.flag_line_number);
searcher = searcher.invert_match(self.args.flag_invert_match); searcher = searcher.invert_match(self.args.flag_invert_match);
searcher = searcher.after_context( searcher = searcher.after_context(self.args.after_context());
self.args.flag_after_context); searcher = searcher.before_context(self.args.before_context());
searcher = searcher.before_context(
self.args.flag_before_context);
if let Err(err) = searcher.run() { if let Err(err) = searcher.run() {
eprintln!("{}", err); eprintln!("{}", err);
} }
} }
let outbuf = printer.into_inner(); let outbuf = printer.into_inner();
if !outbuf.is_empty() { if !outbuf.is_empty() {
let mut stdout = self.stdout.lock(); let mut out = self.out.lock();
let _ = stdout.write_all(&outbuf); out.write_file_matches(&outbuf);
let _ = stdout.flush();
} }
self.outbuf = Some(outbuf); self.outbuf = Some(outbuf);
} }
} }
} }
struct Out<W: io::Write> {
args: Arc<Args>,
wtr: io::BufWriter<W>,
printed: bool,
}
impl<W: io::Write> Out<W> {
fn new(args: Arc<Args>, wtr: W) -> Out<W> {
Out {
args: args,
wtr: io::BufWriter::new(wtr),
printed: false,
}
}
fn write_file_matches(&mut self, buf: &[u8]) {
if self.printed && self.args.has_context() {
let _ = self.wtr.write_all(b"--\n");
}
let _ = self.wtr.write_all(buf);
let _ = self.wtr.flush();
self.printed = true;
}
}

View File

@ -65,7 +65,9 @@ impl<W: io::Write> Printer<W> {
self.write(b":"); self.write(b":");
} }
self.write(&buf[start..end]); self.write(&buf[start..end]);
self.write(b"\n"); if buf[start..end].last() != Some(&b'\n') {
self.write(b"\n");
}
} }
pub fn context<P: AsRef<Path>>( pub fn context<P: AsRef<Path>>(
@ -83,11 +85,13 @@ impl<W: io::Write> Printer<W> {
self.write(b"-"); self.write(b"-");
} }
self.write(&buf[start..end]); self.write(&buf[start..end]);
self.write(b"\n"); if buf[start..end].last() != Some(&b'\n') {
self.write(b"\n");
}
} }
pub fn binary_matched<P: AsRef<Path>>(&mut self, path: P) { pub fn binary_matched<P: AsRef<Path>>(&mut self, path: P) {
wln!(&mut self.wtr, "binary file {} matches", path.as_ref().display()); wln!(&mut self.wtr, "Binary file {} matches", path.as_ref().display());
} }
fn write(&mut self, buf: &[u8]) { fn write(&mut self, buf: &[u8]) {

View File

@ -164,6 +164,9 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
let upto = self.inp.lastnl; let upto = self.inp.lastnl;
self.print_after_context(upto); self.print_after_context(upto);
if !try!(self.fill()) { if !try!(self.fill()) {
if self.inp.is_binary {
self.printer.binary_matched(self.path);
}
break; break;
} }
while self.inp.pos < self.inp.lastnl { while self.inp.pos < self.inp.lastnl {
@ -195,7 +198,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
} }
} }
if matched { if matched {
self.inp.pos = self.last_match.end() + 1; self.inp.pos = self.last_match.end();
} else { } else {
self.inp.pos = self.inp.lastnl; self.inp.pos = self.inp.lastnl;
} }
@ -220,7 +223,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
} else { } else {
self.last_printed = 0; self.last_printed = 0;
} }
if keep_from < self.last_line { if keep_from <= self.last_line {
self.last_line = self.last_line - keep_from; self.last_line = self.last_line - keep_from;
} else { } else {
self.count_lines(keep_from); self.count_lines(keep_from);
@ -240,7 +243,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
if !self.count { if !self.count {
self.print_match(start, end); self.print_match(start, end);
} }
self.inp.pos = end + 1; self.inp.pos = end;
self.match_count += 1; self.match_count += 1;
} }
} }
@ -251,14 +254,14 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
return; return;
} }
let start = self.last_printed; let start = self.last_printed;
let end = upto.saturating_sub(1); let end = upto;
if start >= end { if start >= end {
return; return;
} }
let before_context_start = let before_context_start =
start + start_of_previous_lines( start + start_of_previous_lines(
&self.inp.buf[start..], &self.inp.buf[start..],
end - start, end - start - 1,
self.before_context); self.before_context);
let mut it = IterLines::new(before_context_start); let mut it = IterLines::new(before_context_start);
while let Some((s, e)) = it.next(&self.inp.buf[..end]) { while let Some((s, e)) = it.next(&self.inp.buf[..end]) {
@ -286,24 +289,22 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
#[inline(always)] #[inline(always)]
fn print_match(&mut self, start: usize, end: usize) { fn print_match(&mut self, start: usize, end: usize) {
let last_printed = cmp::min(end + 1, self.inp.lastnl);
self.print_separator(start); self.print_separator(start);
self.count_lines(start); self.count_lines(start);
self.add_line(last_printed); self.add_line(end);
self.printer.matched( self.printer.matched(
&self.path, &self.inp.buf, start, end, self.line_count); &self.path, &self.inp.buf, start, end, self.line_count);
self.last_printed = last_printed; self.last_printed = end;
self.after_context_remaining = self.after_context; self.after_context_remaining = self.after_context;
} }
#[inline(always)] #[inline(always)]
fn print_context(&mut self, start: usize, end: usize) { fn print_context(&mut self, start: usize, end: usize) {
let last_printed = cmp::min(end + 1, self.inp.lastnl);
self.count_lines(start); self.count_lines(start);
self.add_line(last_printed); self.add_line(end);
self.printer.context( self.printer.context(
&self.path, &self.inp.buf, start, end, self.line_count); &self.path, &self.inp.buf, start, end, self.line_count);
self.last_printed = last_printed; self.last_printed = end;
} }
#[inline(always)] #[inline(always)]
@ -424,6 +425,7 @@ impl InputBuffer {
&mut self.buf[self.end..self.end + self.read_size])); &mut self.buf[self.end..self.end + self.read_size]));
if self.first { if self.first {
if is_binary(&self.buf[self.end..self.end + n]) { if is_binary(&self.buf[self.end..self.end + n]) {
self.is_binary = true;
return Ok(false); return Ok(false);
} }
} }
@ -435,10 +437,6 @@ impl InputBuffer {
self.lastnl = self.end; self.lastnl = self.end;
break; break;
} }
// We know there is no nl between self.start..self.end since:
// 1) If this is the first iteration, then any bytes preceding
// self.end do not contain nl by construction.
// 2) Subsequent iterations only occur if no nl could be found.
self.lastnl = self.lastnl =
memrchr(b'\n', &self.buf[self.end..self.end + n]) memrchr(b'\n', &self.buf[self.end..self.end + n])
.map(|i| self.end + i + 1) .map(|i| self.end + i + 1)
@ -495,6 +493,8 @@ impl IterLines {
/// Return the start and end position of the next line in the buffer. The /// Return the start and end position of the next line in the buffer. The
/// buffer given should be the same on every call. /// buffer given should be the same on every call.
///
/// The range returned includes the new line.
#[inline(always)] #[inline(always)]
fn next(&mut self, buf: &[u8]) -> Option<(usize, usize)> { fn next(&mut self, buf: &[u8]) -> Option<(usize, usize)> {
match memchr(b'\n', &buf[self.pos..]) { match memchr(b'\n', &buf[self.pos..]) {
@ -509,8 +509,8 @@ impl IterLines {
} }
Some(end) => { Some(end) => {
let start = self.pos; let start = self.pos;
let end = self.pos + end; let end = self.pos + end + 1;
self.pos = end + 1; self.pos = end;
Some((start, end)) Some((start, end))
} }
} }
@ -532,7 +532,7 @@ fn start_of_previous_lines(
mut end: usize, mut end: usize,
mut count: usize, mut count: usize,
) -> usize { ) -> usize {
if buf.is_empty() { if buf[..end].is_empty() {
return 0; return 0;
} }
if count == 0 { if count == 0 {
@ -567,6 +567,9 @@ fn start_of_previous_lines(
count -= 1; count -= 1;
end = i; end = i;
if end == 0 { if end == 0 {
if buf[end] == b'\n' && count == 0 {
end += 1;
}
return end; return end;
} }
end -= 1; end -= 1;
@ -591,6 +594,32 @@ mod tests {
use super::{InputBuffer, Searcher, start_of_previous_lines}; use super::{InputBuffer, Searcher, start_of_previous_lines};
lazy_static! {
static ref SHERLOCK: &'static str = "\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
";
static ref CODE: &'static str = "\
extern crate snap;
use std::io;
fn main() {
let stdin = io::stdin();
let stdout = io::stdout();
// Wrap the stdin reader in a Snappy reader.
let mut rdr = snap::Reader::new(stdin.lock());
let mut wtr = stdout.lock();
io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
}
";
}
fn hay(s: &str) -> io::Cursor<Vec<u8>> { fn hay(s: &str) -> io::Cursor<Vec<u8>> {
io::Cursor::new(s.to_string().into_bytes()) io::Cursor::new(s.to_string().into_bytes())
} }
@ -603,16 +632,43 @@ mod tests {
&Path::new("/baz.rs") &Path::new("/baz.rs")
} }
type TestSearcher<'a> = Searcher<'a, io::Cursor<Vec<u8>>, Vec<u8>>;
fn search_smallcap<F: FnMut(TestSearcher) -> TestSearcher>(
pat: &str,
haystack: &str,
mut map: F,
) -> (u64, String) {
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![]);
let grep = GrepBuilder::new(pat).build().unwrap();
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), hay(haystack));
map(searcher).run().unwrap()
};
(count, String::from_utf8(pp.into_inner()).unwrap())
}
fn search<F: FnMut(TestSearcher) -> TestSearcher>(
pat: &str,
haystack: &str,
mut map: F,
) -> (u64, String) {
let mut inp = InputBuffer::with_capacity(4096);
let mut pp = Printer::new(vec![]);
let grep = GrepBuilder::new(pat).build().unwrap();
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), hay(haystack));
map(searcher).run().unwrap()
};
(count, String::from_utf8(pp.into_inner()).unwrap())
}
#[test] #[test]
fn previous_lines() { fn previous_lines() {
let text = &b"\ let text = SHERLOCK.as_bytes();
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
"[..];
assert_eq!(366, text.len()); assert_eq!(366, text.len());
assert_eq!(0, start_of_previous_lines(text, 366, 100)); assert_eq!(0, start_of_previous_lines(text, 366, 100));
@ -712,24 +768,8 @@ and exhibited clearly, with a label attached.\
#[test] #[test]
fn basic_search() { fn basic_search() {
let text = hay("\ let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s|s);
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![]);
let grep = matcher("Sherlock");
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher.run().unwrap()
};
assert_eq!(2, count); assert_eq!(2, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\ assert_eq!(out, "\
/baz.rs:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:be, to a very large extent, the result of luck. Sherlock Holmes
@ -738,24 +778,9 @@ and exhibited clearly, with a label attached.\
#[test] #[test]
fn line_numbers() { fn line_numbers() {
let text = hay("\ let (count, out) = search_smallcap(
For the Doctor Watsons of this world, as opposed to the Sherlock "Sherlock", &*SHERLOCK, |s| s.line_number(true));
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![]);
let grep = matcher("Sherlock");
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher.line_number(true).run().unwrap()
};
assert_eq!(2, count); assert_eq!(2, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\ assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
@ -764,47 +789,17 @@ and exhibited clearly, with a label attached.\
#[test] #[test]
fn count() { fn count() {
let text = hay("\ let (count, out) = search_smallcap(
For the Doctor Watsons of this world, as opposed to the Sherlock "Sherlock", &*SHERLOCK, |s| s.count(true));
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![]);
let grep = matcher("Sherlock");
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher.count(true).run().unwrap()
};
assert_eq!(2, count); assert_eq!(2, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "/baz.rs:2\n"); assert_eq!(out, "/baz.rs:2\n");
} }
#[test] #[test]
fn invert_match() { fn invert_match() {
let text = hay("\ let (count, out) = search_smallcap(
For the Doctor Watsons of this world, as opposed to the Sherlock "Sherlock", &*SHERLOCK, |s| s.invert_match(true));
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![]);
let grep = matcher("Sherlock");
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher.invert_match(true).run().unwrap()
};
assert_eq!(4, count); assert_eq!(4, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\ assert_eq!(out, "\
/baz.rs:Holmeses, success in the province of detective work must always /baz.rs:Holmeses, success in the province of detective work must always
/baz.rs:can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:can extract a clew from a wisp of straw or a flake of cigar ash;
@ -815,24 +810,10 @@ and exhibited clearly, with a label attached.\
#[test] #[test]
fn invert_match_line_numbers() { fn invert_match_line_numbers() {
let text = hay("\ let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
For the Doctor Watsons of this world, as opposed to the Sherlock s.invert_match(true).line_number(true)
Holmeses, success in the province of detective work must always });
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![]);
let grep = matcher("Sherlock");
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher.invert_match(true).line_number(true).run().unwrap()
};
assert_eq!(4, count); assert_eq!(4, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\ assert_eq!(out, "\
/baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:2:Holmeses, success in the province of detective work must always
/baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash;
@ -843,95 +824,91 @@ and exhibited clearly, with a label attached.\
#[test] #[test]
fn invert_match_count() { fn invert_match_count() {
let text = hay("\ let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
For the Doctor Watsons of this world, as opposed to the Sherlock s.invert_match(true).count(true)
Holmeses, success in the province of detective work must always });
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(1);
let mut pp = Printer::new(vec![]);
let grep = matcher("Sherlock");
let count = {
let searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher.invert_match(true).count(true).run().unwrap()
};
assert_eq!(4, count); assert_eq!(4, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "/baz.rs:4\n"); assert_eq!(out, "/baz.rs:4\n");
} }
macro_rules! before_context { #[test]
($name:ident, $query:expr, $num:expr, $expect:expr) => { fn before_context_one1() {
before_context!($name, $query, $num, $expect, false); let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
}; s.line_number(true).before_context(1)
($name:ident, $query:expr, $num:expr, $expect:expr, $invert:expr) => { });
#[test] assert_eq!(2, count);
fn $name() { assert_eq!(out, "\
let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(4096);
let mut pp = Printer::new(vec![]);
let grep = matcher($query);
let count = {
let mut searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher = searcher.line_number(true);
searcher = searcher.before_context($num);
searcher = searcher.invert_match($invert);
searcher.run().unwrap()
};
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, $expect);
}
};
}
before_context!(before_context_one, "Sherlock", 1, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs-2-Holmeses, success in the province of detective work must always
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
"); ");
}
before_context!(before_context_invert_one1, "Sherlock", 1, "\ #[test]
fn before_context_invert_one1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).before_context(1).invert_match(true)
});
assert_eq!(4, count);
assert_eq!(out, "\
/baz.rs-1-For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-1-For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:2:Holmeses, success in the province of detective work must always
/baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes
/baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash;
/baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted,
/baz.rs:6:and exhibited clearly, with a label attached. /baz.rs:6:and exhibited clearly, with a label attached.
", true); ");
}
before_context!(before_context_invert_one2, " a ", 1, "\ #[test]
fn before_context_invert_one2() {
let (count, out) = search_smallcap(" a ", &*SHERLOCK, |s| {
s.line_number(true).before_context(1).invert_match(true)
});
assert_eq!(3, count);
assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:2:Holmeses, success in the province of detective work must always
-- --
/baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash;
/baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted,
", true); ");
}
before_context!(before_context_two1, "Sherlock", 2, "\ #[test]
fn before_context_two1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).before_context(2)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs-2-Holmeses, success in the province of detective work must always
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
"); ");
}
before_context!(before_context_two2, "dusted", 2, "\ #[test]
fn before_context_two2() {
let (count, out) = search_smallcap("dusted", &*SHERLOCK, |s| {
s.line_number(true).before_context(2)
});
assert_eq!(1, count);
assert_eq!(out, "\
/baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes
/baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash;
/baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted,
"); ");
}
before_context!(before_context_two3, "success|attached", 2, "\ #[test]
fn before_context_two3() {
let (count, out) = search_smallcap(
"success|attached", &*SHERLOCK, |s| {
s.line_number(true).before_context(2)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs-1-For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs-1-For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:2:Holmeses, success in the province of detective work must always
-- --
@ -939,91 +916,150 @@ and exhibited clearly, with a label attached.\
/baz.rs-5-but Doctor Watson has to have it taken out for him and dusted, /baz.rs-5-but Doctor Watson has to have it taken out for him and dusted,
/baz.rs:6:and exhibited clearly, with a label attached. /baz.rs:6:and exhibited clearly, with a label attached.
"); ");
}
before_context!(before_context_three, "Sherlock", 3, "\ #[test]
fn before_context_two4() {
let (count, out) = search("stdin", &*CODE, |s| {
s.line_number(true).before_context(2)
});
assert_eq!(3, count);
assert_eq!(out, "\
/baz.rs-4-
/baz.rs-5-fn main() {
/baz.rs:6: let stdin = io::stdin();
/baz.rs-7- let stdout = io::stdout();
/baz.rs-8-
/baz.rs:9: // Wrap the stdin reader in a Snappy reader.
/baz.rs:10: let mut rdr = snap::Reader::new(stdin.lock());
");
}
#[test]
fn before_context_two5() {
let (count, out) = search("stdout", &*CODE, |s| {
s.line_number(true).before_context(2)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs-5-fn main() {
/baz.rs-6- let stdin = io::stdin();
/baz.rs:7: let stdout = io::stdout();
--
/baz.rs-9- // Wrap the stdin reader in a Snappy reader.
/baz.rs-10- let mut rdr = snap::Reader::new(stdin.lock());
/baz.rs:11: let mut wtr = stdout.lock();
");
}
#[test]
fn before_context_three1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).before_context(3)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs-2-Holmeses, success in the province of detective work must always
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
"); ");
macro_rules! after_context {
($name:ident, $query:expr, $num:expr, $expect:expr) => {
after_context!($name, $query, $num, $expect, false);
};
($name:ident, $query:expr, $num:expr, $expect:expr, $invert:expr) => {
#[test]
fn $name() {
let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
let mut inp = InputBuffer::with_capacity(4096);
let mut pp = Printer::new(vec![]);
let grep = matcher($query);
let count = {
let mut searcher = Searcher::new(
&mut inp, &mut pp, &grep, test_path(), text);
searcher = searcher.line_number(true);
searcher = searcher.after_context($num);
searcher = searcher.invert_match($invert);
searcher.run().unwrap()
};
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, $expect);
}
};
} }
after_context!(after_context_one, "Sherlock", 1, "\ #[test]
fn after_context_one1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).after_context(1)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs-2-Holmeses, success in the province of detective work must always
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
/baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash;
"); ");
}
after_context!(after_context_invert_one1, "Sherlock", 1, "\ #[test]
fn after_context_invert_one1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).after_context(1).invert_match(true)
});
assert_eq!(4, count);
assert_eq!(out, "\
/baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:2:Holmeses, success in the province of detective work must always
/baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes
/baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash;
/baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted,
/baz.rs:6:and exhibited clearly, with a label attached. /baz.rs:6:and exhibited clearly, with a label attached.
", true); ");
}
after_context!(after_context_invert_one2, " a ", 1, "\ #[test]
fn after_context_invert_one2() {
let (count, out) = search_smallcap(" a ", &*SHERLOCK, |s| {
s.line_number(true).after_context(1).invert_match(true)
});
assert_eq!(3, count);
assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:2:Holmeses, success in the province of detective work must always
/baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes
-- --
/baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted,
/baz.rs-6-and exhibited clearly, with a label attached. /baz.rs-6-and exhibited clearly, with a label attached.
", true); ");
}
after_context!(after_context_two1, "Sherlock", 2, "\ #[test]
fn after_context_two1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).after_context(2)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs-2-Holmeses, success in the province of detective work must always
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
/baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash;
/baz.rs-5-but Doctor Watson has to have it taken out for him and dusted, /baz.rs-5-but Doctor Watson has to have it taken out for him and dusted,
"); ");
}
after_context!(after_context_two2, "dusted", 2, "\ #[test]
fn after_context_two2() {
let (count, out) = search_smallcap("dusted", &*SHERLOCK, |s| {
s.line_number(true).after_context(2)
});
assert_eq!(1, count);
assert_eq!(out, "\
/baz.rs:5:but Doctor Watson has to have it taken out for him and dusted, /baz.rs:5:but Doctor Watson has to have it taken out for him and dusted,
/baz.rs-6-and exhibited clearly, with a label attached. /baz.rs-6-and exhibited clearly, with a label attached.
"); ");
}
after_context!(after_context_two3, "success|attached", 2, "\ #[test]
fn after_context_two3() {
let (count, out) = search_smallcap(
"success|attached", &*SHERLOCK, |s| {
s.line_number(true).after_context(2)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs:2:Holmeses, success in the province of detective work must always /baz.rs:2:Holmeses, success in the province of detective work must always
/baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs-3-be, to a very large extent, the result of luck. Sherlock Holmes
/baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash; /baz.rs-4-can extract a clew from a wisp of straw or a flake of cigar ash;
-- --
/baz.rs:6:and exhibited clearly, with a label attached. /baz.rs:6:and exhibited clearly, with a label attached.
"); ");
}
after_context!(after_context_three, "Sherlock", 3, "\ #[test]
fn after_context_three1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).after_context(3)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock /baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs-2-Holmeses, success in the province of detective work must always /baz.rs-2-Holmeses, success in the province of detective work must always
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes /baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
@ -1031,4 +1067,26 @@ and exhibited clearly, with a label attached.\
/baz.rs-5-but Doctor Watson has to have it taken out for him and dusted, /baz.rs-5-but Doctor Watson has to have it taken out for him and dusted,
/baz.rs-6-and exhibited clearly, with a label attached. /baz.rs-6-and exhibited clearly, with a label attached.
"); ");
}
#[test]
fn before_after_context_two1() {
let (count, out) = search(
r"fn main|let mut rdr", &*CODE, |s| {
s.line_number(true).after_context(2).before_context(2)
});
assert_eq!(2, count);
assert_eq!(out, "\
/baz.rs-3-use std::io;
/baz.rs-4-
/baz.rs:5:fn main() {
/baz.rs-6- let stdin = io::stdin();
/baz.rs-7- let stdout = io::stdout();
/baz.rs-8-
/baz.rs-9- // Wrap the stdin reader in a Snappy reader.
/baz.rs:10: let mut rdr = snap::Reader::new(stdin.lock());
/baz.rs-11- let mut wtr = stdout.lock();
/baz.rs-12- io::copy(&mut rdr, &mut wtr).expect(\"I/O operation failed\");
");
}
} }