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 {
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])
.map_or(0, |i| i + 1);
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 {
start: start,
end: end,

View File

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

View File

@ -164,6 +164,9 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
let upto = self.inp.lastnl;
self.print_after_context(upto);
if !try!(self.fill()) {
if self.inp.is_binary {
self.printer.binary_matched(self.path);
}
break;
}
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 {
self.inp.pos = self.last_match.end() + 1;
self.inp.pos = self.last_match.end();
} else {
self.inp.pos = self.inp.lastnl;
}
@ -220,7 +223,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
} else {
self.last_printed = 0;
}
if keep_from < self.last_line {
if keep_from <= self.last_line {
self.last_line = self.last_line - keep_from;
} else {
self.count_lines(keep_from);
@ -240,7 +243,7 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
if !self.count {
self.print_match(start, end);
}
self.inp.pos = end + 1;
self.inp.pos = end;
self.match_count += 1;
}
}
@ -251,14 +254,14 @@ impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
return;
}
let start = self.last_printed;
let end = upto.saturating_sub(1);
let end = upto;
if start >= end {
return;
}
let before_context_start =
start + start_of_previous_lines(
&self.inp.buf[start..],
end - start,
end - start - 1,
self.before_context);
let mut it = IterLines::new(before_context_start);
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)]
fn print_match(&mut self, start: usize, end: usize) {
let last_printed = cmp::min(end + 1, self.inp.lastnl);
self.print_separator(start);
self.count_lines(start);
self.add_line(last_printed);
self.add_line(end);
self.printer.matched(
&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;
}
#[inline(always)]
fn print_context(&mut self, start: usize, end: usize) {
let last_printed = cmp::min(end + 1, self.inp.lastnl);
self.count_lines(start);
self.add_line(last_printed);
self.add_line(end);
self.printer.context(
&self.path, &self.inp.buf, start, end, self.line_count);
self.last_printed = last_printed;
self.last_printed = end;
}
#[inline(always)]
@ -424,6 +425,7 @@ impl InputBuffer {
&mut self.buf[self.end..self.end + self.read_size]));
if self.first {
if is_binary(&self.buf[self.end..self.end + n]) {
self.is_binary = true;
return Ok(false);
}
}
@ -435,10 +437,6 @@ impl InputBuffer {
self.lastnl = self.end;
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 =
memrchr(b'\n', &self.buf[self.end..self.end + n])
.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
/// buffer given should be the same on every call.
///
/// The range returned includes the new line.
#[inline(always)]
fn next(&mut self, buf: &[u8]) -> Option<(usize, usize)> {
match memchr(b'\n', &buf[self.pos..]) {
@ -509,8 +509,8 @@ impl IterLines {
}
Some(end) => {
let start = self.pos;
let end = self.pos + end;
self.pos = end + 1;
let end = self.pos + end + 1;
self.pos = end;
Some((start, end))
}
}
@ -532,7 +532,7 @@ fn start_of_previous_lines(
mut end: usize,
mut count: usize,
) -> usize {
if buf.is_empty() {
if buf[..end].is_empty() {
return 0;
}
if count == 0 {
@ -567,6 +567,9 @@ fn start_of_previous_lines(
count -= 1;
end = i;
if end == 0 {
if buf[end] == b'\n' && count == 0 {
end += 1;
}
return end;
}
end -= 1;
@ -591,6 +594,32 @@ mod tests {
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>> {
io::Cursor::new(s.to_string().into_bytes())
}
@ -603,16 +632,43 @@ mod tests {
&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]
fn previous_lines() {
let text = &b"\
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 text = SHERLOCK.as_bytes();
assert_eq!(366, text.len());
assert_eq!(0, start_of_previous_lines(text, 366, 100));
@ -712,24 +768,8 @@ and exhibited clearly, with a label attached.\
#[test]
fn basic_search() {
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(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()
};
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s|s);
assert_eq!(2, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\
/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
@ -738,24 +778,9 @@ and exhibited clearly, with a label attached.\
#[test]
fn line_numbers() {
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(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()
};
let (count, out) = search_smallcap(
"Sherlock", &*SHERLOCK, |s| s.line_number(true));
assert_eq!(2, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\
/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
@ -764,47 +789,17 @@ and exhibited clearly, with a label attached.\
#[test]
fn count() {
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(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()
};
let (count, out) = search_smallcap(
"Sherlock", &*SHERLOCK, |s| s.count(true));
assert_eq!(2, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "/baz.rs:2\n");
}
#[test]
fn invert_match() {
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(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()
};
let (count, out) = search_smallcap(
"Sherlock", &*SHERLOCK, |s| s.invert_match(true));
assert_eq!(4, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\
/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;
@ -815,24 +810,10 @@ and exhibited clearly, with a label attached.\
#[test]
fn invert_match_line_numbers() {
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(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()
};
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.invert_match(true).line_number(true)
});
assert_eq!(4, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "\
/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;
@ -843,95 +824,91 @@ and exhibited clearly, with a label attached.\
#[test]
fn invert_match_count() {
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(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()
};
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.invert_match(true).count(true)
});
assert_eq!(4, count);
let out = String::from_utf8(pp.into_inner()).unwrap();
assert_eq!(out, "/baz.rs:4\n");
}
macro_rules! before_context {
($name:ident, $query:expr, $num:expr, $expect:expr) => {
before_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.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, "\
#[test]
fn before_context_one1() {
let (count, out) = search_smallcap("Sherlock", &*SHERLOCK, |s| {
s.line_number(true).before_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-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
");
}
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: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: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: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: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: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-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
");
}
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-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,
");
}
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: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: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-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
");
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-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-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-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:5:but Doctor Watson has to have it taken out for him and dusted,
/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: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:5:but Doctor Watson has to have it taken out for him and dusted,
/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-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-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,
");
}
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-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-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: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-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
@ -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-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\");
");
}
}