Compare commits

...

15 Commits
0.2.0 ... 0.2.1

Author SHA1 Message Date
Andrew Gallant
de79be2db2 0.2.1 2016-09-26 20:02:58 -04:00
Andrew Gallant
416b69bae5 changelog 0.2.1 2016-09-26 20:02:47 -04:00
Andrew Gallant
3e78fce3a3 Don't print empty lines in single threaded mode.
Fixes #99.
2016-09-26 19:57:23 -04:00
Andrew Gallant
7a3fd1f23f Add a --null flag.
This flag causes a NUL byte to follow any file path in ripgrep's output.

Closes #89.
2016-09-26 19:21:17 -04:00
Andrew Gallant
d306403440 Fix an off-by-one error with --column.
Fixes #105.
2016-09-26 19:09:59 -04:00
Andrew Gallant
ebabe1df6a Merge branch 'gitignore_blank_lines' 2016-09-26 18:55:43 -04:00
Andrew Gallant
f27aa3ff6f Add regression test.
Fixes #106.
2016-09-26 18:55:26 -04:00
Tom Jackson
20ccd441f2 Allow (and ignore) whitespace-only lines in .gitignore files
Git considers these to be blank lines.
2016-09-26 18:52:57 -04:00
Andrew Gallant
104d740f76 Don't quit if opening a file fails.
This was already working correctly in multithreaded mode, but in single
threaded mode, a file failing to open caused search to stop. That's bad.

