From dfebed6cbe44593094e1632acd2d360e47d3abf1 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Thu, 22 Sep 2016 21:32:38 -0400 Subject: [PATCH] Add --vimgrep flag. The --vimgrep flag forces a line to be printed for every match, with line and column numbers. --- doc/rg.1 | 8 ++++++++ doc/rg.1.md | 5 +++++ src/args.rs | 17 ++++++++++++++++- src/printer.rs | 44 ++++++++++++++++++++++++++++++++++++++++---- tests/tests.rs | 13 +++++++++++++ 5 files changed, 82 insertions(+), 5 deletions(-) diff --git a/doc/rg.1 b/doc/rg.1 index 1e401906..300ad1b1 100644 --- a/doc/rg.1 +++ b/doc/rg.1 @@ -237,6 +237,14 @@ Defaults to the number of logical CPUs (capped at 6). Show the version number of ripgrep and exit. .RS .RE +.TP +.B \-\-vimgrep +Show results with every match on its own line, including line numbers +and column numbers. +(With this option, a line with more than one match of the regex will be +printed more than once.) +.RS +.RE .SH FILE TYPE MANAGEMENT OPTIONS .TP .B \-\-type\-list diff --git a/doc/rg.1.md b/doc/rg.1.md index 953bc1f8..81e974eb 100644 --- a/doc/rg.1.md +++ b/doc/rg.1.md @@ -153,6 +153,11 @@ the raw speed of grep. --version : Show the version number of ripgrep and exit. +--vimgrep +: Show results with every match on its own line, including line + numbers and column numbers. (With this option, a line with more + than one match of the regex will be printed more than once.) + # FILE TYPE MANAGEMENT OPTIONS --type-list diff --git a/src/args.rs b/src/args.rs index 77210a8f..777556dc 100644 --- a/src/args.rs +++ b/src/args.rs @@ -153,6 +153,11 @@ Less common options: --version Show the version number of ripgrep and exit. + --vimgrep + Show results with every match on its own line, including line + numbers and column numbers. (With this option, a line with more + than one match of the regex will be printed more than once.) + File type management options: --type-list Show all supported file types and their associated globs. @@ -206,6 +211,7 @@ pub struct RawArgs { flag_type_add: Vec, flag_type_clear: Vec, flag_unrestricted: u32, + flag_vimgrep: bool, flag_with_filename: bool, flag_word_regexp: bool, } @@ -231,6 +237,7 @@ pub struct Args { ignore_case: bool, invert_match: bool, line_number: bool, + line_per_match: bool, mmap: bool, no_ignore: bool, no_ignore_parent: bool, @@ -302,7 +309,9 @@ impl RawArgs { self.flag_threads }; let color = - if self.flag_color == "auto" { + if self.flag_vimgrep { + false + } else if self.flag_color == "auto" { atty::on_stdout() || self.flag_pretty } else { self.flag_color == "always" @@ -344,6 +353,7 @@ impl RawArgs { ignore_case: self.flag_ignore_case, invert_match: self.flag_invert_match, line_number: !self.flag_no_line_number && self.flag_line_number, + line_per_match: self.flag_vimgrep, mmap: mmap, no_ignore: no_ignore, no_ignore_parent: @@ -367,6 +377,10 @@ impl RawArgs { args.heading = true; } } + if self.flag_vimgrep { + args.column = true; + args.line_number = true; + } Ok(args) } @@ -490,6 +504,7 @@ impl Args { .context_separator(self.context_separator.clone()) .eol(self.eol) .heading(self.heading) + .line_per_match(self.line_per_match) .quiet(self.quiet) .with_filename(self.with_filename); if let Some(ref rep) = self.replace { diff --git a/src/printer.rs b/src/printer.rs index afc0de4f..65905f9c 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -28,6 +28,8 @@ pub struct Printer { /// /// N.B. If with_filename is false, then this setting has no effect. heading: bool, + /// Whether to show every match on its own line. + line_per_match: bool, /// Whether to suppress all output. quiet: bool, /// A string to use as a replacement of each match in a matching line. @@ -46,6 +48,7 @@ impl Printer { context_separator: "--".to_string().into_bytes(), eol: b'\n', heading: false, + line_per_match: false, quiet: false, replace: None, with_filename: false, @@ -79,6 +82,12 @@ impl Printer { self } + /// Whether to show every match on its own line. + pub fn line_per_match(mut self, yes: bool) -> Printer { + self.line_per_match = yes; + self + } + /// When set, all output is suppressed. pub fn quiet(mut self, yes: bool) -> Printer { self.quiet = yes; @@ -165,6 +174,34 @@ impl Printer { start: usize, end: usize, line_number: Option, + ) { + if !self.line_per_match { + let column = + if self.column { + Some(re.find(&buf[start..end]) + .map(|(s, _)| s + 1).unwrap_or(0) as u64) + } else { + None + }; + return self.write_match( + re, path, buf, start, end, line_number, column); + } + for (s, _) in re.find_iter(&buf[start..end]) { + let column = if self.column { Some(s as u64) } else { None }; + self.write_match( + re, path.as_ref(), buf, start, end, line_number, column); + } + } + + fn write_match>( + &mut self, + re: &Regex, + path: P, + buf: &[u8], + start: usize, + end: usize, + line_number: Option, + column: Option, ) { if self.heading && self.with_filename && !self.has_printed { self.write_heading(path.as_ref()); @@ -175,8 +212,7 @@ impl Printer { if let Some(line_number) = line_number { self.line_number(line_number, b':'); } - if self.column { - let c = re.find(&buf[start..end]).map(|(s, _)| s + 1).unwrap_or(0); + if let Some(c) = column { self.write(c.to_string().as_bytes()); self.write(b":"); } @@ -185,14 +221,14 @@ impl Printer { &buf[start..end], &**self.replace.as_ref().unwrap()); self.write(&line); } else { - self.write_match(re, &buf[start..end]); + self.write_matched_line(re, &buf[start..end]); } if buf[start..end].last() != Some(&self.eol) { self.write_eol(); } } - pub fn write_match(&mut self, re: &Regex, buf: &[u8]) { + fn write_matched_line(&mut self, re: &Regex, buf: &[u8]) { if !self.wtr.supports_color() { self.write(buf); return; diff --git a/tests/tests.rs b/tests/tests.rs index 1341aa10..693c74be 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -569,6 +569,19 @@ sherlock!(unrestricted3, "foo", ".", |wd: WorkDir, mut cmd: Command| { assert_eq!(lines, "file:foo\x00bar\nfile:foo\x00baz\n"); }); +sherlock!(vimgrep, "Sherlock|Watson", ".", |wd: WorkDir, mut cmd: Command| { + cmd.arg("--vimgrep"); + + let lines: String = wd.stdout(&mut cmd); + let expected = "\ +sherlock:1:15:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:1:56:For the Doctor Watsons of this world, as opposed to the Sherlock +sherlock:3:48:be, to a very large extent, the result of luck. Sherlock Holmes +sherlock:5:11:but Doctor Watson has to have it taken out for him and dusted, +"; + assert_eq!(lines, expected); +}); + #[test] fn binary_nosearch() { let wd = WorkDir::new("binary_nosearch");