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:
		
							
								
								
									
										45
									
								
								src/args.rs
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								src/args.rs
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| use std::cmp; | ||||
| use std::env; | ||||
| use std::io; | ||||
| use std::fs; | ||||
| use std::io::{self, BufRead}; | ||||
| use std::path::{Path, PathBuf}; | ||||
| use std::process; | ||||
|  | ||||
| @@ -34,6 +35,7 @@ use Result; | ||||
| /// (TL;DR: The CLI parser is generated from the usage string below.) | ||||
| const USAGE: &'static str = " | ||||
| Usage: rg [options] -e PATTERN ... [<path> ...] | ||||
|        rg [options] -f FILE [<path> ...] | ||||
|        rg [options] <pattern> [<path> ...] | ||||
|        rg [options] --files [<path> ...] | ||||
|        rg [options] --type-list | ||||
| @@ -107,6 +109,11 @@ Less common options: | ||||
|     --debug | ||||
|         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 | ||||
|         Print each file that would be searched (but don't search). | ||||
|  | ||||
| @@ -242,6 +249,7 @@ pub struct RawArgs { | ||||
|     flag_count: bool, | ||||
|     flag_files_with_matches: bool, | ||||
|     flag_debug: bool, | ||||
|     flag_file: Option<String>, | ||||
|     flag_files: bool, | ||||
|     flag_follow: bool, | ||||
|     flag_glob: Vec<String>, | ||||
| @@ -479,23 +487,32 @@ impl RawArgs { | ||||
|         btypes.build().map_err(From::from) | ||||
|     } | ||||
|  | ||||
|     fn pattern(&self) -> String { | ||||
|         if !self.flag_regexp.is_empty() { | ||||
|             if self.flag_fixed_strings { | ||||
|                 self.flag_regexp.iter().cloned().map(|lit| { | ||||
|                     self.word_pattern(regex::quote(&lit)) | ||||
|                 }).collect::<Vec<String>>().join("|") | ||||
|     fn pattern(&self) -> Result<String> { | ||||
|         let patterns: Vec<String> = if !self.flag_regexp.is_empty() { | ||||
|             self.flag_regexp.iter().cloned().collect() | ||||
|         } else if let Some(ref file) = self.flag_file { | ||||
|             if file == "-" { | ||||
|                 // 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 { | ||||
|                 self.flag_regexp.iter().cloned().map(|pat| { | ||||
|                     self.word_pattern(pat) | ||||
|                 }).collect::<Vec<String>>().join("|") | ||||
|                 let f = try!(fs::File::open(&Path::new(file))); | ||||
|                 try!(io::BufReader::new(f).lines().collect()) | ||||
|             } | ||||
|         } else { | ||||
|             vec![self.arg_pattern.clone()] | ||||
|         }; | ||||
|  | ||||
|         if self.flag_fixed_strings { | ||||
|                 self.word_pattern(regex::quote(&self.arg_pattern)) | ||||
|             Ok(patterns.into_iter().map(|p| { | ||||
|                 self.word_pattern(regex::quote(&p)) | ||||
|             }).collect::<Vec<String>>().join("|")) | ||||
|         } else { | ||||
|                 self.word_pattern(self.arg_pattern.clone()) | ||||
|             } | ||||
|             Ok(patterns.into_iter().map(|p| { | ||||
|                 self.word_pattern(p) | ||||
|             }).collect::<Vec<String>>().join("|")) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -520,7 +537,7 @@ impl RawArgs { | ||||
|         let casei = | ||||
|             self.flag_ignore_case | ||||
|             && !self.flag_case_sensitive; | ||||
|         GrepBuilder::new(&self.pattern()) | ||||
|         GrepBuilder::new(&try!(self.pattern())) | ||||
|             .case_smart(smart) | ||||
|             .case_insensitive(casei) | ||||
|             .line_terminator(self.eol()) | ||||
|   | ||||
| @@ -902,6 +902,29 @@ clean!(regression_228, "test", ".", |wd: WorkDir, mut cmd: Command| { | ||||
|     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 | ||||
| sherlock!(feature_20_no_filename, "Sherlock", ".", | ||||
| |wd: WorkDir, mut cmd: Command| { | ||||
|   | ||||
| @@ -153,7 +153,41 @@ impl WorkDir { | ||||
|  | ||||
|     /// Gets the output of a command. If the command failed, then this panics. | ||||
|     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() { | ||||
|             let suggest = | ||||
|                 if o.stderr.is_empty() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user