Fixes #98.
2016-09-26 18:44:19 -04:00
Andrew Gallant
2da0eab2b8 Don't initialize ignores for file arguments.
We'll never use them, so it's wasted effort.
2016-09-26 18:44:19 -04:00
Andrew Gallant
b8c7864a02 Merge pull request #107 from kaushalmodi/add-systemverilog-type
Add SystemVerilog (SV) type
2016-09-26 16:54:49 -04:00
Kaushal Modi
ec26995655 Add SystemVerilog (SV) type 2016-09-26 15:24:35 -04:00
Andrew Gallant
a41235a3b5 Merge pull request #100 from emlyn/patch-1
Recognise cljc and cljx extensions as Clojure(script)
2016-09-26 08:59:59 -04:00
Emlyn Corrin
1a91b900e7 Clojure files can also end in cljc or cljx
(see https://github.com/clojure/clojurescript/wiki/Using-cljc)
2016-09-26 09:53:46 +01:00
Andrew Gallant
2b15832655 update brew formula to 0.2.0 2016-09-25 22:50:50 -04:00
13 changed files with 241 additions and 77 deletions

View File

@@ -1,3 +1,26 @@
0.2.1
=====
Feature enhancements:
* Added or improved file type filtering for Clojure and SystemVerilog.
* [FEATURE #89](https://github.com/BurntSushi/ripgrep/issues/89):
Add a --null flag that outputs a NUL byte after every file path.
Bug fixes:
* [BUG #98](https://github.com/BurntSushi/ripgrep/issues/98):
Fix a bug in single threaded mode when if opening a file failed, ripgrep
quit instead of continuing the search.
* [BUG #99](https://github.com/BurntSushi/ripgrep/issues/99):
Fix another bug in single threaded mode where empty lines were being printed
by mistake.
* [BUG #105](https://github.com/BurntSushi/ripgrep/issues/105):
Fix an off-by-one error with --column.
* [BUG #106](https://github.com/BurntSushi/ripgrep/issues/106):
Fix a bug where a whitespace only line in a gitignore file caused ripgrep
to panic (i.e., crash).
0.2.0 0.2.0
===== =====
Feature enhancements: Feature enhancements:

2
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
[root] [root]
name = "ripgrep" name = "ripgrep"
version = "0.2.0" version = "0.2.1"
dependencies = [ dependencies = [
"deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.85 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.85 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ripgrep" name = "ripgrep"
version = "0.2.0" #:version version = "0.2.1" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
Line oriented search tool using Rust's regex library. Combines the raw Line oriented search tool using Rust's regex library. Combines the raw

View File

@@ -239,6 +239,14 @@ Note that .ignore files will continue to be respected.
.RS .RS
.RE .RE
.TP .TP
.B \-\-null
Whenever a file name is printed, follow it with a NUL byte.
This includes printing filenames before matches, and when printing a
list of matching files such as with \-\-count, \-\-files\-with\-matches
and \-\-files.
.RS
.RE
.TP
.B \-p, \-\-pretty .B \-p, \-\-pretty
Alias for \-\-color=always \-\-heading \-n. Alias for \-\-color=always \-\-heading \-n.
.RS .RS

View File

@@ -155,6 +155,12 @@ the raw speed of grep.
: Don't respect version control ignore files (e.g., .gitignore). : Don't respect version control ignore files (e.g., .gitignore).
Note that .ignore files will continue to be respected. Note that .ignore files will continue to be respected.
--null
: Whenever a file name is printed, follow it with a NUL byte.
This includes printing filenames before matches, and when printing
a list of matching files such as with --count, --files-with-matches
and --files.
-p, --pretty -p, --pretty
: Alias for --color=always --heading -n. : Alias for --color=always --heading -n.

View File

@@ -1,15 +1,15 @@
require 'formula' require 'formula'
class Ripgrep < Formula class Ripgrep < Formula
version '0.1.17' version '0.2.0'
desc "Search tool like grep and The Silver Searcher." desc "Search tool like grep and The Silver Searcher."
homepage "https://github.com/BurntSushi/ripgrep" homepage "https://github.com/BurntSushi/ripgrep"
if Hardware::CPU.is_64_bit? if Hardware::CPU.is_64_bit?
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz" url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz"
sha256 "cb7b551a08849cef6ef8f17229224f094299692981976a3c5873c93f68c8fa1a" sha256 "f55ef5dac04178bcae0d6c5ba2d09690d326e8c7c3f28e561025b04e1ab81d80"
else else
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-i686-apple-darwin.tar.gz" url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-i686-apple-darwin.tar.gz"
sha256 "0e936874b9f3fd661c5566e7f8fe18343baa5e9371e57d8d71000e9234fc376b" sha256 "d901d55ccb48c19067f563d42652dfd8642bf50d28a40c0e2a4d3e866857a93b"
end end
def install def install

View File

@@ -155,6 +155,12 @@ Less common options:
Don't respect version control ignore files (e.g., .gitignore). Don't respect version control ignore files (e.g., .gitignore).
Note that .ignore files will continue to be respected. Note that .ignore files will continue to be respected.
--null
Whenever a file name is printed, follow it with a NUL byte.
This includes printing filenames before matches, and when printing
a list of matching files such as with --count, --files-with-matches
and --files.
-p, --pretty -p, --pretty
Alias for --color=always --heading -n. Alias for --color=always --heading -n.
@@ -224,6 +230,7 @@ pub struct RawArgs {
flag_no_line_number: bool, flag_no_line_number: bool,
flag_no_mmap: bool, flag_no_mmap: bool,
flag_no_filename: bool, flag_no_filename: bool,
flag_null: bool,
flag_pretty: bool, flag_pretty: bool,
flag_quiet: bool, flag_quiet: bool,
flag_regexp: Vec<String>, flag_regexp: Vec<String>,
@@ -269,6 +276,7 @@ pub struct Args {
no_ignore: bool, no_ignore: bool,
no_ignore_parent: bool, no_ignore_parent: bool,
no_ignore_vcs: bool, no_ignore_vcs: bool,
null: bool,
quiet: bool, quiet: bool,
replace: Option<Vec<u8>>, replace: Option<Vec<u8>>,
text: bool, text: bool,
@@ -399,6 +407,7 @@ impl RawArgs {
no_ignore_vcs: no_ignore_vcs:
// --no-ignore implies --no-ignore-vcs // --no-ignore implies --no-ignore-vcs
self.flag_no_ignore_vcs || no_ignore, self.flag_no_ignore_vcs || no_ignore,
null: self.flag_null,
quiet: self.flag_quiet, quiet: self.flag_quiet,
replace: self.flag_replace.clone().map(|s| s.into_bytes()), replace: self.flag_replace.clone().map(|s| s.into_bytes()),
text: text, text: text,
@@ -553,6 +562,7 @@ impl Args {
.heading(self.heading) .heading(self.heading)
.line_per_match(self.line_per_match) .line_per_match(self.line_per_match)
.quiet(self.quiet) .quiet(self.quiet)
.null(self.null)
.with_filename(self.with_filename); .with_filename(self.with_filename);
if let Some(ref rep) = self.replace { if let Some(ref rep) = self.replace {
p = p.replace(rep.clone()); p = p.replace(rep.clone());
@@ -673,15 +683,20 @@ impl Args {
pub fn walker(&self, path: &Path) -> Result<walk::Iter> { pub fn walker(&self, path: &Path) -> Result<walk::Iter> {
let wd = WalkDir::new(path).follow_links(self.follow); let wd = WalkDir::new(path).follow_links(self.follow);
let mut ig = Ignore::new(); let mut ig = Ignore::new();
ig.ignore_hidden(!self.hidden); // Only register ignore rules if this is a directory. If it's a file,
ig.no_ignore(self.no_ignore); // then it was explicitly given by the end user, so we always search
ig.no_ignore_vcs(self.no_ignore_vcs); // it.
ig.add_types(self.types.clone()); if path.is_dir() {
if !self.no_ignore_parent { ig.ignore_hidden(!self.hidden);
try!(ig.push_parents(path)); ig.no_ignore(self.no_ignore);
} ig.no_ignore_vcs(self.no_ignore_vcs);
if let Some(ref overrides) = self.glob_overrides { ig.add_types(self.types.clone());
ig.add_override(overrides.clone()); if !self.no_ignore_parent {
try!(ig.push_parents(path));
}
if let Some(ref overrides) = self.glob_overrides {
ig.add_override(overrides.clone());
}
} }
Ok(walk::Iter::new(ig, wd)) Ok(walk::Iter::new(ig, wd))
} }

View File

@@ -283,12 +283,15 @@ impl GitignoreBuilder {
from: P, from: P,
mut line: &str, mut line: &str,
) -> Result<(), Error> { ) -> Result<(), Error> {
if line.is_empty() || line.starts_with("#") { if line.starts_with("#") {
return Ok(()); return Ok(());
} }
if !line.ends_with("\\ ") { if !line.ends_with("\\ ") {
line = line.trim_right(); line = line.trim_right();
} }
if line.is_empty() {
return Ok(());
}
let mut pat = Pattern { let mut pat = Pattern {
from: from.as_ref().to_path_buf(), from: from.as_ref().to_path_buf(),
original: line.to_string(), original: line.to_string(),
@@ -426,4 +429,10 @@ mod tests {
not_ignored!(ignot11, ROOT, "#foo", "#foo"); not_ignored!(ignot11, ROOT, "#foo", "#foo");
not_ignored!(ignot12, ROOT, "\n\n\n", "foo"); not_ignored!(ignot12, ROOT, "\n\n\n", "foo");
not_ignored!(ignot13, ROOT, "foo/**", "foo", true); not_ignored!(ignot13, ROOT, "foo/**", "foo", true);
// See: https://github.com/BurntSushi/ripgrep/issues/106
#[test]
fn regression_106() {
Gitignore::from_str("/", " ").unwrap();
}
} }

View File

@@ -22,8 +22,8 @@ extern crate winapi;
use std::error::Error; use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::{self, Write}; use std::io;
use std::path::{Path, PathBuf}; use std::path::Path;
use std::process; use std::process;
use std::result; use std::result;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -89,16 +89,15 @@ fn run(args: Args) -> Result<u64> {
let args = Arc::new(args); let args = Arc::new(args);
let paths = args.paths(); let paths = args.paths();
let threads = cmp::max(1, args.threads() - 1); let threads = cmp::max(1, args.threads() - 1);
let isone =
paths.len() == 1 && (paths[0] == Path::new("-") || paths[0].is_file());
if args.files() { if args.files() {
return run_files(args.clone()); return run_files(args.clone());
} }
if args.type_list() { if args.type_list() {
return run_types(args.clone()); return run_types(args.clone());
} }
if paths.len() == 1 && (paths[0] == Path::new("-") || paths[0].is_file()) { if threads == 1 || isone {
return run_one(args.clone(), &paths[0]);
}
if threads == 1 {
return run_one_thread(args.clone()); return run_one_thread(args.clone());
} }
@@ -158,32 +157,35 @@ fn run_one_thread(args: Arc<Args>) -> Result<u64> {
match_count: 0, match_count: 0,
}; };
let paths = args.paths(); let paths = args.paths();
let filesep = args.file_separator();
let mut term = args.stdout(); let mut term = args.stdout();
let mut paths_searched: u64 = 0; let mut paths_searched: u64 = 0;
for p in paths { for p in paths {
if p == Path::new("-") { if p == Path::new("-") {
if worker.match_count > 0 {
if let Some(ref sep) = filesep {
let _ = term.write_all(sep);
let _ = term.write_all(b"\n");
}
}
paths_searched += 1; paths_searched += 1;
let mut printer = args.printer(&mut term); let mut printer = args.printer(&mut term);
if worker.match_count > 0 {
if let Some(sep) = args.file_separator() {
printer = printer.file_separator(sep);
}
}
worker.do_work(&mut printer, WorkReady::Stdin); worker.do_work(&mut printer, WorkReady::Stdin);
} else { } else {
for ent in try!(args.walker(p)) { for ent in try!(args.walker(p)) {
if worker.match_count > 0 {
if let Some(ref sep) = filesep {
let _ = term.write_all(sep);
let _ = term.write_all(b"\n");
}
}
paths_searched += 1; paths_searched += 1;
let mut printer = args.printer(&mut term); let mut printer = args.printer(&mut term);
let file = try!(File::open(ent.path())); if worker.match_count > 0 {
if let Some(sep) = args.file_separator() {
printer = printer.file_separator(sep);
}
}
let file = match File::open(ent.path()) {
Ok(file) => file,
Err(err) => {
eprintln!("{}: {}", ent.path().display(), err);
continue;
}
};
worker.do_work(&mut printer, WorkReady::DirFile(ent, file)); worker.do_work(&mut printer, WorkReady::DirFile(ent, file));
} }
} }
@@ -196,25 +198,6 @@ fn run_one_thread(args: Arc<Args>) -> Result<u64> {
Ok(worker.match_count) Ok(worker.match_count)
} }
fn run_one(args: Arc<Args>, path: &Path) -> Result<u64> {
let mut worker = Worker {
args: args.clone(),
inpbuf: args.input_buffer(),
grep: args.grep(),
match_count: 0,
};
let term = args.stdout();
let mut printer = args.printer(term);
let work =
if path == Path::new("-") {
WorkReady::Stdin
} else {
WorkReady::PathFile(path.to_path_buf(), try!(File::open(path)))
};
worker.do_work(&mut printer, work);
Ok(worker.match_count)
}
fn run_files(args: Arc<Args>) -> Result<u64> { fn run_files(args: Arc<Args>) -> Result<u64> {
let term = args.stdout(); let term = args.stdout();
let mut printer = args.printer(term); let mut printer = args.printer(term);
@@ -253,7 +236,6 @@ enum Work {
enum WorkReady { enum WorkReady {
Stdin, Stdin,
DirFile(DirEntry, File), DirFile(DirEntry, File),
PathFile(PathBuf, File),
} }
struct MultiWorker { struct MultiWorker {
@@ -328,17 +310,6 @@ impl Worker {
self.search(printer, path, file) self.search(printer, path, file)
} }
} }
WorkReady::PathFile(path, file) => {
let mut path = &*path;
if let Some(p) = strip_prefix("./", path) {
path = p;
}
if self.args.mmap() {
self.search_mmap(printer, path, &file)
} else {
self.search(printer, path, file)
}
}
}; };
match result { match result {
Ok(count) => { Ok(count) => {

View File

@@ -48,8 +48,6 @@ impl Out {
/// If set, the separator is printed between matches from different files. /// If set, the separator is printed between matches from different files.
/// By default, no separator is printed. /// By default, no separator is printed.
///
/// If sep is empty, then no file separator is printed.
pub fn file_separator(mut self, sep: Vec<u8>) -> Out { pub fn file_separator(mut self, sep: Vec<u8>) -> Out {
self.file_separator = Some(sep); self.file_separator = Some(sep);
self self

View File

@@ -25,6 +25,8 @@ pub struct Printer<W> {
/// printed via the match directly, but occasionally we need to insert them /// printed via the match directly, but occasionally we need to insert them
/// ourselves (for example, to print a context separator). /// ourselves (for example, to print a context separator).
eol: u8, eol: u8,
/// A file separator to show before any matches are printed.
file_separator: Option<Vec<u8>>,
/// Whether to show file name as a heading or not. /// Whether to show file name as a heading or not.
/// ///
/// N.B. If with_filename is false, then this setting has no effect. /// N.B. If with_filename is false, then this setting has no effect.
@@ -33,6 +35,9 @@ pub struct Printer<W> {
line_per_match: bool, line_per_match: bool,
/// Whether to suppress all output. /// Whether to suppress all output.
quiet: bool, quiet: bool,
/// Whether to print NUL bytes after a file path instead of new lines
/// or `:`.
null: bool,
/// A string to use as a replacement of each match in a matching line. /// A string to use as a replacement of each match in a matching line.
replace: Option<Vec<u8>>, replace: Option<Vec<u8>>,
/// Whether to prefix each match with the corresponding file name. /// Whether to prefix each match with the corresponding file name.
@@ -48,9 +53,11 @@ impl<W: Terminal + Send> Printer<W> {
column: false, column: false,
context_separator: "--".to_string().into_bytes(), context_separator: "--".to_string().into_bytes(),
eol: b'\n', eol: b'\n',
file_separator: None,
heading: false, heading: false,
line_per_match: false, line_per_match: false,
quiet: false, quiet: false,
null: false,
replace: None, replace: None,
with_filename: false, with_filename: false,
} }
@@ -75,6 +82,13 @@ impl<W: Terminal + Send> Printer<W> {
self self
} }
/// If set, the separator is printed before any matches. By default, no
/// separator is printed.
pub fn file_separator(mut self, sep: Vec<u8>) -> Printer<W> {
self.file_separator = Some(sep);
self
}
/// Whether to show file name as a heading or not. /// Whether to show file name as a heading or not.
/// ///
/// N.B. If with_filename is false, then this setting has no effect. /// N.B. If with_filename is false, then this setting has no effect.
@@ -89,6 +103,13 @@ impl<W: Terminal + Send> Printer<W> {
self self
} }
/// Whether to cause NUL bytes to follow file paths instead of other
/// visual separators (like `:`, `-` and `\n`).
pub fn null(mut self, yes: bool) -> Printer<W> {
self.null = yes;
self
}
/// When set, all output is suppressed. /// When set, all output is suppressed.
pub fn quiet(mut self, yes: bool) -> Printer<W> { pub fn quiet(mut self, yes: bool) -> Printer<W> {
self.quiet = yes; self.quiet = yes;
@@ -146,14 +167,22 @@ impl<W: Terminal + Send> Printer<W> {
pub fn path<P: AsRef<Path>>(&mut self, path: P) { pub fn path<P: AsRef<Path>>(&mut self, path: P) {
let path = strip_prefix("./", path.as_ref()).unwrap_or(path.as_ref()); let path = strip_prefix("./", path.as_ref()).unwrap_or(path.as_ref());
self.write_path(path); self.write_path(path);
self.write_eol(); if self.null {
self.write(b"\x00");
} else {
self.write_eol();
}
} }
/// Prints the given path and a count of the number of matches found. /// Prints the given path and a count of the number of matches found.
pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) { pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) {
if self.with_filename { if self.with_filename {
self.write_path(path); self.write_path(path);
self.write(b":"); if self.null {
self.write(b"\x00");
} else {
self.write(b":");
}
} }
self.write(count.to_string().as_bytes()); self.write(count.to_string().as_bytes());
self.write_eol(); self.write_eol();
@@ -186,7 +215,7 @@ impl<W: Terminal + Send> Printer<W> {
let column = let column =
if self.column { if self.column {
Some(re.find(&buf[start..end]) Some(re.find(&buf[start..end])
.map(|(s, _)| s + 1).unwrap_or(0) as u64) .map(|(s, _)| s).unwrap_or(0) as u64)
} else { } else {
None None
}; };
@@ -211,10 +240,15 @@ impl<W: Terminal + Send> Printer<W> {
column: Option<u64>, column: Option<u64>,
) { ) {
if self.heading && self.with_filename && !self.has_printed { if self.heading && self.with_filename && !self.has_printed {
self.write_file_sep();
self.write_heading(path.as_ref()); self.write_heading(path.as_ref());
} else if !self.heading && self.with_filename { } else if !self.heading && self.with_filename {
self.write_path(path.as_ref()); self.write_path(path.as_ref());
self.write(b":"); if self.null {
self.write(b"\x00");
} else {
self.write(b":");
}
} }
if let Some(line_number) = line_number { if let Some(line_number) = line_number {
self.line_number(line_number, b':'); self.line_number(line_number, b':');
@@ -261,10 +295,15 @@ impl<W: Terminal + Send> Printer<W> {
line_number: Option<u64>, line_number: Option<u64>,
) { ) {
if self.heading && self.with_filename && !self.has_printed { if self.heading && self.with_filename && !self.has_printed {
self.write_file_sep();
self.write_heading(path.as_ref()); self.write_heading(path.as_ref());
} else if !self.heading && self.with_filename { } else if !self.heading && self.with_filename {
self.write_path(path.as_ref()); self.write_path(path.as_ref());
self.write(b"-"); if self.null {
self.write(b"\x00");
} else {
self.write(b"-");
}
} }
if let Some(line_number) = line_number { if let Some(line_number) = line_number {
self.line_number(line_number, b'-'); self.line_number(line_number, b'-');
@@ -281,7 +320,11 @@ impl<W: Terminal + Send> Printer<W> {
let _ = self.wtr.attr(Attr::Bold); let _ = self.wtr.attr(Attr::Bold);
} }
self.write_path(path.as_ref()); self.write_path(path.as_ref());
self.write_eol(); if self.null {
self.write(b"\x00");
} else {
self.write_eol();
}
if self.wtr.supports_color() { if self.wtr.supports_color() {
let _ = self.wtr.reset(); let _ = self.wtr.reset();
} }
@@ -324,4 +367,15 @@ impl<W: Terminal + Send> Printer<W> {
let eol = self.eol; let eol = self.eol;
self.write(&[eol]); self.write(&[eol]);
} }
fn write_file_sep(&mut self) {
if self.quiet {
return;
}
if let Some(ref sep) = self.file_separator {
self.has_printed = true;
let _ = self.wtr.write_all(sep);
let _ = self.wtr.write_all(b"\n");
}
}
} }

View File

@@ -18,7 +18,7 @@ const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
("awk", &["*.awk"]), ("awk", &["*.awk"]),
("c", &["*.c", "*.h", "*.H"]), ("c", &["*.c", "*.h", "*.H"]),
("cbor", &["*.cbor"]), ("cbor", &["*.cbor"]),
("clojure", &["*.clj", "*.cljs"]), ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
("cmake", &["CMakeLists.txt"]), ("cmake", &["CMakeLists.txt"]),
("coffeescript", &["*.coffee"]), ("coffeescript", &["*.coffee"]),
("cpp", &[ ("cpp", &[
@@ -70,6 +70,7 @@ const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
("scala", &["*.scala"]), ("scala", &["*.scala"]),
("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]), ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
("sql", &["*.sql"]), ("sql", &["*.sql"]),
("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
("swift", &["*.swift"]), ("swift", &["*.swift"]),
("tex", &["*.tex", "*.cls", "*.sty"]), ("tex", &["*.tex", "*.cls", "*.sty"]),
("ts", &["*.ts", "*.tsx"]), ("ts", &["*.ts", "*.tsx"]),

View File

@@ -54,6 +54,13 @@ fn path(unix: &str) -> String {
} }
} }
fn sort_lines(lines: &str) -> String {
let mut lines: Vec<String> =
lines.trim().lines().map(|s| s.to_owned()).collect();
lines.sort();
format!("{}\n", lines.join("\n"))
}
sherlock!(single_file, |wd: WorkDir, mut cmd| { sherlock!(single_file, |wd: WorkDir, mut cmd| {
let lines: String = wd.stdout(&mut cmd); let lines: String = wd.stdout(&mut cmd);
let expected = "\ let expected = "\
@@ -86,8 +93,8 @@ sherlock!(columns, |wd: WorkDir, mut cmd: Command| {
cmd.arg("--column"); cmd.arg("--column");
let lines: String = wd.stdout(&mut cmd); let lines: String = wd.stdout(&mut cmd);
let expected = "\ let expected = "\
58:For the Doctor Watsons of this world, as opposed to the Sherlock 57:For the Doctor Watsons of this world, as opposed to the Sherlock
50:be, to a very large extent, the result of luck. Sherlock Holmes 49:be, to a very large extent, the result of luck. Sherlock Holmes
"; ";
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
@@ -716,6 +723,36 @@ clean!(regression_93, r"(\d{1,3}\.){3}\d{1,3}", ".",
assert_eq!(lines, "foo:192.168.1.1\n"); assert_eq!(lines, "foo:192.168.1.1\n");
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/99
clean!(regression_99, "test", ".",
|wd: WorkDir, mut cmd: Command| {
wd.create("foo1", "test");
wd.create("foo2", "zzz");
wd.create("bar", "test");
cmd.arg("-j1").arg("--heading");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(sort_lines(&lines), sort_lines("bar\ntest\n\nfoo1\ntest\n"));
});
// See: https://github.com/BurntSushi/ripgrep/issues/105
clean!(regression_105_part1, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("foo", "zztest");
cmd.arg("--vimgrep");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo:1:3:zztest\n");
});
// See: https://github.com/BurntSushi/ripgrep/issues/105
clean!(regression_105_part2, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("foo", "zztest");
cmd.arg("--column");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo:3:zztest\n");
});
// See: https://github.com/BurntSushi/ripgrep/issues/20 // See: https://github.com/BurntSushi/ripgrep/issues/20
sherlock!(feature_20, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { sherlock!(feature_20, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
cmd.arg("--no-filename"); cmd.arg("--no-filename");
@@ -752,6 +789,48 @@ sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/89
sherlock!(feature_89_files_with_matches, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--null").arg("--files-with-matches");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "sherlock\x00");
});
// See: https://github.com/BurntSushi/ripgrep/issues/89
sherlock!(feature_89_count, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--null").arg("--count");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "sherlock\x002\n");
});
// See: https://github.com/BurntSushi/ripgrep/issues/89
sherlock!(feature_89_files, "NADA", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--null").arg("--files");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "sherlock\x00");
});
// See: https://github.com/BurntSushi/ripgrep/issues/89
sherlock!(feature_89_match, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--null").arg("-C1");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
sherlock\x00For the Doctor Watsons of this world, as opposed to the Sherlock
sherlock\x00Holmeses, success in the province of detective work must always
sherlock\x00be, to a very large extent, the result of luck. Sherlock Holmes
sherlock\x00can extract a clew from a wisp of straw or a flake of cigar ash;
";
assert_eq!(lines, expected);
});
#[test] #[test]
fn binary_nosearch() { fn binary_nosearch() {
let wd = WorkDir::new("binary_nosearch"); let wd = WorkDir::new("binary_nosearch");