diff options
author | Andrew Gallant <jamslam@gmail.com> | 2019-04-08 19:28:38 -0400 |
---|---|---|
committer | Andrew Gallant <jamslam@gmail.com> | 2019-04-14 19:29:27 -0400 |
commit | a7d26c8f144a4957b75f71087a66692d0b25759a (patch) | |
tree | 4888ac5ea66643ac919d4e12c60cc51992bef11a /src/args.rs | |
parent | bd222ae93fa0cabe7d51ba8db40ece99579bdaed (diff) |
binary: rejigger ripgrep's handling of binary files
This commit attempts to surface binary filtering in a slightly more
user friendly way. Namely, before, ripgrep would silently stop
searching a file if it detected a NUL byte, even if it had previously
printed a match. This can lead to the user quite reasonably assuming
that there are no more matches, since a partial search is fairly
unintuitive. (ripgrep has this behavior by default because it really
wants to NOT search binary files at all, just like it doesn't search
gitignored or hidden files.)
With this commit, if a match has already been printed and ripgrep detects
a NUL byte, then it will print a warning message indicating that the search
stopped prematurely.
Moreover, this commit adds a new flag, --binary, which causes ripgrep to
stop filtering binary files, but in a way that still avoids dumping
binary data into terminals. That is, the --binary flag makes ripgrep
behave more like grep's default behavior.
For files explicitly specified in a search, e.g., `rg foo some-file`,
then no binary filtering is applied (just like no gitignore and no
hidden file filtering is applied). Instead, ripgrep behaves as if you
gave the --binary flag for all explicitly given files.
This was a fairly invasive change, and potentially increases the UX
complexity of ripgrep around binary files. (Before, there were two
binary modes, where as now there are three.) However, ripgrep is now a
bit louder with warning messages when binary file detection might
otherwise be hiding potential matches, so hopefully this is a net
improvement.
Finally, the `-uuu` convenience now maps to `--no-ignore --hidden
--binary`, since this is closer to the actualy intent of the
`--unrestricted` flag, i.e., to reduce ripgrep's smart filtering. As a
consequence, `rg -uuu foo` should now search roughly the same number of
bytes as `grep -r foo`, and `rg -uuua foo` should search roughly the
same number of bytes as `grep -ra foo`. (The "roughly" weasel word is
used because grep's and ripgrep's binary file detection might differ
somewhat---perhaps based on buffer sizes---which can impact exactly what
is and isn't searched.)
See the numerous tests in tests/binary.rs for intended behavior.
Fixes #306, Fixes #855
Diffstat (limited to 'src/args.rs')
-rw-r--r-- | src/args.rs | 45 |
1 files changed, 35 insertions, 10 deletions
diff --git a/src/args.rs b/src/args.rs index 61e1d4f3..6a5f09f9 100644 --- a/src/args.rs +++ b/src/args.rs @@ -286,15 +286,18 @@ impl Args { &self, wtr: W, ) -> Result<SearchWorker<W>> { + let matches = self.matches(); let matcher = self.matcher().clone(); let printer = self.printer(wtr)?; - let searcher = self.matches().searcher(self.paths())?; + let searcher = matches.searcher(self.paths())?; let mut builder = SearchWorkerBuilder::new(); builder - .json_stats(self.matches().is_present("json")) - .preprocessor(self.matches().preprocessor()) - .preprocessor_globs(self.matches().preprocessor_globs()?) - .search_zip(self.matches().is_present("search-zip")); + .json_stats(matches.is_present("json")) + .preprocessor(matches.preprocessor()) + .preprocessor_globs(matches.preprocessor_globs()?) + .search_zip(matches.is_present("search-zip")) + .binary_detection_implicit(matches.binary_detection_implicit()) + .binary_detection_explicit(matches.binary_detection_explicit()); Ok(builder.build(matcher, searcher, printer)) } @@ -802,8 +805,7 @@ impl ArgMatches { .before_context(ctx_before) .after_context(ctx_after) .passthru(self.is_present("passthru")) - .memory_map(self.mmap_choice(paths)) - .binary_detection(self.binary_detection()); + .memory_map(self.mmap_choice(paths)); match self.encoding()? { EncodingMode::Some(enc) => { builder.encoding(Some(enc)); @@ -862,19 +864,42 @@ impl ArgMatches { /// /// Methods are sorted alphabetically. impl ArgMatches { - /// Returns the form of binary detection to perform. - fn binary_detection(&self) -> BinaryDetection { + /// Returns the form of binary detection to perform on files that are + /// implicitly searched via recursive directory traversal. + fn binary_detection_implicit(&self) -> BinaryDetection { let none = self.is_present("text") - || self.unrestricted_count() >= 3 || self.is_present("null-data"); + let convert = + self.is_present("binary") + || self.unrestricted_count() >= 3; if none { BinaryDetection::none() + } else if convert { + BinaryDetection::convert(b'\x00') } else { BinaryDetection::quit(b'\x00') } } + /// Returns the form of binary detection to perform on files that are + /// explicitly searched via the user invoking ripgrep on a particular + /// file or files or stdin. + /// + /// In general, this should never be BinaryDetection::quit, since that acts + /// as a filter (but quitting immediately once a NUL byte is seen), and we + /// should never filter out files that the user wants to explicitly search. + fn binary_detection_explicit(&self) -> BinaryDetection { + let none = + self.is_present("text") + || self.is_present("null-data"); + if none { + BinaryDetection::none() + } else { + BinaryDetection::convert(b'\x00') + } + } + /// Returns true if the command line configuration implies that a match /// can never be shown. fn can_never_match(&self, patterns: &[String]) -> bool { |