mirror of
				https://github.com/BurntSushi/ripgrep.git
				synced 2025-10-30 23:17:47 +02:00 
			
		
		
		
	Allow specifying patterns with -f FILE and -f-
				
					
				
			This is a somewhat basic implementation of `-f-` (#7), with unit tests. Changes include: 1. The internals of the `pattern` function have been refactored to avoid code duplication, but there's a lot more we could do. Right now we read the entire pattern list into a `Vec`. 2. There's now a `WorkDir::pipe` command that allows sending standard input to `rg` when testing. Not implemented: aho-corasick.
This commit is contained in:
		
							
								
								
									
										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() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user