mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-04-14 00:58:43 +02:00
Merge pull request #227 from emk/issue-7
Allow specifying patterns with `-f FILE` and `-f-`
This commit is contained in:
commit
39e1a0d694
49
src/args.rs
49
src/args.rs
@ -1,6 +1,7 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io;
|
use std::fs;
|
||||||
|
use std::io::{self, BufRead};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ use Result;
|
|||||||
/// (TL;DR: The CLI parser is generated from the usage string below.)
|
/// (TL;DR: The CLI parser is generated from the usage string below.)
|
||||||
const USAGE: &'static str = "
|
const USAGE: &'static str = "
|
||||||
Usage: rg [options] -e PATTERN ... [<path> ...]
|
Usage: rg [options] -e PATTERN ... [<path> ...]
|
||||||
|
rg [options] -f FILE [<path> ...]
|
||||||
rg [options] <pattern> [<path> ...]
|
rg [options] <pattern> [<path> ...]
|
||||||
rg [options] --files [<path> ...]
|
rg [options] --files [<path> ...]
|
||||||
rg [options] --type-list
|
rg [options] --type-list
|
||||||
@ -107,6 +109,11 @@ Less common options:
|
|||||||
--debug
|
--debug
|
||||||
Show debug messages.
|
Show debug messages.
|
||||||
|
|
||||||
|
-f, --file FILE
|
||||||
|
Search for patterns specified in a file, one per line. Empty pattern
|
||||||
|
lines will match all input lines, and the newline is not counted as part
|
||||||
|
of the pattern.
|
||||||
|
|
||||||
--files
|
--files
|
||||||
Print each file that would be searched (but don't search).
|
Print each file that would be searched (but don't search).
|
||||||
|
|
||||||
@ -242,6 +249,7 @@ pub struct RawArgs {
|
|||||||
flag_count: bool,
|
flag_count: bool,
|
||||||
flag_files_with_matches: bool,
|
flag_files_with_matches: bool,
|
||||||
flag_debug: bool,
|
flag_debug: bool,
|
||||||
|
flag_file: Option<String>,
|
||||||
flag_files: bool,
|
flag_files: bool,
|
||||||
flag_follow: bool,
|
flag_follow: bool,
|
||||||
flag_glob: Vec<String>,
|
flag_glob: Vec<String>,
|
||||||
@ -479,23 +487,32 @@ impl RawArgs {
|
|||||||
btypes.build().map_err(From::from)
|
btypes.build().map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern(&self) -> String {
|
fn pattern(&self) -> Result<String> {
|
||||||
if !self.flag_regexp.is_empty() {
|
let patterns: Vec<String> = if !self.flag_regexp.is_empty() {
|
||||||
if self.flag_fixed_strings {
|
self.flag_regexp.iter().cloned().collect()
|
||||||
self.flag_regexp.iter().cloned().map(|lit| {
|
} else if let Some(ref file) = self.flag_file {
|
||||||
self.word_pattern(regex::quote(&lit))
|
if file == "-" {
|
||||||
}).collect::<Vec<String>>().join("|")
|
// We need two local variables here to get the lock
|
||||||
|
// lifetimes correct.
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let result = stdin.lock().lines().collect();
|
||||||
|
try!(result)
|
||||||
} else {
|
} else {
|
||||||
self.flag_regexp.iter().cloned().map(|pat| {
|
let f = try!(fs::File::open(&Path::new(file)));
|
||||||
self.word_pattern(pat)
|
try!(io::BufReader::new(f).lines().collect())
|
||||||
}).collect::<Vec<String>>().join("|")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if self.flag_fixed_strings {
|
vec![self.arg_pattern.clone()]
|
||||||
self.word_pattern(regex::quote(&self.arg_pattern))
|
};
|
||||||
} else {
|
|
||||||
self.word_pattern(self.arg_pattern.clone())
|
if self.flag_fixed_strings {
|
||||||
}
|
Ok(patterns.into_iter().map(|p| {
|
||||||
|
self.word_pattern(regex::quote(&p))
|
||||||
|
}).collect::<Vec<String>>().join("|"))
|
||||||
|
} else {
|
||||||
|
Ok(patterns.into_iter().map(|p| {
|
||||||
|
self.word_pattern(p)
|
||||||
|
}).collect::<Vec<String>>().join("|"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,7 +537,7 @@ impl RawArgs {
|
|||||||
let casei =
|
let casei =
|
||||||
self.flag_ignore_case
|
self.flag_ignore_case
|
||||||
&& !self.flag_case_sensitive;
|
&& !self.flag_case_sensitive;
|
||||||
GrepBuilder::new(&self.pattern())
|
GrepBuilder::new(&try!(self.pattern()))
|
||||||
.case_smart(smart)
|
.case_smart(smart)
|
||||||
.case_insensitive(casei)
|
.case_insensitive(casei)
|
||||||
.line_terminator(self.eol())
|
.line_terminator(self.eol())
|
||||||
|
@ -902,6 +902,29 @@ clean!(regression_228, "test", ".", |wd: WorkDir, mut cmd: Command| {
|
|||||||
wd.assert_err(&mut cmd);
|
wd.assert_err(&mut cmd);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/7
|
||||||
|
sherlock!(feature_7, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| {
|
||||||
|
wd.create("pat", "Sherlock\nHolmes");
|
||||||
|
let lines: String = wd.stdout(&mut cmd);
|
||||||
|
let expected = "\
|
||||||
|
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||||
|
Holmeses, success in the province of detective work must always
|
||||||
|
be, to a very large extent, the result of luck. Sherlock Holmes
|
||||||
|
";
|
||||||
|
assert_eq!(lines, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/7
|
||||||
|
sherlock!(feature_7_dash, "-f-", ".", |wd: WorkDir, mut cmd: Command| {
|
||||||
|
let output = wd.pipe(&mut cmd, "Sherlock");
|
||||||
|
let lines = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let expected = "\
|
||||||
|
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
|
||||||
|
";
|
||||||
|
assert_eq!(lines, expected);
|
||||||
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/20
|
// See: https://github.com/BurntSushi/ripgrep/issues/20
|
||||||
sherlock!(feature_20_no_filename, "Sherlock", ".",
|
sherlock!(feature_20_no_filename, "Sherlock", ".",
|
||||||
|wd: WorkDir, mut cmd: Command| {
|
|wd: WorkDir, mut cmd: Command| {
|
||||||
|
@ -153,7 +153,41 @@ impl WorkDir {
|
|||||||
|
|
||||||
/// Gets the output of a command. If the command failed, then this panics.
|
/// Gets the output of a command. If the command failed, then this panics.
|
||||||
pub fn output(&self, cmd: &mut process::Command) -> process::Output {
|
pub fn output(&self, cmd: &mut process::Command) -> process::Output {
|
||||||
let o = cmd.output().unwrap();
|
let output = cmd.output().unwrap();
|
||||||
|
self.expect_success(cmd, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pipe `input` to a command, and collect the output.
|
||||||
|
pub fn pipe(
|
||||||
|
&self,
|
||||||
|
cmd: &mut process::Command,
|
||||||
|
input: &str
|
||||||
|
) -> process::Output {
|
||||||
|
cmd.stdin(process::Stdio::piped());
|
||||||
|
cmd.stdout(process::Stdio::piped());
|
||||||
|
cmd.stderr(process::Stdio::piped());
|
||||||
|
|
||||||
|
let mut child = cmd.spawn().unwrap();
|
||||||
|
|
||||||
|
// Pipe input to child process using a separate thread to avoid
|
||||||
|
// risk of deadlock between parent and child process.
|
||||||
|
let mut stdin = child.stdin.take().expect("expected standard input");
|
||||||
|
let input = input.to_owned();
|
||||||
|
let worker = thread::spawn(move || {
|
||||||
|
write!(stdin, "{}", input)
|
||||||
|
});
|
||||||
|
|
||||||
|
let output = self.expect_success(cmd, child.wait_with_output().unwrap());
|
||||||
|
worker.join().unwrap().unwrap();
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If `o` is not the output of a successful process run
|
||||||
|
fn expect_success(
|
||||||
|
&self,
|
||||||
|
cmd: &process::Command,
|
||||||
|
o: process::Output
|
||||||
|
) -> process::Output {
|
||||||
if !o.status.success() {
|
if !o.status.success() {
|
||||||
let suggest =
|
let suggest =
|
||||||
if o.stderr.is_empty() {
|
if o.stderr.is_empty() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user