mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-05-19 09:40:22 -07:00
ripgrep: migrate to libripgrep
This commit is contained in:
parent
b3769ef8f1
commit
7eb34e8b32
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -194,7 +194,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grep2"
|
name = "grep2"
|
||||||
version = "0.1.8"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"grep-matcher 0.0.1",
|
"grep-matcher 0.0.1",
|
||||||
"grep-printer 0.0.1",
|
"grep-printer 0.0.1",
|
||||||
@ -345,6 +345,7 @@ dependencies = [
|
|||||||
"encoding_rs_io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"encoding_rs_io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"globset 0.4.1",
|
"globset 0.4.1",
|
||||||
"grep 0.1.9",
|
"grep 0.1.9",
|
||||||
|
"grep2 0.2.0",
|
||||||
"ignore 0.4.3",
|
"ignore 0.4.3",
|
||||||
"lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -45,6 +45,7 @@ encoding_rs = "0.8"
|
|||||||
encoding_rs_io = "0.1"
|
encoding_rs_io = "0.1"
|
||||||
globset = { version = "0.4.0", path = "globset" }
|
globset = { version = "0.4.0", path = "globset" }
|
||||||
grep = { version = "0.1.8", path = "grep" }
|
grep = { version = "0.1.8", path = "grep" }
|
||||||
|
grep2 = { version = "0.2.0", path = "grep2" }
|
||||||
ignore = { version = "0.4.0", path = "ignore" }
|
ignore = { version = "0.4.0", path = "ignore" }
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
@ -131,6 +131,10 @@ _rg() {
|
|||||||
'--mmap[search using memory maps when possible]'
|
'--mmap[search using memory maps when possible]'
|
||||||
"--no-mmap[don't search using memory maps]"
|
"--no-mmap[don't search using memory maps]"
|
||||||
|
|
||||||
|
+ '(multiline)' # multiline options
|
||||||
|
'--multiline[permit matching across multiple lines]'
|
||||||
|
$no"--no-multiline[restrict matches to at most one line each]"
|
||||||
|
|
||||||
+ '(only)' # Only-match options
|
+ '(only)' # Only-match options
|
||||||
'(passthru replace)'{-o,--only-matching}'[show only matching part of each line]'
|
'(passthru replace)'{-o,--only-matching}'[show only matching part of each line]'
|
||||||
|
|
||||||
|
43
src/app.rs
43
src/app.rs
@ -2,8 +2,8 @@
|
|||||||
// including some light validation.
|
// including some light validation.
|
||||||
//
|
//
|
||||||
// This module is purposely written in a bare-bones way, since it is included
|
// This module is purposely written in a bare-bones way, since it is included
|
||||||
// in ripgrep's build.rs file as a way to generate completion files for common
|
// in ripgrep's build.rs file as a way to generate a man page and completion
|
||||||
// shells.
|
// files for common shells.
|
||||||
//
|
//
|
||||||
// The only other place that ripgrep deals with clap is in src/args.rs, which
|
// The only other place that ripgrep deals with clap is in src/args.rs, which
|
||||||
// is where we read clap's configuration from the end user's arguments and turn
|
// is where we read clap's configuration from the end user's arguments and turn
|
||||||
@ -478,7 +478,7 @@ impl RGArg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We add an extra space to long descriptions so that a black line is inserted
|
// We add an extra space to long descriptions so that a blank line is inserted
|
||||||
// between flag descriptions in --help output.
|
// between flag descriptions in --help output.
|
||||||
macro_rules! long {
|
macro_rules! long {
|
||||||
($lit:expr) => { concat!($lit, " ") }
|
($lit:expr) => { concat!($lit, " ") }
|
||||||
@ -525,6 +525,7 @@ pub fn all_args_and_flags() -> Vec<RGArg> {
|
|||||||
flag_max_depth(&mut args);
|
flag_max_depth(&mut args);
|
||||||
flag_max_filesize(&mut args);
|
flag_max_filesize(&mut args);
|
||||||
flag_mmap(&mut args);
|
flag_mmap(&mut args);
|
||||||
|
flag_multiline(&mut args);
|
||||||
flag_no_config(&mut args);
|
flag_no_config(&mut args);
|
||||||
flag_no_ignore(&mut args);
|
flag_no_ignore(&mut args);
|
||||||
flag_no_ignore_global(&mut args);
|
flag_no_ignore_global(&mut args);
|
||||||
@ -813,10 +814,23 @@ fn flag_debug(args: &mut Vec<RGArg>) {
|
|||||||
const SHORT: &str = "Show debug messages.";
|
const SHORT: &str = "Show debug messages.";
|
||||||
const LONG: &str = long!("\
|
const LONG: &str = long!("\
|
||||||
Show debug messages. Please use this when filing a bug report.
|
Show debug messages. Please use this when filing a bug report.
|
||||||
|
|
||||||
|
The --debug flag is generally useful for figuring out why ripgrep skipped
|
||||||
|
searching a particular file. The debug messages should mention all files
|
||||||
|
skipped and why they were skipped.
|
||||||
|
|
||||||
|
To get even more debug output, use the --trace flag, which implies --debug
|
||||||
|
along with additional trace data. With --trace, the output could be quite
|
||||||
|
large and is generally more useful for development.
|
||||||
");
|
");
|
||||||
let arg = RGArg::switch("debug")
|
let arg = RGArg::switch("debug")
|
||||||
.help(SHORT).long_help(LONG);
|
.help(SHORT).long_help(LONG);
|
||||||
args.push(arg);
|
args.push(arg);
|
||||||
|
|
||||||
|
let arg = RGArg::switch("trace")
|
||||||
|
.hidden()
|
||||||
|
.overrides("debug");
|
||||||
|
args.push(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flag_dfa_size_limit(args: &mut Vec<RGArg>) {
|
fn flag_dfa_size_limit(args: &mut Vec<RGArg>) {
|
||||||
@ -1198,6 +1212,24 @@ This flag overrides --mmap.
|
|||||||
args.push(arg);
|
args.push(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flag_multiline(args: &mut Vec<RGArg>) {
|
||||||
|
const SHORT: &str = "Enable matching across multiple lines.";
|
||||||
|
const LONG: &str = long!("\
|
||||||
|
Enable matching across multiple lines.
|
||||||
|
|
||||||
|
This flag can be disabled with --no-multiline.
|
||||||
|
");
|
||||||
|
let arg = RGArg::switch("multiline")
|
||||||
|
.help(SHORT).long_help(LONG)
|
||||||
|
.overrides("no-multiline");
|
||||||
|
args.push(arg);
|
||||||
|
|
||||||
|
let arg = RGArg::switch("no-multiline")
|
||||||
|
.hidden()
|
||||||
|
.overrides("multiline");
|
||||||
|
args.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
fn flag_no_config(args: &mut Vec<RGArg>) {
|
fn flag_no_config(args: &mut Vec<RGArg>) {
|
||||||
const SHORT: &str = "Never read configuration files.";
|
const SHORT: &str = "Never read configuration files.";
|
||||||
const LONG: &str = long!("\
|
const LONG: &str = long!("\
|
||||||
@ -1374,13 +1406,10 @@ the empty string. For example, if you are searching using 'rg foo' then using
|
|||||||
'rg \"^|foo\"' instead will emit every line in every file searched, but only
|
'rg \"^|foo\"' instead will emit every line in every file searched, but only
|
||||||
occurrences of 'foo' will be highlighted. This flag enables the same behavior
|
occurrences of 'foo' will be highlighted. This flag enables the same behavior
|
||||||
without needing to modify the pattern.
|
without needing to modify the pattern.
|
||||||
|
|
||||||
This flag conflicts with the --only-matching and --replace flags.
|
|
||||||
");
|
");
|
||||||
let arg = RGArg::switch("passthru")
|
let arg = RGArg::switch("passthru")
|
||||||
.help(SHORT).long_help(LONG)
|
.help(SHORT).long_help(LONG)
|
||||||
.alias("passthrough")
|
.alias("passthrough");
|
||||||
.conflicts(&["only-matching", "replace"]);
|
|
||||||
args.push(arg);
|
args.push(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
src/args.rs
27
src/args.rs
@ -21,6 +21,7 @@ use atty;
|
|||||||
use ignore::overrides::{Override, OverrideBuilder};
|
use ignore::overrides::{Override, OverrideBuilder};
|
||||||
use ignore::types::{FileTypeDef, Types, TypesBuilder};
|
use ignore::types::{FileTypeDef, Types, TypesBuilder};
|
||||||
use ignore;
|
use ignore;
|
||||||
|
use messages::{set_messages, set_ignore_messages};
|
||||||
use printer::{ColorSpecs, Printer};
|
use printer::{ColorSpecs, Printer};
|
||||||
use unescape::{escape, unescape};
|
use unescape::{escape, unescape};
|
||||||
use worker::{Worker, WorkerBuilder};
|
use worker::{Worker, WorkerBuilder};
|
||||||
@ -64,10 +65,8 @@ pub struct Args {
|
|||||||
mmap: bool,
|
mmap: bool,
|
||||||
no_ignore: bool,
|
no_ignore: bool,
|
||||||
no_ignore_global: bool,
|
no_ignore_global: bool,
|
||||||
no_ignore_messages: bool,
|
|
||||||
no_ignore_parent: bool,
|
no_ignore_parent: bool,
|
||||||
no_ignore_vcs: bool,
|
no_ignore_vcs: bool,
|
||||||
no_messages: bool,
|
|
||||||
null: bool,
|
null: bool,
|
||||||
only_matching: bool,
|
only_matching: bool,
|
||||||
path_separator: Option<u8>,
|
path_separator: Option<u8>,
|
||||||
@ -101,6 +100,8 @@ impl Args {
|
|||||||
// arguments, then we re-parse argv, otherwise we just use the matches
|
// arguments, then we re-parse argv, otherwise we just use the matches
|
||||||
// we have here.
|
// we have here.
|
||||||
let early_matches = ArgMatches(app::app().get_matches());
|
let early_matches = ArgMatches(app::app().get_matches());
|
||||||
|
set_messages(!early_matches.is_present("no-messages"));
|
||||||
|
set_ignore_messages(!early_matches.is_present("no-ignore-messages"));
|
||||||
|
|
||||||
if let Err(err) = Logger::init() {
|
if let Err(err) = Logger::init() {
|
||||||
errored!("failed to initialize logger: {}", err);
|
errored!("failed to initialize logger: {}", err);
|
||||||
@ -120,6 +121,8 @@ impl Args {
|
|||||||
} else {
|
} else {
|
||||||
log::set_max_level(log::LevelFilter::Warn);
|
log::set_max_level(log::LevelFilter::Warn);
|
||||||
}
|
}
|
||||||
|
set_messages(!matches.is_present("no-messages"));
|
||||||
|
set_ignore_messages(!matches.is_present("no-ignore-messages"));
|
||||||
matches.to_args()
|
matches.to_args()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +140,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
// If the user wants ripgrep to use a config file, then parse args
|
// If the user wants ripgrep to use a config file, then parse args
|
||||||
// from that first.
|
// from that first.
|
||||||
let mut args = config::args(early_matches.is_present("no-messages"));
|
let mut args = config::args();
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return early_matches;
|
return early_matches;
|
||||||
}
|
}
|
||||||
@ -286,7 +289,6 @@ impl Args {
|
|||||||
.invert_match(self.invert_match)
|
.invert_match(self.invert_match)
|
||||||
.max_count(self.max_count)
|
.max_count(self.max_count)
|
||||||
.mmap(self.mmap)
|
.mmap(self.mmap)
|
||||||
.no_messages(self.no_messages)
|
|
||||||
.quiet(self.quiet)
|
.quiet(self.quiet)
|
||||||
.text(self.text)
|
.text(self.text)
|
||||||
.search_zip_files(self.search_zip_files)
|
.search_zip_files(self.search_zip_files)
|
||||||
@ -310,17 +312,6 @@ impl Args {
|
|||||||
self.type_list
|
self.type_list
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if error messages should be suppressed.
|
|
||||||
pub fn no_messages(&self) -> bool {
|
|
||||||
self.no_messages
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if error messages associated with parsing .ignore or
|
|
||||||
/// .gitignore files should be suppressed.
|
|
||||||
pub fn no_ignore_messages(&self) -> bool {
|
|
||||||
self.no_ignore_messages
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new recursive directory iterator over the paths in argv.
|
/// Create a new recursive directory iterator over the paths in argv.
|
||||||
pub fn walker(&self) -> ignore::Walk {
|
pub fn walker(&self) -> ignore::Walk {
|
||||||
self.walker_builder().build()
|
self.walker_builder().build()
|
||||||
@ -340,9 +331,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
for path in &self.ignore_files {
|
for path in &self.ignore_files {
|
||||||
if let Some(err) = wd.add_ignore(path) {
|
if let Some(err) = wd.add_ignore(path) {
|
||||||
if !self.no_messages && !self.no_ignore_messages {
|
ignore_message!("{}", err);
|
||||||
eprintln!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,10 +408,8 @@ impl<'a> ArgMatches<'a> {
|
|||||||
mmap: mmap,
|
mmap: mmap,
|
||||||
no_ignore: self.no_ignore(),
|
no_ignore: self.no_ignore(),
|
||||||
no_ignore_global: self.no_ignore_global(),
|
no_ignore_global: self.no_ignore_global(),
|
||||||
no_ignore_messages: self.is_present("no-ignore-messages"),
|
|
||||||
no_ignore_parent: self.no_ignore_parent(),
|
no_ignore_parent: self.no_ignore_parent(),
|
||||||
no_ignore_vcs: self.no_ignore_vcs(),
|
no_ignore_vcs: self.no_ignore_vcs(),
|
||||||
no_messages: self.is_present("no-messages"),
|
|
||||||
null: self.is_present("null"),
|
null: self.is_present("null"),
|
||||||
only_matching: self.is_present("only-matching"),
|
only_matching: self.is_present("only-matching"),
|
||||||
path_separator: self.path_separator()?,
|
path_separator: self.path_separator()?,
|
||||||
|
1384
src/args2.rs
Normal file
1384
src/args2.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,10 +12,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use Result;
|
use Result;
|
||||||
|
|
||||||
/// Return a sequence of arguments derived from ripgrep rc configuration files.
|
/// Return a sequence of arguments derived from ripgrep rc configuration files.
|
||||||
///
|
pub fn args() -> Vec<OsString> {
|
||||||
/// If no_messages is false and there was a problem reading a config file,
|
|
||||||
/// then errors are printed to stderr.
|
|
||||||
pub fn args(no_messages: bool) -> Vec<OsString> {
|
|
||||||
let config_path = match env::var_os("RIPGREP_CONFIG_PATH") {
|
let config_path = match env::var_os("RIPGREP_CONFIG_PATH") {
|
||||||
None => return vec![],
|
None => return vec![],
|
||||||
Some(config_path) => {
|
Some(config_path) => {
|
||||||
@ -28,20 +25,20 @@ pub fn args(no_messages: bool) -> Vec<OsString> {
|
|||||||
let (args, errs) = match parse(&config_path) {
|
let (args, errs) = match parse(&config_path) {
|
||||||
Ok((args, errs)) => (args, errs),
|
Ok((args, errs)) => (args, errs),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !no_messages {
|
message!("{}", err);
|
||||||
eprintln!("{}", err);
|
|
||||||
}
|
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !no_messages && !errs.is_empty() {
|
if !errs.is_empty() {
|
||||||
for err in errs {
|
for err in errs {
|
||||||
eprintln!("{}:{}", config_path.display(), err);
|
message!("{}:{}", config_path.display(), err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!(
|
debug!(
|
||||||
"{}: arguments loaded from config file: {:?}",
|
"{}: arguments loaded from config file: {:?}",
|
||||||
config_path.display(), args);
|
config_path.display(),
|
||||||
|
args
|
||||||
|
);
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +56,7 @@ fn parse<P: AsRef<Path>>(
|
|||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
match File::open(&path) {
|
match File::open(&path) {
|
||||||
Ok(file) => parse_reader(file),
|
Ok(file) => parse_reader(file),
|
||||||
Err(err) => errored!("{}: {}", path.display(), err),
|
Err(err) => Err(From::from(format!("{}: {}", path.display(), err))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,19 +34,30 @@ impl Log for Logger {
|
|||||||
match (record.file(), record.line()) {
|
match (record.file(), record.line()) {
|
||||||
(Some(file), Some(line)) => {
|
(Some(file), Some(line)) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}/{}/{}:{}: {}",
|
"{}|{}|{}:{}: {}",
|
||||||
record.level(), record.target(),
|
record.level(),
|
||||||
file, line, record.args());
|
record.target(),
|
||||||
|
file,
|
||||||
|
line,
|
||||||
|
record.args()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(Some(file), None) => {
|
(Some(file), None) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}/{}/{}: {}",
|
"{}|{}|{}: {}",
|
||||||
record.level(), record.target(), file, record.args());
|
record.level(),
|
||||||
|
record.target(),
|
||||||
|
file,
|
||||||
|
record.args()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{}/{}: {}",
|
"{}|{}: {}",
|
||||||
record.level(), record.target(), record.args());
|
record.level(),
|
||||||
|
record.target(),
|
||||||
|
record.args()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
66
src/main.rs
66
src/main.rs
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(dead_code, unused_imports, unused_mut, unused_variables)]
|
||||||
|
|
||||||
extern crate atty;
|
extern crate atty;
|
||||||
extern crate bytecount;
|
extern crate bytecount;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -6,6 +8,7 @@ extern crate encoding_rs;
|
|||||||
extern crate encoding_rs_io;
|
extern crate encoding_rs_io;
|
||||||
extern crate globset;
|
extern crate globset;
|
||||||
extern crate grep;
|
extern crate grep;
|
||||||
|
extern crate grep2;
|
||||||
extern crate ignore;
|
extern crate ignore;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
@ -39,31 +42,40 @@ macro_rules! errored {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod messages;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod args;
|
mod args;
|
||||||
|
mod args2;
|
||||||
mod config;
|
mod config;
|
||||||
mod decompressor;
|
mod decompressor;
|
||||||
mod preprocessor;
|
mod preprocessor;
|
||||||
mod logger;
|
mod logger;
|
||||||
|
mod main2;
|
||||||
|
mod path_printer;
|
||||||
mod pathutil;
|
mod pathutil;
|
||||||
mod printer;
|
mod printer;
|
||||||
|
mod search;
|
||||||
mod search_buffer;
|
mod search_buffer;
|
||||||
mod search_stream;
|
mod search_stream;
|
||||||
|
mod subject;
|
||||||
mod unescape;
|
mod unescape;
|
||||||
mod worker;
|
mod worker;
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Box<Error>>;
|
pub type Result<T> = result::Result<T, Box<Error>>;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
reset_sigpipe();
|
main2::main2();
|
||||||
match Args::parse().map(Arc::new).and_then(run) {
|
// reset_sigpipe();
|
||||||
Ok(0) => process::exit(1),
|
// match Args::parse().map(Arc::new).and_then(run) {
|
||||||
Ok(_) => process::exit(0),
|
// Ok(0) => process::exit(1),
|
||||||
Err(err) => {
|
// Ok(_) => process::exit(0),
|
||||||
eprintln!("{}", err);
|
// Err(err) => {
|
||||||
process::exit(2);
|
// eprintln!("{}", err);
|
||||||
}
|
// process::exit(2);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(args: Arc<Args>) -> Result<u64> {
|
fn run(args: Arc<Args>) -> Result<u64> {
|
||||||
@ -113,8 +125,6 @@ fn run_parallel(args: &Arc<Args>) -> Result<u64> {
|
|||||||
result,
|
result,
|
||||||
args.stdout_handle(),
|
args.stdout_handle(),
|
||||||
args.files(),
|
args.files(),
|
||||||
args.no_messages(),
|
|
||||||
args.no_ignore_messages(),
|
|
||||||
) {
|
) {
|
||||||
None => return Continue,
|
None => return Continue,
|
||||||
Some(dent) => dent,
|
Some(dent) => dent,
|
||||||
@ -145,11 +155,9 @@ fn run_parallel(args: &Arc<Args>) -> Result<u64> {
|
|||||||
Continue
|
Continue
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
if !args.paths().is_empty() && paths_searched.load(Ordering::SeqCst) == 0 {
|
if paths_searched.load(Ordering::SeqCst) == 0 {
|
||||||
if !args.no_messages() {
|
|
||||||
eprint_nothing_searched();
|
eprint_nothing_searched();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
let match_line_count = match_line_count.load(Ordering::SeqCst) as u64;
|
let match_line_count = match_line_count.load(Ordering::SeqCst) as u64;
|
||||||
let paths_searched = paths_searched.load(Ordering::SeqCst) as u64;
|
let paths_searched = paths_searched.load(Ordering::SeqCst) as u64;
|
||||||
let paths_matched = paths_matched.load(Ordering::SeqCst) as u64;
|
let paths_matched = paths_matched.load(Ordering::SeqCst) as u64;
|
||||||
@ -176,8 +184,6 @@ fn run_one_thread(args: &Arc<Args>) -> Result<u64> {
|
|||||||
result,
|
result,
|
||||||
args.stdout_handle(),
|
args.stdout_handle(),
|
||||||
args.files(),
|
args.files(),
|
||||||
args.no_messages(),
|
|
||||||
args.no_ignore_messages(),
|
|
||||||
) {
|
) {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(dent) => dent,
|
Some(dent) => dent,
|
||||||
@ -203,11 +209,9 @@ fn run_one_thread(args: &Arc<Args>) -> Result<u64> {
|
|||||||
paths_matched += 1;
|
paths_matched += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !args.paths().is_empty() && paths_searched == 0 {
|
if paths_searched == 0 {
|
||||||
if !args.no_messages() {
|
|
||||||
eprint_nothing_searched();
|
eprint_nothing_searched();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if args.stats() {
|
if args.stats() {
|
||||||
print_stats(
|
print_stats(
|
||||||
match_line_count,
|
match_line_count,
|
||||||
@ -241,8 +245,6 @@ fn run_files_parallel(args: Arc<Args>) -> Result<u64> {
|
|||||||
result,
|
result,
|
||||||
args.stdout_handle(),
|
args.stdout_handle(),
|
||||||
args.files(),
|
args.files(),
|
||||||
args.no_messages(),
|
|
||||||
args.no_ignore_messages(),
|
|
||||||
) {
|
) {
|
||||||
tx.send(dent).unwrap();
|
tx.send(dent).unwrap();
|
||||||
if args.quiet() {
|
if args.quiet() {
|
||||||
@ -263,8 +265,6 @@ fn run_files_one_thread(args: &Arc<Args>) -> Result<u64> {
|
|||||||
result,
|
result,
|
||||||
args.stdout_handle(),
|
args.stdout_handle(),
|
||||||
args.files(),
|
args.files(),
|
||||||
args.no_messages(),
|
|
||||||
args.no_ignore_messages(),
|
|
||||||
) {
|
) {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(dent) => dent,
|
Some(dent) => dent,
|
||||||
@ -293,21 +293,15 @@ fn get_or_log_dir_entry(
|
|||||||
result: result::Result<ignore::DirEntry, ignore::Error>,
|
result: result::Result<ignore::DirEntry, ignore::Error>,
|
||||||
stdout_handle: Option<&same_file::Handle>,
|
stdout_handle: Option<&same_file::Handle>,
|
||||||
files_only: bool,
|
files_only: bool,
|
||||||
no_messages: bool,
|
|
||||||
no_ignore_messages: bool,
|
|
||||||
) -> Option<ignore::DirEntry> {
|
) -> Option<ignore::DirEntry> {
|
||||||
match result {
|
match result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !no_messages {
|
message!("{}", err);
|
||||||
eprintln!("{}", err);
|
|
||||||
}
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Ok(dent) => {
|
Ok(dent) => {
|
||||||
if let Some(err) = dent.error() {
|
if let Some(err) = dent.error() {
|
||||||
if !no_messages && !no_ignore_messages {
|
ignore_message!("{}", err);
|
||||||
eprintln!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if dent.file_type().is_none() {
|
if dent.file_type().is_none() {
|
||||||
return Some(dent); // entry is stdin
|
return Some(dent); // entry is stdin
|
||||||
@ -321,7 +315,7 @@ fn get_or_log_dir_entry(
|
|||||||
}
|
}
|
||||||
// If we are redirecting stdout to a file, then don't search that
|
// If we are redirecting stdout to a file, then don't search that
|
||||||
// file.
|
// file.
|
||||||
if !files_only && is_stdout_file(&dent, stdout_handle, no_messages) {
|
if !files_only && is_stdout_file(&dent, stdout_handle) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(dent)
|
Some(dent)
|
||||||
@ -371,7 +365,6 @@ fn ignore_entry_is_file(dent: &ignore::DirEntry) -> bool {
|
|||||||
fn is_stdout_file(
|
fn is_stdout_file(
|
||||||
dent: &ignore::DirEntry,
|
dent: &ignore::DirEntry,
|
||||||
stdout_handle: Option<&same_file::Handle>,
|
stdout_handle: Option<&same_file::Handle>,
|
||||||
no_messages: bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let stdout_handle = match stdout_handle {
|
let stdout_handle = match stdout_handle {
|
||||||
None => return false,
|
None => return false,
|
||||||
@ -385,9 +378,7 @@ fn is_stdout_file(
|
|||||||
match same_file::Handle::from_path(dent.path()) {
|
match same_file::Handle::from_path(dent.path()) {
|
||||||
Ok(h) => stdout_handle == &h,
|
Ok(h) => stdout_handle == &h,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !no_messages {
|
message!("{}: {}", dent.path().display(), err);
|
||||||
eprintln!("{}: {}", dent.path().display(), err);
|
|
||||||
}
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -407,7 +398,8 @@ fn maybe_dent_eq_handle(_: &ignore::DirEntry, _: &same_file::Handle) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn eprint_nothing_searched() {
|
fn eprint_nothing_searched() {
|
||||||
eprintln!("No files were searched, which means ripgrep probably \
|
message!(
|
||||||
|
"No files were searched, which means ripgrep probably \
|
||||||
applied a filter you didn't expect. \
|
applied a filter you didn't expect. \
|
||||||
Try running again with --debug.");
|
Try running again with --debug.");
|
||||||
}
|
}
|
||||||
|
263
src/main2.rs
Normal file
263
src/main2.rs
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::process;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use grep2::printer::Stats;
|
||||||
|
use ignore::WalkState;
|
||||||
|
|
||||||
|
use args2::Args;
|
||||||
|
use subject::Subject;
|
||||||
|
use Result;
|
||||||
|
|
||||||
|
pub fn main2() {
|
||||||
|
match Args::parse().and_then(run) {
|
||||||
|
Ok(false) => process::exit(1),
|
||||||
|
Ok(true) => process::exit(0),
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{}", err);
|
||||||
|
process::exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(args: Args) -> Result<bool> {
|
||||||
|
use args2::Command::*;
|
||||||
|
|
||||||
|
match args.command()? {
|
||||||
|
Search => search(args),
|
||||||
|
SearchParallel => search_parallel(args),
|
||||||
|
SearchNever => Ok(false),
|
||||||
|
Files => files(args),
|
||||||
|
FilesParallel => files_parallel(args),
|
||||||
|
Types => types(args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The top-level entry point for single-threaded search. This recursively
|
||||||
|
/// steps through the file list (current directory by default) and searches
|
||||||
|
/// each file sequentially.
|
||||||
|
fn search(args: Args) -> Result<bool> {
|
||||||
|
let started_at = Instant::now();
|
||||||
|
let quit_after_match = args.quit_after_match()?;
|
||||||
|
let subject_builder = args.subject_builder();
|
||||||
|
let mut stats = args.stats()?;
|
||||||
|
let mut searcher = args.search_worker(args.stdout())?;
|
||||||
|
let mut matched = false;
|
||||||
|
|
||||||
|
for result in args.walker()? {
|
||||||
|
let subject = match subject_builder.build_from_result(result) {
|
||||||
|
Some(subject) => subject,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let search_result = match searcher.search(&subject) {
|
||||||
|
Ok(search_result) => search_result,
|
||||||
|
Err(err) => {
|
||||||
|
// A broken pipe means graceful termination.
|
||||||
|
if err.kind() == io::ErrorKind::BrokenPipe {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
message!("{}: {}", subject.path().display(), err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
matched = matched || search_result.has_match();
|
||||||
|
if let Some(ref mut stats) = stats {
|
||||||
|
*stats += search_result.stats().unwrap();
|
||||||
|
}
|
||||||
|
if matched && quit_after_match {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref stats) = stats {
|
||||||
|
let elapsed = Instant::now().duration_since(started_at);
|
||||||
|
// We don't care if we couldn't print this successfully.
|
||||||
|
let _ = searcher.printer().print_stats(elapsed, stats);
|
||||||
|
}
|
||||||
|
Ok(matched)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The top-level entry point for multi-threaded search. The parallelism is
|
||||||
|
/// itself achieved by the recursive directory traversal. All we need to do is
|
||||||
|
/// feed it a worker for performing a search on each file.
|
||||||
|
fn search_parallel(args: Args) -> Result<bool> {
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::atomic::Ordering::SeqCst;
|
||||||
|
|
||||||
|
let quit_after_match = args.quit_after_match()?;
|
||||||
|
let started_at = Instant::now();
|
||||||
|
let subject_builder = Arc::new(args.subject_builder());
|
||||||
|
let bufwtr = Arc::new(args.buffer_writer()?);
|
||||||
|
let stats = Arc::new(args.stats()?.map(Mutex::new));
|
||||||
|
let matched = Arc::new(AtomicBool::new(false));
|
||||||
|
let mut searcher_err = None;
|
||||||
|
args.walker_parallel()?.run(|| {
|
||||||
|
let args = args.clone();
|
||||||
|
let bufwtr = Arc::clone(&bufwtr);
|
||||||
|
let stats = Arc::clone(&stats);
|
||||||
|
let matched = Arc::clone(&matched);
|
||||||
|
let subject_builder = Arc::clone(&subject_builder);
|
||||||
|
let mut searcher = match args.search_worker(bufwtr.buffer()) {
|
||||||
|
Ok(searcher) => searcher,
|
||||||
|
Err(err) => {
|
||||||
|
searcher_err = Some(err);
|
||||||
|
return Box::new(move |_| {
|
||||||
|
WalkState::Quit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::new(move |result| {
|
||||||
|
let subject = match subject_builder.build_from_result(result) {
|
||||||
|
Some(subject) => subject,
|
||||||
|
None => return WalkState::Continue,
|
||||||
|
};
|
||||||
|
searcher.printer().get_mut().clear();
|
||||||
|
let search_result = match searcher.search(&subject) {
|
||||||
|
Ok(search_result) => search_result,
|
||||||
|
Err(err) => {
|
||||||
|
message!("{}: {}", subject.path().display(), err);
|
||||||
|
return WalkState::Continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if search_result.has_match() {
|
||||||
|
matched.store(true, SeqCst);
|
||||||
|
}
|
||||||
|
if let Some(ref locked_stats) = *stats {
|
||||||
|
let mut stats = locked_stats.lock().unwrap();
|
||||||
|
*stats += search_result.stats().unwrap();
|
||||||
|
}
|
||||||
|
if let Err(err) = bufwtr.print(searcher.printer().get_mut()) {
|
||||||
|
// A broken pipe means graceful termination.
|
||||||
|
if err.kind() == io::ErrorKind::BrokenPipe {
|
||||||
|
return WalkState::Quit;
|
||||||
|
}
|
||||||
|
// Otherwise, we continue on our merry way.
|
||||||
|
message!("{}: {}", subject.path().display(), err);
|
||||||
|
}
|
||||||
|
if matched.load(SeqCst) && quit_after_match {
|
||||||
|
WalkState::Quit
|
||||||
|
} else {
|
||||||
|
WalkState::Continue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if let Some(err) = searcher_err.take() {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
if let Some(ref locked_stats) = *stats {
|
||||||
|
let elapsed = Instant::now().duration_since(started_at);
|
||||||
|
let stats = locked_stats.lock().unwrap();
|
||||||
|
let mut searcher = args.search_worker(args.stdout())?;
|
||||||
|
// We don't care if we couldn't print this successfully.
|
||||||
|
let _ = searcher.printer().print_stats(elapsed, &stats);
|
||||||
|
}
|
||||||
|
Ok(matched.load(SeqCst))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The top-level entry point for listing files without searching them. This
|
||||||
|
/// recursively steps through the file list (current directory by default) and
|
||||||
|
/// prints each path sequentially using a single thread.
|
||||||
|
fn files(args: Args) -> Result<bool> {
|
||||||
|
let quit_after_match = args.quit_after_match()?;
|
||||||
|
let subject_builder = args.subject_builder();
|
||||||
|
let mut matched = false;
|
||||||
|
let mut path_printer = args.path_printer(args.stdout())?;
|
||||||
|
for result in args.walker()? {
|
||||||
|
let subject = match subject_builder.build_from_result(result) {
|
||||||
|
Some(subject) => subject,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
matched = true;
|
||||||
|
if quit_after_match {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Err(err) = path_printer.write_path(subject.path()) {
|
||||||
|
// A broken pipe means graceful termination.
|
||||||
|
if err.kind() == io::ErrorKind::BrokenPipe {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Otherwise, we have some other error that's preventing us from
|
||||||
|
// writing to stdout, so we should bubble it up.
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(matched)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The top-level entry point for listing files without searching them. This
|
||||||
|
/// recursively steps through the file list (current directory by default) and
|
||||||
|
/// prints each path sequentially using multiple threads.
|
||||||
|
fn files_parallel(args: Args) -> Result<bool> {
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::atomic::Ordering::SeqCst;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
let quit_after_match = args.quit_after_match()?;
|
||||||
|
let subject_builder = Arc::new(args.subject_builder());
|
||||||
|
let mut path_printer = args.path_printer(args.stdout())?;
|
||||||
|
let matched = Arc::new(AtomicBool::new(false));
|
||||||
|
let (tx, rx) = mpsc::channel::<Subject>();
|
||||||
|
|
||||||
|
let print_thread = thread::spawn(move || -> io::Result<()> {
|
||||||
|
for subject in rx.iter() {
|
||||||
|
path_printer.write_path(subject.path())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
args.walker_parallel()?.run(|| {
|
||||||
|
let args = args.clone();
|
||||||
|
let subject_builder = Arc::clone(&subject_builder);
|
||||||
|
let matched = Arc::clone(&matched);
|
||||||
|
let tx = tx.clone();
|
||||||
|
|
||||||
|
Box::new(move |result| {
|
||||||
|
let subject = match subject_builder.build_from_result(result) {
|
||||||
|
Some(subject) => subject,
|
||||||
|
None => return WalkState::Continue,
|
||||||
|
};
|
||||||
|
matched.store(true, SeqCst);
|
||||||
|
if quit_after_match {
|
||||||
|
WalkState::Quit
|
||||||
|
} else {
|
||||||
|
match tx.send(subject) {
|
||||||
|
Ok(_) => WalkState::Continue,
|
||||||
|
Err(_) => WalkState::Quit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
drop(tx);
|
||||||
|
if let Err(err) = print_thread.join().unwrap() {
|
||||||
|
// A broken pipe means graceful termination, so fall through.
|
||||||
|
// Otherwise, something bad happened while writing to stdout, so bubble
|
||||||
|
// it up.
|
||||||
|
if err.kind() != io::ErrorKind::BrokenPipe {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(matched.load(SeqCst))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The top-level entry point for --type-list.
|
||||||
|
fn types(args: Args) -> Result<bool> {
|
||||||
|
let mut count = 0;
|
||||||
|
let mut stdout = args.stdout();
|
||||||
|
for def in args.type_defs()? {
|
||||||
|
count += 1;
|
||||||
|
stdout.write_all(def.name().as_bytes())?;
|
||||||
|
stdout.write_all(b": ")?;
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
for glob in def.globs() {
|
||||||
|
if !first {
|
||||||
|
stdout.write_all(b", ")?;
|
||||||
|
}
|
||||||
|
stdout.write_all(glob.as_bytes())?;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
stdout.write_all(b"\n")?;
|
||||||
|
}
|
||||||
|
Ok(count > 0)
|
||||||
|
}
|
50
src/messages.rs
Normal file
50
src/messages.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use std::sync::atomic::{ATOMIC_BOOL_INIT, AtomicBool, Ordering};
|
||||||
|
|
||||||
|
static MESSAGES: AtomicBool = ATOMIC_BOOL_INIT;
|
||||||
|
static IGNORE_MESSAGES: AtomicBool = ATOMIC_BOOL_INIT;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! message {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if ::messages::messages() {
|
||||||
|
eprintln!($($tt)*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ignore_message {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if ::messages::messages() && ::messages::ignore_messages() {
|
||||||
|
eprintln!($($tt)*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if messages should be shown.
|
||||||
|
pub fn messages() -> bool {
|
||||||
|
MESSAGES.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether messages should be shown or not.
|
||||||
|
///
|
||||||
|
/// By default, they are not shown.
|
||||||
|
pub fn set_messages(yes: bool) {
|
||||||
|
MESSAGES.store(yes, Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if "ignore" related messages should be shown.
|
||||||
|
pub fn ignore_messages() -> bool {
|
||||||
|
IGNORE_MESSAGES.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether "ignore" related messages should be shown or not.
|
||||||
|
///
|
||||||
|
/// By default, they are not shown.
|
||||||
|
///
|
||||||
|
/// Note that this is overridden if `messages` is disabled. Namely, if
|
||||||
|
/// `messages` is disabled, then "ignore" messages are never shown, regardless
|
||||||
|
/// of this setting.
|
||||||
|
pub fn set_ignore_messages(yes: bool) {
|
||||||
|
IGNORE_MESSAGES.store(yes, Ordering::SeqCst)
|
||||||
|
}
|
101
src/path_printer.rs
Normal file
101
src/path_printer.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use grep2::printer::{ColorSpecs, PrinterPath};
|
||||||
|
use termcolor::WriteColor;
|
||||||
|
|
||||||
|
/// A configuration for describing how paths should be written.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Config {
|
||||||
|
colors: ColorSpecs,
|
||||||
|
separator: Option<u8>,
|
||||||
|
terminator: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
colors: ColorSpecs::default(),
|
||||||
|
separator: None,
|
||||||
|
terminator: b'\n',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A builder for constructing things to search over.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PathPrinterBuilder {
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathPrinterBuilder {
|
||||||
|
/// Return a new subject builder with a default configuration.
|
||||||
|
pub fn new() -> PathPrinterBuilder {
|
||||||
|
PathPrinterBuilder { config: Config::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new path printer with the current configuration that writes
|
||||||
|
/// paths to the given writer.
|
||||||
|
pub fn build<W: WriteColor>(&self, wtr: W) -> PathPrinter<W> {
|
||||||
|
PathPrinter {
|
||||||
|
config: self.config.clone(),
|
||||||
|
wtr: wtr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the color specification for this printer.
|
||||||
|
///
|
||||||
|
/// Currently, only the `path` component of the given specification is
|
||||||
|
/// used.
|
||||||
|
pub fn color_specs(
|
||||||
|
&mut self,
|
||||||
|
specs: ColorSpecs,
|
||||||
|
) -> &mut PathPrinterBuilder {
|
||||||
|
self.config.colors = specs;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A path separator.
|
||||||
|
///
|
||||||
|
/// When provided, the path's default separator will be replaced with
|
||||||
|
/// the given separator.
|
||||||
|
///
|
||||||
|
/// This is not set by default, and the system's default path separator
|
||||||
|
/// will be used.
|
||||||
|
pub fn separator(&mut self, sep: Option<u8>) -> &mut PathPrinterBuilder {
|
||||||
|
self.config.separator = sep;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A path terminator.
|
||||||
|
///
|
||||||
|
/// When printing a path, it will be by terminated by the given byte.
|
||||||
|
///
|
||||||
|
/// This is set to `\n` by default.
|
||||||
|
pub fn terminator(&mut self, terminator: u8) -> &mut PathPrinterBuilder {
|
||||||
|
self.config.terminator = terminator;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A printer for emitting paths to a writer, with optional color support.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PathPrinter<W> {
|
||||||
|
config: Config,
|
||||||
|
wtr: W,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: WriteColor> PathPrinter<W> {
|
||||||
|
/// Write the given path to the underlying writer.
|
||||||
|
pub fn write_path(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
let ppath = PrinterPath::with_separator(path, self.config.separator);
|
||||||
|
if !self.wtr.supports_color() {
|
||||||
|
self.wtr.write_all(ppath.as_bytes())?;
|
||||||
|
} else {
|
||||||
|
self.wtr.set_color(self.config.colors.path())?;
|
||||||
|
self.wtr.write_all(ppath.as_bytes())?;
|
||||||
|
self.wtr.reset()?;
|
||||||
|
}
|
||||||
|
self.wtr.write_all(&[self.config.terminator])
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,6 @@ typically faster than the same operations as provided in `std::path`. In
|
|||||||
particular, we really want to avoid the costly operation of parsing the path
|
particular, we really want to avoid the costly operation of parsing the path
|
||||||
into its constituent components. We give up on Windows, but on Unix, we deal
|
into its constituent components. We give up on Windows, but on Unix, we deal
|
||||||
with the raw bytes directly.
|
with the raw bytes directly.
|
||||||
|
|
||||||
On large repositories (like chromium), this can have a ~25% performance
|
|
||||||
improvement on just listing the files to search (!).
|
|
||||||
*/
|
*/
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@ use std::io::{self, Read};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{self, Stdio};
|
use std::process::{self, Stdio};
|
||||||
|
|
||||||
use Result;
|
|
||||||
|
|
||||||
/// PreprocessorReader provides an `io::Read` impl to read kids output.
|
/// PreprocessorReader provides an `io::Read` impl to read kids output.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PreprocessorReader {
|
pub struct PreprocessorReader {
|
||||||
@ -26,7 +24,7 @@ impl PreprocessorReader {
|
|||||||
pub fn from_cmd_path(
|
pub fn from_cmd_path(
|
||||||
cmd: PathBuf,
|
cmd: PathBuf,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<PreprocessorReader> {
|
) -> io::Result<PreprocessorReader> {
|
||||||
let child = process::Command::new(&cmd)
|
let child = process::Command::new(&cmd)
|
||||||
.arg(path)
|
.arg(path)
|
||||||
.stdin(Stdio::from(File::open(path)?))
|
.stdin(Stdio::from(File::open(path)?))
|
||||||
@ -34,10 +32,13 @@ impl PreprocessorReader {
|
|||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
"error running preprocessor command '{}': {}",
|
"error running preprocessor command '{}': {}",
|
||||||
cmd.display(),
|
cmd.display(),
|
||||||
err,
|
err,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
Ok(PreprocessorReader {
|
Ok(PreprocessorReader {
|
||||||
|
372
src/search.rs
Normal file
372
src/search.rs
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use grep2::matcher::Matcher;
|
||||||
|
use grep2::printer::{JSON, Standard, Summary, Stats};
|
||||||
|
use grep2::regex::RegexMatcher;
|
||||||
|
use grep2::searcher::Searcher;
|
||||||
|
use termcolor::WriteColor;
|
||||||
|
|
||||||
|
use decompressor::{DecompressionReader, is_compressed};
|
||||||
|
use preprocessor::PreprocessorReader;
|
||||||
|
use subject::Subject;
|
||||||
|
|
||||||
|
/// The configuration for the search worker. Among a few other things, the
|
||||||
|
/// configuration primarily controls the way we show search results to users
|
||||||
|
/// at a very high level.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Config {
|
||||||
|
preprocessor: Option<PathBuf>,
|
||||||
|
search_zip: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
preprocessor: None,
|
||||||
|
search_zip: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A builder for configuring and constructing a search worker.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SearchWorkerBuilder {
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SearchWorkerBuilder {
|
||||||
|
fn default() -> SearchWorkerBuilder {
|
||||||
|
SearchWorkerBuilder::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchWorkerBuilder {
|
||||||
|
/// Create a new builder for configuring and constructing a search worker.
|
||||||
|
pub fn new() -> SearchWorkerBuilder {
|
||||||
|
SearchWorkerBuilder { config: Config::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new search worker using the given searcher, matcher and
|
||||||
|
/// printer.
|
||||||
|
pub fn build<W: WriteColor>(
|
||||||
|
&self,
|
||||||
|
matcher: PatternMatcher,
|
||||||
|
searcher: Searcher,
|
||||||
|
printer: Printer<W>,
|
||||||
|
) -> SearchWorker<W> {
|
||||||
|
let config = self.config.clone();
|
||||||
|
SearchWorker { config, matcher, searcher, printer }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the path to a preprocessor command.
|
||||||
|
///
|
||||||
|
/// When this is set, instead of searching files directly, the given
|
||||||
|
/// command will be run with the file path as the first argument, and the
|
||||||
|
/// output of that command will be searched instead.
|
||||||
|
pub fn preprocessor(
|
||||||
|
&mut self,
|
||||||
|
cmd: Option<PathBuf>,
|
||||||
|
) -> &mut SearchWorkerBuilder {
|
||||||
|
self.config.preprocessor = cmd;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the decompression and searching of common compressed files.
|
||||||
|
///
|
||||||
|
/// When enabled, if a particular file path is recognized as a compressed
|
||||||
|
/// file, then it is decompressed before searching.
|
||||||
|
///
|
||||||
|
/// Note that if a preprocessor command is set, then it overrides this
|
||||||
|
/// setting.
|
||||||
|
pub fn search_zip(&mut self, yes: bool) -> &mut SearchWorkerBuilder {
|
||||||
|
self.config.search_zip = yes;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of executing a search.
|
||||||
|
///
|
||||||
|
/// Generally speaking, the "result" of a search is sent to a printer, which
|
||||||
|
/// writes results to an underlying writer such as stdout or a file. However,
|
||||||
|
/// every search also has some aggregate statistics or meta data that may be
|
||||||
|
/// useful to higher level routines.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SearchResult {
|
||||||
|
has_match: bool,
|
||||||
|
binary_byte_offset: Option<u64>,
|
||||||
|
stats: Option<Stats>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchResult {
|
||||||
|
/// Whether the search found a match or not.
|
||||||
|
pub fn has_match(&self) -> bool {
|
||||||
|
self.has_match
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the search found binary data, and if so, the first absolute
|
||||||
|
/// byte offset at which it was detected.
|
||||||
|
///
|
||||||
|
/// This always returns `None` if binary data detection is disabled, even
|
||||||
|
/// when binary data is present.
|
||||||
|
pub fn binary_byte_offset(&self) -> Option<u64> {
|
||||||
|
self.binary_byte_offset
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return aggregate search statistics for a single search, if available.
|
||||||
|
///
|
||||||
|
/// It can be expensive to compute statistics, so these are only present
|
||||||
|
/// if explicitly enabled in the printer provided by the caller.
|
||||||
|
pub fn stats(&self) -> Option<&Stats> {
|
||||||
|
self.stats.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The pattern matcher used by a search worker.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum PatternMatcher {
|
||||||
|
RustRegex(RegexMatcher),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The printer used by a search worker.
|
||||||
|
///
|
||||||
|
/// The `W` type parameter refers to the type of the underlying writer.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Printer<W> {
|
||||||
|
/// Use the standard printer, which supports the classic grep-like format.
|
||||||
|
Standard(Standard<W>),
|
||||||
|
/// Use the summary printer, which supports aggregate displays of search
|
||||||
|
/// results.
|
||||||
|
Summary(Summary<W>),
|
||||||
|
/// A JSON printer, which emits results in the JSON Lines format.
|
||||||
|
JSON(JSON<W>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: WriteColor> Printer<W> {
|
||||||
|
/// Print the given statistics to the underlying writer in a way that is
|
||||||
|
/// consistent with this printer's format.
|
||||||
|
///
|
||||||
|
/// While `Stats` contains a duration itself, this only corresponds to the
|
||||||
|
/// time spent searching, where as `total_duration` should roughly
|
||||||
|
/// approximate the lifespan of the ripgrep process itself.
|
||||||
|
pub fn print_stats(
|
||||||
|
&mut self,
|
||||||
|
total_duration: Duration,
|
||||||
|
stats: &Stats,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
match *self {
|
||||||
|
Printer::JSON(_) => unimplemented!(),
|
||||||
|
Printer::Standard(_) | Printer::Summary(_) => {
|
||||||
|
self.print_stats_human(total_duration, stats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_stats_human(
|
||||||
|
&mut self,
|
||||||
|
total_duration: Duration,
|
||||||
|
stats: &Stats,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let mut wtr = self.get_mut();
|
||||||
|
|
||||||
|
write!(
|
||||||
|
wtr,
|
||||||
|
"
|
||||||
|
{matches} matches
|
||||||
|
{lines} matched lines
|
||||||
|
{searches_with_match} files contained matches
|
||||||
|
{searches} files searched
|
||||||
|
{bytes_printed} bytes printed
|
||||||
|
{bytes_searched} bytes searched
|
||||||
|
{search_time:.6} seconds spent searching
|
||||||
|
{process_time:.6} seconds
|
||||||
|
",
|
||||||
|
matches = stats.matches(),
|
||||||
|
lines = stats.matched_lines(),
|
||||||
|
searches_with_match = stats.searches_with_match(),
|
||||||
|
searches = stats.searches(),
|
||||||
|
bytes_printed = stats.bytes_printed(),
|
||||||
|
bytes_searched = stats.bytes_searched(),
|
||||||
|
search_time = fractional_seconds(stats.elapsed()),
|
||||||
|
process_time = fractional_seconds(total_duration)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the underlying printer's writer.
|
||||||
|
pub fn get_mut(&mut self) -> &mut W {
|
||||||
|
match *self {
|
||||||
|
Printer::Standard(ref mut p) => p.get_mut(),
|
||||||
|
Printer::Summary(ref mut p) => p.get_mut(),
|
||||||
|
Printer::JSON(ref mut p) => p.get_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A worker for executing searches.
|
||||||
|
///
|
||||||
|
/// It is intended for a single worker to execute many searches, and is
|
||||||
|
/// generally intended to be used from a single thread. When searching using
|
||||||
|
/// multiple threads, it is better to create a new worker for each thread.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SearchWorker<W> {
|
||||||
|
config: Config,
|
||||||
|
matcher: PatternMatcher,
|
||||||
|
searcher: Searcher,
|
||||||
|
printer: Printer<W>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: WriteColor> SearchWorker<W> {
|
||||||
|
/// Execute a search over the given subject.
|
||||||
|
pub fn search(&mut self, subject: &Subject) -> io::Result<SearchResult> {
|
||||||
|
self.search_impl(subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the underlying printer.
|
||||||
|
pub fn printer(&mut self) -> &mut Printer<W> {
|
||||||
|
&mut self.printer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search the given subject using the appropriate strategy.
|
||||||
|
fn search_impl(&mut self, subject: &Subject) -> io::Result<SearchResult> {
|
||||||
|
let path = subject.path();
|
||||||
|
if subject.is_stdin() {
|
||||||
|
let stdin = io::stdin();
|
||||||
|
// A `return` here appeases the borrow checker. NLL will fix this.
|
||||||
|
return self.search_reader(path, stdin.lock());
|
||||||
|
} else if self.config.preprocessor.is_some() {
|
||||||
|
let cmd = self.config.preprocessor.clone().unwrap();
|
||||||
|
let rdr = PreprocessorReader::from_cmd_path(cmd, path)?;
|
||||||
|
self.search_reader(path, rdr)
|
||||||
|
} else if self.config.search_zip && is_compressed(path) {
|
||||||
|
match DecompressionReader::from_path(path) {
|
||||||
|
None => Ok(SearchResult::default()),
|
||||||
|
Some(rdr) => self.search_reader(path, rdr),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.search_path(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search the contents of the given file path.
|
||||||
|
fn search_path(&mut self, path: &Path) -> io::Result<SearchResult> {
|
||||||
|
use self::PatternMatcher::*;
|
||||||
|
|
||||||
|
let (searcher, printer) = (&mut self.searcher, &mut self.printer);
|
||||||
|
match self.matcher {
|
||||||
|
RustRegex(ref m) => search_path(m, searcher, printer, path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes a search on the given reader, which may or may not correspond
|
||||||
|
/// directly to the contents of the given file path. Instead, the reader
|
||||||
|
/// may actually cause something else to be searched (for example, when
|
||||||
|
/// a preprocessor is set or when decompression is enabled). In those
|
||||||
|
/// cases, the file path is used for visual purposes only.
|
||||||
|
///
|
||||||
|
/// Generally speaking, this method should only be used when there is no
|
||||||
|
/// other choice. Searching via `search_path` provides more opportunities
|
||||||
|
/// for optimizations (such as memory maps).
|
||||||
|
fn search_reader<R: io::Read>(
|
||||||
|
&mut self,
|
||||||
|
path: &Path,
|
||||||
|
rdr: R,
|
||||||
|
) -> io::Result<SearchResult> {
|
||||||
|
use self::PatternMatcher::*;
|
||||||
|
|
||||||
|
let (searcher, printer) = (&mut self.searcher, &mut self.printer);
|
||||||
|
match self.matcher {
|
||||||
|
RustRegex(ref m) => search_reader(m, searcher, printer, path, rdr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search the contents of the given file path using the given matcher,
|
||||||
|
/// searcher and printer.
|
||||||
|
fn search_path<M: Matcher, W: WriteColor>(
|
||||||
|
matcher: M,
|
||||||
|
searcher: &mut Searcher,
|
||||||
|
printer: &mut Printer<W>,
|
||||||
|
path: &Path,
|
||||||
|
) -> io::Result<SearchResult> {
|
||||||
|
match *printer {
|
||||||
|
Printer::Standard(ref mut p) => {
|
||||||
|
let mut sink = p.sink_with_path(&matcher, path);
|
||||||
|
searcher.search_path(&matcher, path, &mut sink)?;
|
||||||
|
Ok(SearchResult {
|
||||||
|
has_match: sink.has_match(),
|
||||||
|
binary_byte_offset: sink.binary_byte_offset(),
|
||||||
|
stats: sink.stats().map(|s| s.clone()),
|
||||||
|
..SearchResult::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Printer::Summary(ref mut p) => {
|
||||||
|
let mut sink = p.sink_with_path(&matcher, path);
|
||||||
|
searcher.search_path(&matcher, path, &mut sink)?;
|
||||||
|
Ok(SearchResult {
|
||||||
|
has_match: sink.has_match(),
|
||||||
|
binary_byte_offset: sink.binary_byte_offset(),
|
||||||
|
stats: sink.stats().map(|s| s.clone()),
|
||||||
|
..SearchResult::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Printer::JSON(ref mut p) => {
|
||||||
|
let mut sink = p.sink_with_path(&matcher, path);
|
||||||
|
searcher.search_path(&matcher, path, &mut sink)?;
|
||||||
|
Ok(SearchResult {
|
||||||
|
has_match: sink.has_match(),
|
||||||
|
binary_byte_offset: sink.binary_byte_offset(),
|
||||||
|
stats: Some(sink.stats().clone()),
|
||||||
|
..SearchResult::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search the contents of the given reader using the given matcher, searcher
|
||||||
|
/// and printer.
|
||||||
|
fn search_reader<M: Matcher, R: io::Read, W: WriteColor>(
|
||||||
|
matcher: M,
|
||||||
|
searcher: &mut Searcher,
|
||||||
|
printer: &mut Printer<W>,
|
||||||
|
path: &Path,
|
||||||
|
rdr: R,
|
||||||
|
) -> io::Result<SearchResult> {
|
||||||
|
match *printer {
|
||||||
|
Printer::Standard(ref mut p) => {
|
||||||
|
let mut sink = p.sink_with_path(&matcher, path);
|
||||||
|
searcher.search_reader(&matcher, rdr, &mut sink)?;
|
||||||
|
Ok(SearchResult {
|
||||||
|
has_match: sink.has_match(),
|
||||||
|
binary_byte_offset: sink.binary_byte_offset(),
|
||||||
|
stats: sink.stats().map(|s| s.clone()),
|
||||||
|
..SearchResult::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Printer::Summary(ref mut p) => {
|
||||||
|
let mut sink = p.sink_with_path(&matcher, path);
|
||||||
|
searcher.search_reader(&matcher, rdr, &mut sink)?;
|
||||||
|
Ok(SearchResult {
|
||||||
|
has_match: sink.has_match(),
|
||||||
|
binary_byte_offset: sink.binary_byte_offset(),
|
||||||
|
stats: sink.stats().map(|s| s.clone()),
|
||||||
|
..SearchResult::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Printer::JSON(ref mut p) => {
|
||||||
|
let mut sink = p.sink_with_path(&matcher, path);
|
||||||
|
searcher.search_reader(&matcher, rdr, &mut sink)?;
|
||||||
|
Ok(SearchResult {
|
||||||
|
has_match: sink.has_match(),
|
||||||
|
binary_byte_offset: sink.binary_byte_offset(),
|
||||||
|
stats: Some(sink.stats().clone()),
|
||||||
|
..SearchResult::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the given duration as fractional seconds.
|
||||||
|
fn fractional_seconds(duration: Duration) -> f64 {
|
||||||
|
(duration.as_secs() as f64) + (duration.subsec_nanos() as f64 * 1e-9)
|
||||||
|
}
|
231
src/subject.rs
Normal file
231
src/subject.rs
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ignore::{self, DirEntry};
|
||||||
|
use same_file::Handle;
|
||||||
|
|
||||||
|
/// A configuration for describing how subjects should be built.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Config {
|
||||||
|
skip: Option<Arc<Handle>>,
|
||||||
|
strip_dot_prefix: bool,
|
||||||
|
separator: Option<u8>,
|
||||||
|
terminator: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
skip: None,
|
||||||
|
strip_dot_prefix: false,
|
||||||
|
separator: None,
|
||||||
|
terminator: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A builder for constructing things to search over.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SubjectBuilder {
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubjectBuilder {
|
||||||
|
/// Return a new subject builder with a default configuration.
|
||||||
|
pub fn new() -> SubjectBuilder {
|
||||||
|
SubjectBuilder { config: Config::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new subject from a possibly missing directory entry.
|
||||||
|
///
|
||||||
|
/// If the directory entry isn't present, then the corresponding error is
|
||||||
|
/// logged if messages have been configured. Otherwise, if the subject is
|
||||||
|
/// deemed searchable, then it is returned.
|
||||||
|
pub fn build_from_result(
|
||||||
|
&self,
|
||||||
|
result: Result<DirEntry, ignore::Error>,
|
||||||
|
) -> Option<Subject> {
|
||||||
|
match result {
|
||||||
|
Ok(dent) => self.build(dent),
|
||||||
|
Err(err) => {
|
||||||
|
message!("{}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new subject using this builder's configuration.
|
||||||
|
///
|
||||||
|
/// If a subject could not be created or should otherwise not be searched,
|
||||||
|
/// then this returns `None` after emitting any relevant log messages.
|
||||||
|
pub fn build(&self, dent: DirEntry) -> Option<Subject> {
|
||||||
|
let subj = Subject {
|
||||||
|
dent: dent,
|
||||||
|
strip_dot_prefix: self.config.strip_dot_prefix,
|
||||||
|
};
|
||||||
|
if let Some(ignore_err) = subj.dent.error() {
|
||||||
|
ignore_message!("{}", ignore_err);
|
||||||
|
}
|
||||||
|
// If this entry represents stdin, then we always search it.
|
||||||
|
if subj.dent.is_stdin() {
|
||||||
|
return Some(subj);
|
||||||
|
}
|
||||||
|
// If we're supposed to skip a particular file, then skip it.
|
||||||
|
if let Some(ref handle) = self.config.skip {
|
||||||
|
match subj.equals(handle) {
|
||||||
|
Ok(false) => {} // fallthrough
|
||||||
|
Ok(true) => {
|
||||||
|
debug!(
|
||||||
|
"ignoring {}: (probably same file as stdout)",
|
||||||
|
subj.dent.path().display()
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
message!("{}: {}", subj.dent.path().display(), err);
|
||||||
|
debug!(
|
||||||
|
"ignoring {}: got error: {}",
|
||||||
|
subj.dent.path().display(), err
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If this subject has a depth of 0, then it was provided explicitly
|
||||||
|
// by an end user (or via a shell glob). In this case, we always want
|
||||||
|
// to search it if it even smells like a file (e.g., a symlink).
|
||||||
|
if subj.dent.depth() == 0 && !subj.is_dir() {
|
||||||
|
return Some(subj);
|
||||||
|
}
|
||||||
|
// At this point, we only want to search something it's explicitly a
|
||||||
|
// file. This omits symlinks. (If ripgrep was configured to follow
|
||||||
|
// symlinks, then they have already been followed by the directory
|
||||||
|
// traversal.)
|
||||||
|
if subj.is_file() {
|
||||||
|
return Some(subj);
|
||||||
|
}
|
||||||
|
// We got nothin. Emit a debug message, but only if this isn't a
|
||||||
|
// directory. Otherwise, emitting messages for directories is just
|
||||||
|
// noisy.
|
||||||
|
if !subj.is_dir() {
|
||||||
|
debug!(
|
||||||
|
"ignoring {}: failed to pass subject filter: \
|
||||||
|
file type: {:?}, metadata: {:?}",
|
||||||
|
subj.dent.path().display(),
|
||||||
|
subj.dent.file_type(),
|
||||||
|
subj.dent.metadata()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When provided, subjects that represent the same file as the handle
|
||||||
|
/// given will be skipped.
|
||||||
|
///
|
||||||
|
/// Typically, it is useful to pass a handle referring to stdout, such
|
||||||
|
/// that the file being written to isn't searched, which can lead to
|
||||||
|
/// an unbounded feedback mechanism.
|
||||||
|
///
|
||||||
|
/// Only one handle to skip can be provided.
|
||||||
|
pub fn skip(
|
||||||
|
&mut self,
|
||||||
|
handle: Option<Handle>,
|
||||||
|
) -> &mut SubjectBuilder {
|
||||||
|
self.config.skip = handle.map(Arc::new);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When enabled, if the subject's file path starts with `./` then it is
|
||||||
|
/// stripped.
|
||||||
|
///
|
||||||
|
/// This is useful when implicitly searching the current working directory.
|
||||||
|
pub fn strip_dot_prefix(&mut self, yes: bool) -> &mut SubjectBuilder {
|
||||||
|
self.config.strip_dot_prefix = yes;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A subject is a thing we want to search. Generally, a subject is either a
|
||||||
|
/// file or stdin.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Subject {
|
||||||
|
dent: DirEntry,
|
||||||
|
strip_dot_prefix: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Subject {
|
||||||
|
/// Return the file path corresponding to this subject.
|
||||||
|
///
|
||||||
|
/// If this subject corresponds to stdin, then a special `<stdin>` path
|
||||||
|
/// is returned instead.
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
if self.strip_dot_prefix && self.dent.path().starts_with("./") {
|
||||||
|
self.dent.path().strip_prefix("./").unwrap()
|
||||||
|
} else {
|
||||||
|
self.dent.path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if this entry corresponds to stdin.
|
||||||
|
pub fn is_stdin(&self) -> bool {
|
||||||
|
self.dent.is_stdin()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if this subject points to a directory.
|
||||||
|
///
|
||||||
|
/// This works around a bug in Rust's standard library:
|
||||||
|
/// https://github.com/rust-lang/rust/issues/46484
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn is_dir(&self) -> bool {
|
||||||
|
use std::os::windows::fs::MetadataExt;
|
||||||
|
use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
|
||||||
|
|
||||||
|
self.dent.metadata().map(|md| {
|
||||||
|
md.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0
|
||||||
|
}).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if this subject points to a directory.
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn is_dir(&self) -> bool {
|
||||||
|
self.dent.file_type().map_or(false, |ft| ft.is_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if this subject points to a file.
|
||||||
|
///
|
||||||
|
/// This works around a bug in Rust's standard library:
|
||||||
|
/// https://github.com/rust-lang/rust/issues/46484
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn is_file(&self) -> bool {
|
||||||
|
!self.is_dir()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if this subject points to a file.
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn is_file(&self) -> bool {
|
||||||
|
self.dent.file_type().map_or(false, |ft| ft.is_file())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if and only if this subject is believed to be equivalent
|
||||||
|
/// to the given handle. If there was a problem querying this subject for
|
||||||
|
/// information to determine equality, then that error is returned.
|
||||||
|
fn equals(&self, handle: &Handle) -> io::Result<bool> {
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn never_equal(dent: &DirEntry, handle: &Handle) -> bool {
|
||||||
|
dent.ino() != Some(handle.ino())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
fn never_equal(_: &DirEntry, _: &Handle) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we know for sure that these two things aren't equal, then avoid
|
||||||
|
// the costly extra stat call to determine equality.
|
||||||
|
if self.dent.is_stdin() || never_equal(&self.dent, handle) {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
Handle::from_path(self.path()).map(|h| &h == handle)
|
||||||
|
}
|
||||||
|
}
|
@ -44,7 +44,6 @@ struct Options {
|
|||||||
invert_match: bool,
|
invert_match: bool,
|
||||||
line_number: bool,
|
line_number: bool,
|
||||||
max_count: Option<u64>,
|
max_count: Option<u64>,
|
||||||
no_messages: bool,
|
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
text: bool,
|
text: bool,
|
||||||
preprocessor: Option<PathBuf>,
|
preprocessor: Option<PathBuf>,
|
||||||
@ -67,7 +66,6 @@ impl Default for Options {
|
|||||||
invert_match: false,
|
invert_match: false,
|
||||||
line_number: false,
|
line_number: false,
|
||||||
max_count: None,
|
max_count: None,
|
||||||
no_messages: false,
|
|
||||||
quiet: false,
|
quiet: false,
|
||||||
text: false,
|
text: false,
|
||||||
search_zip_files: false,
|
search_zip_files: false,
|
||||||
@ -200,14 +198,6 @@ impl WorkerBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If enabled, error messages are suppressed.
|
|
||||||
///
|
|
||||||
/// This is disabled by default.
|
|
||||||
pub fn no_messages(mut self, yes: bool) -> Self {
|
|
||||||
self.opts.no_messages = yes;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If enabled, don't show any output and quit searching after the first
|
/// If enabled, don't show any output and quit searching after the first
|
||||||
/// match is found.
|
/// match is found.
|
||||||
pub fn quiet(mut self, yes: bool) -> Self {
|
pub fn quiet(mut self, yes: bool) -> Self {
|
||||||
@ -265,9 +255,7 @@ impl Worker {
|
|||||||
match PreprocessorReader::from_cmd_path(cmd, path) {
|
match PreprocessorReader::from_cmd_path(cmd, path) {
|
||||||
Ok(reader) => self.search(printer, path, reader),
|
Ok(reader) => self.search(printer, path, reader),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !self.opts.no_messages {
|
message!("{}", err);
|
||||||
eprintln!("{}", err);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,9 +272,7 @@ impl Worker {
|
|||||||
let file = match File::open(path) {
|
let file = match File::open(path) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !self.opts.no_messages {
|
message!("{}: {}", path.display(), err);
|
||||||
eprintln!("{}: {}", path.display(), err);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -306,9 +292,7 @@ impl Worker {
|
|||||||
count
|
count
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !self.opts.no_messages {
|
message!("{}", err);
|
||||||
eprintln!("{}", err);
|
|
||||||
}
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
336
tests/tests.rs
336
tests/tests.rs
@ -91,8 +91,8 @@ be, to a very large extent, the result of luck. Sherlock Holmes
|
|||||||
sherlock!(dir, "Sherlock", ".", |wd: WorkDir, mut cmd| {
|
sherlock!(dir, "Sherlock", ".", |wd: WorkDir, mut cmd| {
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -148,19 +148,19 @@ sherlock!(with_heading_default, "Sherlock", ".",
|
|||||||
cmd.arg("-j1").arg("--heading");
|
cmd.arg("-j1").arg("--heading");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected1 = "\
|
let expected1 = "\
|
||||||
foo
|
./foo
|
||||||
Sherlock Holmes lives on Baker Street.
|
Sherlock Holmes lives on Baker Street.
|
||||||
|
|
||||||
sherlock
|
./sherlock
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
be, to a very large extent, the result of luck. Sherlock Holmes
|
be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
let expected2 = "\
|
let expected2 = "\
|
||||||
sherlock
|
./sherlock
|
||||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
be, to a very large extent, the result of luck. Sherlock Holmes
|
be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
|
|
||||||
foo
|
./foo
|
||||||
Sherlock Holmes lives on Baker Street.
|
Sherlock Holmes lives on Baker Street.
|
||||||
";
|
";
|
||||||
if lines != expected1 {
|
if lines != expected1 {
|
||||||
@ -289,14 +289,14 @@ sherlock!(file_types, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("file.rs", "Sherlock");
|
wd.create("file.rs", "Sherlock");
|
||||||
cmd.arg("-t").arg("rust");
|
cmd.arg("-t").arg("rust");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.rs:Sherlock\n");
|
assert_eq!(lines, "./file.rs:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(file_types_all, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(file_types_all, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
wd.create("file.py", "Sherlock");
|
wd.create("file.py", "Sherlock");
|
||||||
cmd.arg("-t").arg("all");
|
cmd.arg("-t").arg("all");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.py:Sherlock\n");
|
assert_eq!(lines, "./file.py:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(file_types_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(file_types_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
@ -305,7 +305,7 @@ sherlock!(file_types_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("file.rs", "Sherlock");
|
wd.create("file.rs", "Sherlock");
|
||||||
cmd.arg("-T").arg("rust");
|
cmd.arg("-T").arg("rust");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.py:Sherlock\n");
|
assert_eq!(lines, "./file.py:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(file_types_negate_all, "Sherlock", ".",
|
sherlock!(file_types_negate_all, "Sherlock", ".",
|
||||||
@ -315,8 +315,8 @@ sherlock!(file_types_negate_all, "Sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "\
|
assert_eq!(lines, "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
");
|
");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -333,18 +333,21 @@ sherlock!(file_type_add, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("file.wat", "Sherlock");
|
wd.create("file.wat", "Sherlock");
|
||||||
cmd.arg("--type-add").arg("wat:*.wat").arg("-t").arg("wat");
|
cmd.arg("--type-add").arg("wat:*.wat").arg("-t").arg("wat");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.wat:Sherlock\n");
|
assert_eq!(lines, "./file.wat:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(file_type_add_compose, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(file_type_add_compose, "Sherlock", ".",
|
||||||
|
|wd: WorkDir, mut cmd: Command| {
|
||||||
wd.create("file.py", "Sherlock");
|
wd.create("file.py", "Sherlock");
|
||||||
wd.create("file.rs", "Sherlock");
|
wd.create("file.rs", "Sherlock");
|
||||||
wd.create("file.wat", "Sherlock");
|
wd.create("file.wat", "Sherlock");
|
||||||
cmd.arg("--type-add").arg("wat:*.wat");
|
cmd.arg("--type-add").arg("wat:*.wat");
|
||||||
cmd.arg("--type-add").arg("combo:include:wat,py").arg("-t").arg("combo");
|
cmd.arg("--type-add").arg("combo:include:wat,py").arg("-t").arg("combo");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
println!("{}", lines);
|
assert_eq!(
|
||||||
assert_eq!(sort_lines(&lines), "file.py:Sherlock\nfile.wat:Sherlock\n");
|
sort_lines(&lines),
|
||||||
|
"./file.py:Sherlock\n./file.wat:Sherlock\n"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(glob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(glob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
@ -352,7 +355,7 @@ sherlock!(glob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("file.rs", "Sherlock");
|
wd.create("file.rs", "Sherlock");
|
||||||
cmd.arg("-g").arg("*.rs");
|
cmd.arg("-g").arg("*.rs");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.rs:Sherlock\n");
|
assert_eq!(lines, "./file.rs:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(glob_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(glob_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
@ -361,14 +364,14 @@ sherlock!(glob_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("file.rs", "Sherlock");
|
wd.create("file.rs", "Sherlock");
|
||||||
cmd.arg("-g").arg("!*.rs");
|
cmd.arg("-g").arg("!*.rs");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.py:Sherlock\n");
|
assert_eq!(lines, "./file.py:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(iglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(iglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
wd.create("file.HTML", "Sherlock");
|
wd.create("file.HTML", "Sherlock");
|
||||||
cmd.arg("--iglob").arg("*.html");
|
cmd.arg("--iglob").arg("*.html");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.HTML:Sherlock\n");
|
assert_eq!(lines, "./file.HTML:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(csglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(csglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
@ -376,15 +379,16 @@ sherlock!(csglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("file2.html", "Sherlock");
|
wd.create("file2.html", "Sherlock");
|
||||||
cmd.arg("--glob").arg("*.html");
|
cmd.arg("--glob").arg("*.html");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file2.html:Sherlock\n");
|
assert_eq!(lines, "./file2.html:Sherlock\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(byte_offset_only_matching, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(byte_offset_only_matching, "Sherlock", ".",
|
||||||
|
|wd: WorkDir, mut cmd: Command| {
|
||||||
cmd.arg("-b").arg("-o");
|
cmd.arg("-b").arg("-o");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:56:Sherlock
|
./sherlock:56:Sherlock
|
||||||
sherlock:177:Sherlock
|
./sherlock:177:Sherlock
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -392,35 +396,35 @@ sherlock:177:Sherlock
|
|||||||
sherlock!(count, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(count, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
cmd.arg("--count");
|
cmd.arg("--count");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "sherlock:2\n";
|
let expected = "./sherlock:2\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(count_matches, "the", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(count_matches, "the", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
cmd.arg("--count-matches");
|
cmd.arg("--count-matches");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "sherlock:4\n";
|
let expected = "./sherlock:4\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(count_matches_inverted, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(count_matches_inverted, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
cmd.arg("--count-matches").arg("--invert-match");
|
cmd.arg("--count-matches").arg("--invert-match");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "sherlock:4\n";
|
let expected = "./sherlock:4\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(count_matches_via_only, "the", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(count_matches_via_only, "the", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
cmd.arg("--count").arg("--only-matching");
|
cmd.arg("--count").arg("--only-matching");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "sherlock:4\n";
|
let expected = "./sherlock:4\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(files_with_matches, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(files_with_matches, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
cmd.arg("--files-with-matches");
|
cmd.arg("--files-with-matches");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "sherlock\n";
|
let expected = "./sherlock\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -429,7 +433,7 @@ sherlock!(files_without_matches, "Sherlock", ".",
|
|||||||
wd.create("file.py", "foo");
|
wd.create("file.py", "foo");
|
||||||
cmd.arg("--files-without-match");
|
cmd.arg("--files-without-match");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "file.py\n";
|
let expected = "./file.py\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -527,7 +531,7 @@ sherlock!(max_filesize_parse_no_suffix, "Sherlock", ".",
|
|||||||
cmd.arg("--max-filesize").arg("50").arg("--files");
|
cmd.arg("--max-filesize").arg("50").arg("--files");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
foo
|
./foo
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -541,7 +545,7 @@ sherlock!(max_filesize_parse_k_suffix, "Sherlock", ".",
|
|||||||
cmd.arg("--max-filesize").arg("4K").arg("--files");
|
cmd.arg("--max-filesize").arg("4K").arg("--files");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
foo
|
./foo
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -555,7 +559,7 @@ sherlock!(max_filesize_parse_m_suffix, "Sherlock", ".",
|
|||||||
cmd.arg("--max-filesize").arg("1M").arg("--files");
|
cmd.arg("--max-filesize").arg("1M").arg("--files");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
foo
|
./foo
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -583,8 +587,8 @@ sherlock!(no_ignore_hidden, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--hidden");
|
cmd.arg("--hidden");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
.sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./.sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
.sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./.sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -610,8 +614,8 @@ sherlock!(no_ignore, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--no-ignore");
|
cmd.arg("--no-ignore");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -653,8 +657,8 @@ sherlock!(ignore_git_parent_stop, "Sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -686,8 +690,8 @@ sherlock!(ignore_git_parent_stop_file, "Sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -740,8 +744,8 @@ sherlock!(no_parent_ignore_git, "Sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -771,8 +775,8 @@ sherlock!(symlink_follow, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
baz/sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./baz/sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
baz/sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./baz/sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, path(expected));
|
assert_eq!(lines, path(expected));
|
||||||
});
|
});
|
||||||
@ -783,8 +787,8 @@ sherlock!(unrestricted1, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -796,8 +800,8 @@ sherlock!(unrestricted2, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
.sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./.sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
.sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./.sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -807,7 +811,7 @@ sherlock!(unrestricted3, "foo", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-uuu");
|
cmd.arg("-uuu");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file:foo\x00bar\nfile:foo\x00baz\n");
|
assert_eq!(lines, "./file:foo\x00bar\n./file:foo\x00baz\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
sherlock!(vimgrep, "Sherlock|Watson", ".", |wd: WorkDir, mut cmd: Command| {
|
sherlock!(vimgrep, "Sherlock|Watson", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
@ -815,10 +819,10 @@ sherlock!(vimgrep, "Sherlock|Watson", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:1:16:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:1:16:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:1:57:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:1:57:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:3:49:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:3:49:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
sherlock:5:12:but Doctor Watson has to have it taken out for him and dusted,
|
./sherlock:5:12:but Doctor Watson has to have it taken out for him and dusted,
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -829,10 +833,10 @@ sherlock!(vimgrep_no_line, "Sherlock|Watson", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:16:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:16:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:57:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:57:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:49:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:49:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
sherlock:12:but Doctor Watson has to have it taken out for him and dusted,
|
./sherlock:12:but Doctor Watson has to have it taken out for him and dusted,
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -843,10 +847,10 @@ sherlock!(vimgrep_no_line_no_column, "Sherlock|Watson", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
sherlock:but Doctor Watson has to have it taken out for him and dusted,
|
./sherlock:but Doctor Watson has to have it taken out for him and dusted,
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -869,12 +873,12 @@ clean!(regression_25, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("src/llvm/foo", "test");
|
wd.create("src/llvm/foo", "test");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = path("src/llvm/foo:test\n");
|
let expected = path("./src/llvm/foo:test\n");
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
|
|
||||||
cmd.current_dir(wd.path().join("src"));
|
cmd.current_dir(wd.path().join("src"));
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = path("llvm/foo:test\n");
|
let expected = path("./llvm/foo:test\n");
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -885,7 +889,7 @@ clean!(regression_30, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("vendor/manifest", "test");
|
wd.create("vendor/manifest", "test");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = path("vendor/manifest:test\n");
|
let expected = path("./vendor/manifest:test\n");
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -927,7 +931,7 @@ clean!(regression_67, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("dir/bar", "test");
|
wd.create("dir/bar", "test");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, path("dir/bar:test\n"));
|
assert_eq!(lines, path("./dir/bar:test\n"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/87
|
// See: https://github.com/BurntSushi/ripgrep/issues/87
|
||||||
@ -945,7 +949,7 @@ clean!(regression_90, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create(".foo", "test");
|
wd.create(".foo", "test");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, ".foo:test\n");
|
assert_eq!(lines, "./.foo:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/93
|
// See: https://github.com/BurntSushi/ripgrep/issues/93
|
||||||
@ -954,7 +958,7 @@ clean!(regression_93, r"(\d{1,3}\.){3}\d{1,3}", ".",
|
|||||||
wd.create("foo", "192.168.1.1");
|
wd.create("foo", "192.168.1.1");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
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
|
// See: https://github.com/BurntSushi/ripgrep/issues/99
|
||||||
@ -966,7 +970,10 @@ clean!(regression_99, "test", ".",
|
|||||||
cmd.arg("-j1").arg("--heading");
|
cmd.arg("-j1").arg("--heading");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(sort_lines(&lines), sort_lines("bar\ntest\n\nfoo1\ntest\n"));
|
assert_eq!(
|
||||||
|
sort_lines(&lines),
|
||||||
|
sort_lines("./bar\ntest\n\n./foo1\ntest\n")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/105
|
// See: https://github.com/BurntSushi/ripgrep/issues/105
|
||||||
@ -975,7 +982,7 @@ clean!(regression_105_part1, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--vimgrep");
|
cmd.arg("--vimgrep");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:1:3:zztest\n");
|
assert_eq!(lines, "./foo:1:3:zztest\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/105
|
// See: https://github.com/BurntSushi/ripgrep/issues/105
|
||||||
@ -984,7 +991,7 @@ clean!(regression_105_part2, "test", ".", |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);
|
||||||
assert_eq!(lines, "foo:1:3:zztest\n");
|
assert_eq!(lines, "./foo:1:3:zztest\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/127
|
// See: https://github.com/BurntSushi/ripgrep/issues/127
|
||||||
@ -1009,8 +1016,8 @@ clean!(regression_127, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = format!("\
|
let expected = format!("\
|
||||||
{path}:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./{path}:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
{path}:be, to a very large extent, the result of luck. Sherlock Holmes
|
./{path}:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
", path=path("foo/watson"));
|
", path=path("foo/watson"));
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1021,7 +1028,7 @@ clean!(regression_128, "x", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-n");
|
cmd.arg("-n");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:5:x\n");
|
assert_eq!(lines, "./foo:5:x\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/131
|
// See: https://github.com/BurntSushi/ripgrep/issues/131
|
||||||
@ -1049,8 +1056,8 @@ sherlock!(regression_137, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
sym1:For the Doctor Watsons of this world, as opposed to the Sherlock
|
sym1:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sym1:be, to a very large extent, the result of luck. Sherlock Holmes
|
sym1:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
sym2:For the Doctor Watsons of this world, as opposed to the Sherlock
|
sym2:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
@ -1094,11 +1101,11 @@ clean!(regression_184, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("foo/bar/baz", "test");
|
wd.create("foo/bar/baz", "test");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, format!("{}:test\n", path("foo/bar/baz")));
|
assert_eq!(lines, format!("./{}:test\n", path("foo/bar/baz")));
|
||||||
|
|
||||||
cmd.current_dir(wd.path().join("./foo/bar"));
|
cmd.current_dir(wd.path().join("./foo/bar"));
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "baz:test\n");
|
assert_eq!(lines, "./baz:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/199
|
// See: https://github.com/BurntSushi/ripgrep/issues/199
|
||||||
@ -1107,7 +1114,7 @@ clean!(regression_199, r"\btest\b", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--smart-case");
|
cmd.arg("--smart-case");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:tEsT\n");
|
assert_eq!(lines, "./foo:tEsT\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/206
|
// See: https://github.com/BurntSushi/ripgrep/issues/206
|
||||||
@ -1117,7 +1124,7 @@ clean!(regression_206, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-g").arg("*.txt");
|
cmd.arg("-g").arg("*.txt");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, format!("{}:test\n", path("foo/bar.txt")));
|
assert_eq!(lines, format!("./{}:test\n", path("foo/bar.txt")));
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/210
|
// See: https://github.com/BurntSushi/ripgrep/issues/210
|
||||||
@ -1161,7 +1168,7 @@ clean!(regression_251, "привет", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-i");
|
cmd.arg("-i");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:привет\nfoo:Привет\nfoo:ПрИвЕт\n");
|
assert_eq!(lines, "./foo:привет\n./foo:Привет\n./foo:ПрИвЕт\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/256
|
// See: https://github.com/BurntSushi/ripgrep/issues/256
|
||||||
@ -1205,7 +1212,7 @@ clean!(regression_405, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-g").arg("!/foo/**");
|
cmd.arg("-g").arg("!/foo/**");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, format!("{}:test\n", path("bar/foo/file2.txt")));
|
assert_eq!(lines, format!("./{}:test\n", path("bar/foo/file2.txt")));
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/428
|
// See: https://github.com/BurntSushi/ripgrep/issues/428
|
||||||
@ -1220,7 +1227,7 @@ clean!(regression_428_color_context_path, "foo", ".",
|
|||||||
let expected = format!(
|
let expected = format!(
|
||||||
"{colored_path}:foo\n{colored_path}-bar\n",
|
"{colored_path}:foo\n{colored_path}-bar\n",
|
||||||
colored_path=format!(
|
colored_path=format!(
|
||||||
"\x1b\x5b\x30\x6d\x1b\x5b\x33\x35\x6d{path}\x1b\x5b\x30\x6d",
|
"\x1b\x5b\x30\x6d\x1b\x5b\x33\x35\x6d./{path}\x1b\x5b\x30\x6d",
|
||||||
path=path("sherlock")));
|
path=path("sherlock")));
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1234,16 +1241,17 @@ clean!(regression_428_unrecognized_style, "Sherlok", ".",
|
|||||||
let output = cmd.output().unwrap();
|
let output = cmd.output().unwrap();
|
||||||
let err = String::from_utf8_lossy(&output.stderr);
|
let err = String::from_utf8_lossy(&output.stderr);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
Unrecognized style attribute ''. Choose from: nobold, bold, nointense, intense, \
|
unrecognized style attribute ''. Choose from: nobold, bold, nointense, intense, \
|
||||||
nounderline, underline.
|
nounderline, underline.
|
||||||
";
|
";
|
||||||
assert_eq!(err, expected);
|
assert_eq!(err, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/493
|
// See: https://github.com/BurntSushi/ripgrep/issues/493
|
||||||
clean!(regression_493, " 're ", "input.txt", |wd: WorkDir, mut cmd: Command| {
|
clean!(regression_493, r"\b 're \b", "input.txt",
|
||||||
|
|wd: WorkDir, mut cmd: Command| {
|
||||||
wd.create("input.txt", "peshwaship 're seminomata");
|
wd.create("input.txt", "peshwaship 're seminomata");
|
||||||
cmd.arg("-o").arg("-w");
|
cmd.arg("-o");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, " 're \n");
|
assert_eq!(lines, " 're \n");
|
||||||
@ -1255,8 +1263,8 @@ sherlock!(regression_553_switch, "sherlock", ".",
|
|||||||
cmd.arg("-i");
|
cmd.arg("-i");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
|
|
||||||
@ -1264,8 +1272,8 @@ sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
|||||||
cmd.arg("-i");
|
cmd.arg("-i");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1305,12 +1313,9 @@ clean!(regression_599, "^$", "input.txt", |wd: WorkDir, mut cmd: Command| {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
// Technically, the expected output should only be two lines, but:
|
|
||||||
// https://github.com/BurntSushi/ripgrep/issues/441
|
|
||||||
let expected = "\
|
let expected = "\
|
||||||
[0m1[0m:[0m[31m[0m
|
[0m1[0m:[0m[31m[0m
|
||||||
[0m2[0m:[0m[31m[0m
|
[0m2[0m:[0m[31m[0m
|
||||||
[0m4[0m:
|
|
||||||
";
|
";
|
||||||
assert_eq!(expected, lines);
|
assert_eq!(expected, lines);
|
||||||
});
|
});
|
||||||
@ -1326,7 +1331,7 @@ clean!(regression_807, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
|
|
||||||
cmd.arg("--hidden");
|
cmd.arg("--hidden");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, format!("{}:test\n", path(".a/c/file")));
|
assert_eq!(lines, format!("./{}:test\n", path(".a/c/file")));
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/900
|
// See: https://github.com/BurntSushi/ripgrep/issues/900
|
||||||
@ -1343,7 +1348,7 @@ clean!(feature_1_sjis, "Шерлок Холмс", ".", |wd: WorkDir, mut cmd: Co
|
|||||||
cmd.arg("-Esjis");
|
cmd.arg("-Esjis");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:Шерлок Холмс\n");
|
assert_eq!(lines, "./foo:Шерлок Холмс\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
||||||
@ -1354,7 +1359,7 @@ clean!(feature_1_utf16_auto, "Шерлок Холмс", ".",
|
|||||||
wd.create_bytes("foo", &sherlock[..]);
|
wd.create_bytes("foo", &sherlock[..]);
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:Шерлок Холмс\n");
|
assert_eq!(lines, "./foo:Шерлок Холмс\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
||||||
@ -1366,7 +1371,7 @@ clean!(feature_1_utf16_explicit, "Шерлок Холмс", ".",
|
|||||||
cmd.arg("-Eutf-16le");
|
cmd.arg("-Eutf-16le");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:Шерлок Холмс\n");
|
assert_eq!(lines, "./foo:Шерлок Холмс\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
||||||
@ -1378,7 +1383,7 @@ clean!(feature_1_eucjp, "Шерлок Холмс", ".",
|
|||||||
cmd.arg("-Eeuc-jp");
|
cmd.arg("-Eeuc-jp");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:Шерлок Холмс\n");
|
assert_eq!(lines, "./foo:Шерлок Холмс\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
||||||
@ -1413,8 +1418,8 @@ sherlock!(feature_7_dash, "-f-", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
let output = wd.pipe(&mut cmd, "Sherlock");
|
let output = wd.pipe(&mut cmd, "Sherlock");
|
||||||
let lines = String::from_utf8_lossy(&output.stdout);
|
let lines = String::from_utf8_lossy(&output.stdout);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1439,8 +1444,8 @@ sherlock!(feature_34_only_matching, "Sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:Sherlock
|
./sherlock:Sherlock
|
||||||
sherlock:Sherlock
|
./sherlock:Sherlock
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1452,8 +1457,8 @@ sherlock!(feature_34_only_matching_line_column, "Sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:1:57:Sherlock
|
./sherlock:1:57:Sherlock
|
||||||
sherlock:3:49:Sherlock
|
./sherlock:3:49:Sherlock
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1476,15 +1481,15 @@ sherlock!(feature_45_relative_cwd, "test", ".",
|
|||||||
// First, get a baseline without applying ignore rules.
|
// First, get a baseline without applying ignore rules.
|
||||||
let lines = paths_from_stdout(wd.stdout(&mut cmd));
|
let lines = paths_from_stdout(wd.stdout(&mut cmd));
|
||||||
assert_eq!(lines, paths(&[
|
assert_eq!(lines, paths(&[
|
||||||
"bar/test", "baz/bar/test", "baz/baz/bar/test", "baz/foo",
|
"./bar/test", "./baz/bar/test", "./baz/baz/bar/test", "./baz/foo",
|
||||||
"baz/test", "foo", "test",
|
"./baz/test", "./foo", "./test",
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// Now try again with the ignore file activated.
|
// Now try again with the ignore file activated.
|
||||||
cmd.arg("--ignore-file").arg(".not-an-ignore");
|
cmd.arg("--ignore-file").arg(".not-an-ignore");
|
||||||
let lines = paths_from_stdout(wd.stdout(&mut cmd));
|
let lines = paths_from_stdout(wd.stdout(&mut cmd));
|
||||||
assert_eq!(lines, paths(&[
|
assert_eq!(lines, paths(&[
|
||||||
"baz/bar/test", "baz/baz/bar/test", "baz/test", "test",
|
"./baz/bar/test", "./baz/baz/bar/test", "./baz/test", "./test",
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// Now do it again, but inside the baz directory.
|
// Now do it again, but inside the baz directory.
|
||||||
@ -1496,7 +1501,7 @@ sherlock!(feature_45_relative_cwd, "test", ".",
|
|||||||
cmd.arg("test").arg(".").arg("--ignore-file").arg("../.not-an-ignore");
|
cmd.arg("test").arg(".").arg("--ignore-file").arg("../.not-an-ignore");
|
||||||
cmd.current_dir(wd.path().join("baz"));
|
cmd.current_dir(wd.path().join("baz"));
|
||||||
let lines = paths_from_stdout(wd.stdout(&mut cmd));
|
let lines = paths_from_stdout(wd.stdout(&mut cmd));
|
||||||
assert_eq!(lines, paths(&["baz/bar/test", "test"]));
|
assert_eq!(lines, paths(&["./baz/bar/test", "./test"]));
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/45
|
// See: https://github.com/BurntSushi/ripgrep/issues/45
|
||||||
@ -1509,7 +1514,7 @@ sherlock!(feature_45_precedence_with_others, "test", ".",
|
|||||||
|
|
||||||
cmd.arg("--ignore-file").arg(".not-an-ignore");
|
cmd.arg("--ignore-file").arg(".not-an-ignore");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "imp.log:test\n");
|
assert_eq!(lines, "./imp.log:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/45
|
// See: https://github.com/BurntSushi/ripgrep/issues/45
|
||||||
@ -1523,7 +1528,7 @@ sherlock!(feature_45_precedence_internal, "test", ".",
|
|||||||
cmd.arg("--ignore-file").arg(".not-an-ignore1");
|
cmd.arg("--ignore-file").arg(".not-an-ignore1");
|
||||||
cmd.arg("--ignore-file").arg(".not-an-ignore2");
|
cmd.arg("--ignore-file").arg(".not-an-ignore2");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "imp.log:test\n");
|
assert_eq!(lines, "./imp.log:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/68
|
// See: https://github.com/BurntSushi/ripgrep/issues/68
|
||||||
@ -1535,7 +1540,7 @@ clean!(feature_68_no_ignore_vcs, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--no-ignore-vcs");
|
cmd.arg("--no-ignore-vcs");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:test\n");
|
assert_eq!(lines, "./foo:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/70
|
// See: https://github.com/BurntSushi/ripgrep/issues/70
|
||||||
@ -1545,8 +1550,8 @@ sherlock!(feature_70_smart_case, "sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
./sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
./sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1557,7 +1562,7 @@ sherlock!(feature_89_files_with_matches, "Sherlock", ".",
|
|||||||
cmd.arg("--null").arg("--files-with-matches");
|
cmd.arg("--null").arg("--files-with-matches");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "sherlock\x00");
|
assert_eq!(lines, "./sherlock\x00");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
||||||
@ -1567,7 +1572,7 @@ sherlock!(feature_89_files_without_matches, "Sherlock", ".",
|
|||||||
cmd.arg("--null").arg("--files-without-match");
|
cmd.arg("--null").arg("--files-without-match");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "file.py\x00");
|
assert_eq!(lines, "./file.py\x00");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
||||||
@ -1576,7 +1581,7 @@ sherlock!(feature_89_count, "Sherlock", ".",
|
|||||||
cmd.arg("--null").arg("--count");
|
cmd.arg("--null").arg("--count");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "sherlock\x002\n");
|
assert_eq!(lines, "./sherlock\x002\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
||||||
@ -1585,7 +1590,7 @@ sherlock!(feature_89_files, "NADA", ".",
|
|||||||
cmd.arg("--null").arg("--files");
|
cmd.arg("--null").arg("--files");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "sherlock\x00");
|
assert_eq!(lines, "./sherlock\x00");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
// See: https://github.com/BurntSushi/ripgrep/issues/89
|
||||||
@ -1595,10 +1600,10 @@ sherlock!(feature_89_match, "Sherlock", ".",
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
sherlock\x00For the Doctor Watsons of this world, as opposed to the Sherlock
|
./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\x00Holmeses, success in the province of detective work must always
|
||||||
sherlock\x00be, to a very large extent, the result of luck. Sherlock Holmes
|
./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;
|
./sherlock\x00can extract a clew from a wisp of straw or a flake of cigar ash;
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
@ -1613,7 +1618,7 @@ clean!(feature_109_max_depth, "far", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--maxdepth").arg("2");
|
cmd.arg("--maxdepth").arg("2");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = path("one/pass:far\n");
|
let expected = path("./one/pass:far\n");
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1639,7 +1644,7 @@ clean!(feature_129_matches, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-M26");
|
cmd.arg("-M26");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "foo:test\nfoo:[Omitted long line with 2 matches]\n";
|
let expected = "./foo:test\n./foo:[Omitted long matching line]\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1649,7 +1654,7 @@ clean!(feature_129_context, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-M20").arg("-C1");
|
cmd.arg("-M20").arg("-C1");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "foo:test\nfoo-[Omitted long context line]\n";
|
let expected = "./foo:test\n./foo-[Omitted long context line]\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1659,7 +1664,7 @@ clean!(feature_129_replace, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("-M26").arg("-rfoo");
|
cmd.arg("-M26").arg("-rfoo");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "foo:foo\nfoo:[Omitted long line with 2 replacements]\n";
|
let expected = "./foo:foo\n./foo:[Omitted long line with 2 matches]\n";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1668,7 +1673,7 @@ clean!(feature_159_works, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.create("foo", "test\ntest");
|
wd.create("foo", "test\ntest");
|
||||||
cmd.arg("-m1");
|
cmd.arg("-m1");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "foo:test\n");
|
assert_eq!(lines, "./foo:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/159
|
// See: https://github.com/BurntSushi/ripgrep/issues/159
|
||||||
@ -1684,7 +1689,7 @@ clean!(feature_243_column_line, "test", ".", |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);
|
||||||
assert_eq!(lines, "foo:1:1:test\n");
|
assert_eq!(lines, "./foo:1:1:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/263
|
// See: https://github.com/BurntSushi/ripgrep/issues/263
|
||||||
@ -1696,7 +1701,7 @@ clean!(feature_263_sort_files, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--sort-files");
|
cmd.arg("--sort-files");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "abc:test\nbar:test\nfoo:test\nzoo:test\n");
|
assert_eq!(lines, "./abc:test\n./bar:test\n./foo:test\n./zoo:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/275
|
// See: https://github.com/BurntSushi/ripgrep/issues/275
|
||||||
@ -1706,7 +1711,7 @@ clean!(feature_275_pathsep, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
cmd.arg("--path-separator").arg("Z");
|
cmd.arg("--path-separator").arg("Z");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "fooZbar:test\n");
|
assert_eq!(lines, ".ZfooZbar:test\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/362
|
// See: https://github.com/BurntSushi/ripgrep/issues/362
|
||||||
@ -1746,7 +1751,7 @@ sherlock!(feature_419_zero_as_shortcut_for_null, "Sherlock", ".",
|
|||||||
cmd.arg("-0").arg("--count");
|
cmd.arg("-0").arg("--count");
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "sherlock\x002\n");
|
assert_eq!(lines, "./sherlock\x002\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1932,59 +1937,52 @@ fn feature_411_parallel_search_stats() {
|
|||||||
assert_eq!(lines.contains("seconds"), true);
|
assert_eq!(lines.contains("seconds"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
sherlock!(feature_411_ignore_stats_1, |wd: WorkDir, mut cmd: Command| {
|
|
||||||
cmd.arg("--files-with-matches");
|
|
||||||
cmd.arg("--stats");
|
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
|
||||||
assert_eq!(lines.contains("seconds"), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
sherlock!(feature_411_ignore_stats_2, |wd: WorkDir, mut cmd: Command| {
|
|
||||||
cmd.arg("--files-without-match");
|
|
||||||
cmd.arg("--stats");
|
|
||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
|
||||||
assert_eq!(lines.contains("seconds"), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn feature_740_passthru() {
|
fn feature_740_passthru() {
|
||||||
let wd = WorkDir::new("feature_740");
|
let wd = WorkDir::new("feature_740");
|
||||||
wd.create("file", "\nfoo\nbar\nfoobar\n\nbaz\n");
|
wd.create("file", "\nfoo\nbar\nfoobar\n\nbaz\n");
|
||||||
wd.create("patterns", "foo\n\nbar\n");
|
wd.create("patterns", "foo\nbar\n");
|
||||||
|
|
||||||
// We can't assume that the way colour specs are translated to ANSI
|
// We can't assume that the way colour specs are translated to ANSI
|
||||||
// sequences will remain stable, and --replace doesn't currently work with
|
// sequences will remain stable, and --replace doesn't currently work with
|
||||||
// pass-through, so for now we don't actually test the match sub-strings
|
// pass-through, so for now we don't actually test the match sub-strings
|
||||||
let common_args = &["-n", "--passthru"];
|
let common_args = &["-n", "--passthru"];
|
||||||
let expected = "\
|
let foo_expected = "\
|
||||||
1:
|
1-
|
||||||
2:foo
|
2:foo
|
||||||
3:bar
|
3-bar
|
||||||
4:foobar
|
4:foobar
|
||||||
5:
|
5-
|
||||||
6:baz
|
6-baz
|
||||||
";
|
";
|
||||||
|
|
||||||
// With single pattern
|
// With single pattern
|
||||||
let mut cmd = wd.command();
|
let mut cmd = wd.command();
|
||||||
cmd.args(common_args).arg("foo").arg("file");
|
cmd.args(common_args).arg("foo").arg("file");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, foo_expected);
|
||||||
|
|
||||||
|
let foo_bar_expected = "\
|
||||||
|
1-
|
||||||
|
2:foo
|
||||||
|
3:bar
|
||||||
|
4:foobar
|
||||||
|
5-
|
||||||
|
6-baz
|
||||||
|
";
|
||||||
|
|
||||||
// With multiple -e patterns
|
// With multiple -e patterns
|
||||||
let mut cmd = wd.command();
|
let mut cmd = wd.command();
|
||||||
cmd.args(common_args)
|
cmd.args(common_args)
|
||||||
.arg("-e").arg("foo").arg("-e").arg("bar").arg("file");
|
.arg("-e").arg("foo").arg("-e").arg("bar").arg("file");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, foo_bar_expected);
|
||||||
|
|
||||||
// With multiple -f patterns
|
// With multiple -f patterns
|
||||||
let mut cmd = wd.command();
|
let mut cmd = wd.command();
|
||||||
cmd.args(common_args).arg("-f").arg("patterns").arg("file");
|
cmd.args(common_args).arg("-f").arg("patterns").arg("file");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, foo_bar_expected);
|
||||||
|
|
||||||
// -c should override
|
// -c should override
|
||||||
let mut cmd = wd.command();
|
let mut cmd = wd.command();
|
||||||
@ -1992,15 +1990,35 @@ fn feature_740_passthru() {
|
|||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, "2\n");
|
assert_eq!(lines, "2\n");
|
||||||
|
|
||||||
|
let only_foo_expected = "\
|
||||||
|
1-
|
||||||
|
2:foo
|
||||||
|
3-bar
|
||||||
|
4:foo
|
||||||
|
5-
|
||||||
|
6-baz
|
||||||
|
";
|
||||||
|
|
||||||
// -o should conflict
|
// -o should conflict
|
||||||
let mut cmd = wd.command();
|
let mut cmd = wd.command();
|
||||||
cmd.args(common_args).arg("-o").arg("foo").arg("file");
|
cmd.args(common_args).arg("-o").arg("foo").arg("file");
|
||||||
wd.assert_err(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
|
assert_eq!(lines, only_foo_expected);
|
||||||
|
|
||||||
|
let replace_foo_expected = "\
|
||||||
|
1-
|
||||||
|
2:wat
|
||||||
|
3-bar
|
||||||
|
4:watbar
|
||||||
|
5-
|
||||||
|
6-baz
|
||||||
|
";
|
||||||
|
|
||||||
// -r should conflict
|
// -r should conflict
|
||||||
let mut cmd = wd.command();
|
let mut cmd = wd.command();
|
||||||
cmd.args(common_args).arg("-r").arg("$0").arg("foo").arg("file");
|
cmd.args(common_args).arg("-r").arg("wat").arg("foo").arg("file");
|
||||||
wd.assert_err(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
|
assert_eq!(lines, replace_foo_expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -2081,7 +2099,7 @@ fn regression_270() {
|
|||||||
let mut cmd = wd.command();
|
let mut cmd = wd.command();
|
||||||
cmd.arg("-e").arg("-test").arg("./");
|
cmd.arg("-e").arg("-test").arg("./");
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
assert_eq!(lines, path("foo:-test\n"));
|
assert_eq!(lines, path("./foo:-test\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/391
|
// See: https://github.com/BurntSushi/ripgrep/issues/391
|
||||||
@ -2232,8 +2250,8 @@ fn regression_693_context_option_in_contextless_mode() {
|
|||||||
|
|
||||||
let lines: String = wd.stdout(&mut cmd);
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
bar:1
|
./bar:1
|
||||||
foo:1
|
./foo:1
|
||||||
";
|
";
|
||||||
assert_eq!(lines, expected);
|
assert_eq!(lines, expected);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user