mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2024-12-02 02:56:32 +02:00
style: rustfmt everything
This is why I was so intent on clearing the PR queue. This will effectively invalidate all existing patches, so I wanted to start from a clean slate. We do make one little tweak: we put the default type definitions in their own file and tell rustfmt to keep its grubby mits off of it. We also sort it lexicographically and hopefully will enforce that from here on.
This commit is contained in:
parent
c95f29e3ba
commit
0bc4f0447b
18
build.rs
18
build.rs
@ -21,7 +21,8 @@ fn main() {
|
||||
eprintln!(
|
||||
"OUT_DIR environment variable not defined. \
|
||||
Please file a bug: \
|
||||
https://github.com/BurntSushi/ripgrep/issues/new");
|
||||
https://github.com/BurntSushi/ripgrep/issues/new"
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
@ -90,8 +91,10 @@ fn generate_man_page<P: AsRef<Path>>(outdir: P) -> io::Result<()> {
|
||||
File::create(&txt_path)?.write_all(tpl.as_bytes())?;
|
||||
let result = process::Command::new("a2x")
|
||||
.arg("--no-xmllint")
|
||||
.arg("--doctype").arg("manpage")
|
||||
.arg("--format").arg("manpage")
|
||||
.arg("--doctype")
|
||||
.arg("manpage")
|
||||
.arg("--format")
|
||||
.arg("manpage")
|
||||
.arg(&txt_path)
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
@ -114,7 +117,7 @@ fn formatted_options() -> io::Result<String> {
|
||||
// ripgrep only has two positional arguments, and probably will only
|
||||
// ever have two positional arguments, so we just hardcode them into
|
||||
// the template.
|
||||
if let app::RGArgKind::Positional{..} = arg.kind {
|
||||
if let app::RGArgKind::Positional { .. } = arg.kind {
|
||||
continue;
|
||||
}
|
||||
formatted.push(formatted_arg(&arg)?);
|
||||
@ -124,7 +127,9 @@ fn formatted_options() -> io::Result<String> {
|
||||
|
||||
fn formatted_arg(arg: &RGArg) -> io::Result<String> {
|
||||
match arg.kind {
|
||||
RGArgKind::Positional{..} => panic!("unexpected positional argument"),
|
||||
RGArgKind::Positional { .. } => {
|
||||
panic!("unexpected positional argument")
|
||||
}
|
||||
RGArgKind::Switch { long, short, multiple } => {
|
||||
let mut out = vec![];
|
||||
|
||||
@ -163,7 +168,8 @@ fn formatted_arg(arg: &RGArg) -> io::Result<String> {
|
||||
}
|
||||
|
||||
fn formatted_doc_txt(arg: &RGArg) -> io::Result<String> {
|
||||
let paragraphs: Vec<String> = arg.doc_long
|
||||
let paragraphs: Vec<String> = arg
|
||||
.doc_long
|
||||
.replace("{", "{")
|
||||
.replace("}", r"}")
|
||||
.split("\n\n")
|
||||
|
@ -2,13 +2,13 @@ use std::fmt;
|
||||
use std::hash;
|
||||
use std::iter;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::{Path, is_separator};
|
||||
use std::path::{is_separator, Path};
|
||||
use std::str;
|
||||
|
||||
use regex;
|
||||
use regex::bytes::Regex;
|
||||
|
||||
use {Candidate, Error, ErrorKind, new_regex};
|
||||
use {new_regex, Candidate, Error, ErrorKind};
|
||||
|
||||
/// Describes a matching strategy for a particular pattern.
|
||||
///
|
||||
@ -85,16 +85,16 @@ pub struct Glob {
|
||||
}
|
||||
|
||||
impl PartialEq for Glob {
|
||||
fn eq(&self, other: &Glob) -> bool {
|
||||
self.glob == other.glob && self.opts == other.opts
|
||||
}
|
||||
fn eq(&self, other: &Glob) -> bool {
|
||||
self.glob == other.glob && self.opts == other.opts
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for Glob {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.glob.hash(state);
|
||||
self.opts.hash(state);
|
||||
}
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.glob.hash(state);
|
||||
self.opts.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Glob {
|
||||
@ -227,11 +227,15 @@ struct Tokens(Vec<Token>);
|
||||
|
||||
impl Deref for Tokens {
|
||||
type Target = Vec<Token>;
|
||||
fn deref(&self) -> &Vec<Token> { &self.0 }
|
||||
fn deref(&self) -> &Vec<Token> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Tokens {
|
||||
fn deref_mut(&mut self) -> &mut Vec<Token> { &mut self.0 }
|
||||
fn deref_mut(&mut self) -> &mut Vec<Token> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@ -242,10 +246,7 @@ enum Token {
|
||||
RecursivePrefix,
|
||||
RecursiveSuffix,
|
||||
RecursiveZeroOrMore,
|
||||
Class {
|
||||
negated: bool,
|
||||
ranges: Vec<(char, char)>,
|
||||
},
|
||||
Class { negated: bool, ranges: Vec<(char, char)> },
|
||||
Alternates(Vec<Tokens>),
|
||||
}
|
||||
|
||||
@ -257,12 +258,9 @@ impl Glob {
|
||||
|
||||
/// Returns a matcher for this pattern.
|
||||
pub fn compile_matcher(&self) -> GlobMatcher {
|
||||
let re = new_regex(&self.re)
|
||||
.expect("regex compilation shouldn't fail");
|
||||
GlobMatcher {
|
||||
pat: self.clone(),
|
||||
re: re,
|
||||
}
|
||||
let re =
|
||||
new_regex(&self.re).expect("regex compilation shouldn't fail");
|
||||
GlobMatcher { pat: self.clone(), re: re }
|
||||
}
|
||||
|
||||
/// Returns a strategic matcher.
|
||||
@ -273,13 +271,9 @@ impl Glob {
|
||||
#[cfg(test)]
|
||||
fn compile_strategic_matcher(&self) -> GlobStrategic {
|
||||
let strategy = MatchStrategy::new(self);
|
||||
let re = new_regex(&self.re)
|
||||
.expect("regex compilation shouldn't fail");
|
||||
GlobStrategic {
|
||||
strategy: strategy,
|
||||
pat: self.clone(),
|
||||
re: re,
|
||||
}
|
||||
let re =
|
||||
new_regex(&self.re).expect("regex compilation shouldn't fail");
|
||||
GlobStrategic { strategy: strategy, pat: self.clone(), re: re }
|
||||
}
|
||||
|
||||
/// Returns the original glob pattern used to build this pattern.
|
||||
@ -537,7 +531,7 @@ impl Glob {
|
||||
| Token::RecursiveZeroOrMore => {
|
||||
return None;
|
||||
}
|
||||
Token::Class{..} | Token::Alternates(..) => {
|
||||
Token::Class { .. } | Token::Alternates(..) => {
|
||||
// We *could* be a little smarter here, but either one
|
||||
// of these is going to prevent our literal optimizations
|
||||
// anyway, so give up.
|
||||
@ -574,10 +568,7 @@ impl<'a> GlobBuilder<'a> {
|
||||
///
|
||||
/// The pattern is not compiled until `build` is called.
|
||||
pub fn new(glob: &'a str) -> GlobBuilder<'a> {
|
||||
GlobBuilder {
|
||||
glob: glob,
|
||||
opts: GlobOptions::default(),
|
||||
}
|
||||
GlobBuilder { glob: glob, opts: GlobOptions::default() }
|
||||
}
|
||||
|
||||
/// Parses and builds the pattern.
|
||||
@ -875,25 +866,22 @@ impl<'a> Parser<'a> {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let is_suffix =
|
||||
match self.peek() {
|
||||
None => {
|
||||
assert!(self.bump().is_none());
|
||||
true
|
||||
}
|
||||
Some(',') | Some('}') if self.stack.len() >= 2 => {
|
||||
true
|
||||
}
|
||||
Some(c) if is_separator(c) => {
|
||||
assert!(self.bump().map(is_separator).unwrap_or(false));
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
self.push_token(Token::ZeroOrMore)?;
|
||||
self.push_token(Token::ZeroOrMore)?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let is_suffix = match self.peek() {
|
||||
None => {
|
||||
assert!(self.bump().is_none());
|
||||
true
|
||||
}
|
||||
Some(',') | Some('}') if self.stack.len() >= 2 => true,
|
||||
Some(c) if is_separator(c) => {
|
||||
assert!(self.bump().map(is_separator).unwrap_or(false));
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
self.push_token(Token::ZeroOrMore)?;
|
||||
self.push_token(Token::ZeroOrMore)?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
match self.pop_token()? {
|
||||
Token::RecursivePrefix => {
|
||||
self.push_token(Token::RecursivePrefix)?;
|
||||
@ -973,7 +961,10 @@ impl<'a> Parser<'a> {
|
||||
// invariant: in_range is only set when there is
|
||||
// already at least one character seen.
|
||||
add_to_last_range(
|
||||
&self.glob, ranges.last_mut().unwrap(), c)?;
|
||||
&self.glob,
|
||||
ranges.last_mut().unwrap(),
|
||||
c,
|
||||
)?;
|
||||
} else {
|
||||
ranges.push((c, c));
|
||||
}
|
||||
@ -987,10 +978,7 @@ impl<'a> Parser<'a> {
|
||||
// it as a literal.
|
||||
ranges.push(('-', '-'));
|
||||
}
|
||||
self.push_token(Token::Class {
|
||||
negated: negated,
|
||||
ranges: ranges,
|
||||
})
|
||||
self.push_token(Token::Class { negated: negated, ranges: ranges })
|
||||
}
|
||||
|
||||
fn bump(&mut self) -> Option<char> {
|
||||
@ -1019,9 +1007,9 @@ fn ends_with(needle: &[u8], haystack: &[u8]) -> bool {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use {GlobSetBuilder, ErrorKind};
|
||||
use super::{Glob, GlobBuilder, Token};
|
||||
use super::Token::*;
|
||||
use super::{Glob, GlobBuilder, Token};
|
||||
use {ErrorKind, GlobSetBuilder};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct Options {
|
||||
@ -1037,7 +1025,7 @@ mod tests {
|
||||
let pat = Glob::new($pat).unwrap();
|
||||
assert_eq!($tokens, pat.tokens.0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! syntaxerr {
|
||||
@ -1047,7 +1035,7 @@ mod tests {
|
||||
let err = Glob::new($pat).unwrap_err();
|
||||
assert_eq!(&$err, err.kind());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! toregex {
|
||||
@ -1129,7 +1117,9 @@ mod tests {
|
||||
};
|
||||
}
|
||||
|
||||
fn s(string: &str) -> String { string.to_string() }
|
||||
fn s(string: &str) -> String {
|
||||
string.to_string()
|
||||
}
|
||||
|
||||
fn class(s: char, e: char) -> Token {
|
||||
Class { negated: false, ranges: vec![(s, e)] }
|
||||
@ -1153,16 +1143,20 @@ mod tests {
|
||||
syntax!(any2, "a?b", vec![Literal('a'), Any, Literal('b')]);
|
||||
syntax!(seq1, "*", vec![ZeroOrMore]);
|
||||
syntax!(seq2, "a*b", vec![Literal('a'), ZeroOrMore, Literal('b')]);
|
||||
syntax!(seq3, "*a*b*", vec![
|
||||
ZeroOrMore, Literal('a'), ZeroOrMore, Literal('b'), ZeroOrMore,
|
||||
]);
|
||||
syntax!(
|
||||
seq3,
|
||||
"*a*b*",
|
||||
vec![ZeroOrMore, Literal('a'), ZeroOrMore, Literal('b'), ZeroOrMore,]
|
||||
);
|
||||
syntax!(rseq1, "**", vec![RecursivePrefix]);
|
||||
syntax!(rseq2, "**/", vec![RecursivePrefix]);
|
||||
syntax!(rseq3, "/**", vec![RecursiveSuffix]);
|
||||
syntax!(rseq4, "/**/", vec![RecursiveZeroOrMore]);
|
||||
syntax!(rseq5, "a/**/b", vec![
|
||||
Literal('a'), RecursiveZeroOrMore, Literal('b'),
|
||||
]);
|
||||
syntax!(
|
||||
rseq5,
|
||||
"a/**/b",
|
||||
vec![Literal('a'), RecursiveZeroOrMore, Literal('b'),]
|
||||
);
|
||||
syntax!(cls1, "[a]", vec![class('a', 'a')]);
|
||||
syntax!(cls2, "[!a]", vec![classn('a', 'a')]);
|
||||
syntax!(cls3, "[a-z]", vec![class('a', 'z')]);
|
||||
@ -1174,9 +1168,11 @@ mod tests {
|
||||
syntax!(cls9, "[a-]", vec![rclass(&[('a', 'a'), ('-', '-')])]);
|
||||
syntax!(cls10, "[-a-z]", vec![rclass(&[('-', '-'), ('a', 'z')])]);
|
||||
syntax!(cls11, "[a-z-]", vec![rclass(&[('a', 'z'), ('-', '-')])]);
|
||||
syntax!(cls12, "[-a-z-]", vec![
|
||||
rclass(&[('-', '-'), ('a', 'z'), ('-', '-')]),
|
||||
]);
|
||||
syntax!(
|
||||
cls12,
|
||||
"[-a-z-]",
|
||||
vec![rclass(&[('-', '-'), ('a', 'z'), ('-', '-')]),]
|
||||
);
|
||||
syntax!(cls13, "[]-z]", vec![class(']', 'z')]);
|
||||
syntax!(cls14, "[--z]", vec![class('-', 'z')]);
|
||||
syntax!(cls15, "[ --]", vec![class(' ', '-')]);
|
||||
@ -1194,26 +1190,14 @@ mod tests {
|
||||
syntaxerr!(err_range1, "[z-a]", ErrorKind::InvalidRange('z', 'a'));
|
||||
syntaxerr!(err_range2, "[z--]", ErrorKind::InvalidRange('z', '-'));
|
||||
|
||||
const CASEI: Options = Options {
|
||||
casei: Some(true),
|
||||
litsep: None,
|
||||
bsesc: None,
|
||||
};
|
||||
const SLASHLIT: Options = Options {
|
||||
casei: None,
|
||||
litsep: Some(true),
|
||||
bsesc: None,
|
||||
};
|
||||
const NOBSESC: Options = Options {
|
||||
casei: None,
|
||||
litsep: None,
|
||||
bsesc: Some(false),
|
||||
};
|
||||
const BSESC: Options = Options {
|
||||
casei: None,
|
||||
litsep: None,
|
||||
bsesc: Some(true),
|
||||
};
|
||||
const CASEI: Options =
|
||||
Options { casei: Some(true), litsep: None, bsesc: None };
|
||||
const SLASHLIT: Options =
|
||||
Options { casei: None, litsep: Some(true), bsesc: None };
|
||||
const NOBSESC: Options =
|
||||
Options { casei: None, litsep: None, bsesc: Some(false) };
|
||||
const BSESC: Options =
|
||||
Options { casei: None, litsep: None, bsesc: Some(true) };
|
||||
|
||||
toregex!(re_casei, "a", "(?i)^a$", &CASEI);
|
||||
|
||||
@ -1311,8 +1295,11 @@ mod tests {
|
||||
matches!(matchpat4, "*hello.txt", "some\\path\\to\\hello.txt");
|
||||
matches!(matchpat5, "*hello.txt", "/an/absolute/path/to/hello.txt");
|
||||
matches!(matchpat6, "*some/path/to/hello.txt", "some/path/to/hello.txt");
|
||||
matches!(matchpat7, "*some/path/to/hello.txt",
|
||||
"a/bigger/some/path/to/hello.txt");
|
||||
matches!(
|
||||
matchpat7,
|
||||
"*some/path/to/hello.txt",
|
||||
"a/bigger/some/path/to/hello.txt"
|
||||
);
|
||||
|
||||
matches!(matchescape, "_[[]_[]]_[?]_[*]_!_", "_[_]_?_*_!_");
|
||||
|
||||
@ -1375,28 +1362,44 @@ mod tests {
|
||||
nmatches!(matchnot15, "[!-]", "-");
|
||||
nmatches!(matchnot16, "*hello.txt", "hello.txt-and-then-some");
|
||||
nmatches!(matchnot17, "*hello.txt", "goodbye.txt");
|
||||
nmatches!(matchnot18, "*some/path/to/hello.txt",
|
||||
"some/path/to/hello.txt-and-then-some");
|
||||
nmatches!(matchnot19, "*some/path/to/hello.txt",
|
||||
"some/other/path/to/hello.txt");
|
||||
nmatches!(
|
||||
matchnot18,
|
||||
"*some/path/to/hello.txt",
|
||||
"some/path/to/hello.txt-and-then-some"
|
||||
);
|
||||
nmatches!(
|
||||
matchnot19,
|
||||
"*some/path/to/hello.txt",
|
||||
"some/other/path/to/hello.txt"
|
||||
);
|
||||
nmatches!(matchnot20, "a", "foo/a");
|
||||
nmatches!(matchnot21, "./foo", "foo");
|
||||
nmatches!(matchnot22, "**/foo", "foofoo");
|
||||
nmatches!(matchnot23, "**/foo/bar", "foofoo/bar");
|
||||
nmatches!(matchnot24, "/*.c", "mozilla-sha1/sha1.c");
|
||||
nmatches!(matchnot25, "*.c", "mozilla-sha1/sha1.c", SLASHLIT);
|
||||
nmatches!(matchnot26, "**/m4/ltoptions.m4",
|
||||
"csharp/src/packages/repositories.config", SLASHLIT);
|
||||
nmatches!(
|
||||
matchnot26,
|
||||
"**/m4/ltoptions.m4",
|
||||
"csharp/src/packages/repositories.config",
|
||||
SLASHLIT
|
||||
);
|
||||
nmatches!(matchnot27, "a[^0-9]b", "a0b");
|
||||
nmatches!(matchnot28, "a[^0-9]b", "a9b");
|
||||
nmatches!(matchnot29, "[^-]", "-");
|
||||
nmatches!(matchnot30, "some/*/needle.txt", "some/needle.txt");
|
||||
nmatches!(
|
||||
matchrec31,
|
||||
"some/*/needle.txt", "some/one/two/needle.txt", SLASHLIT);
|
||||
"some/*/needle.txt",
|
||||
"some/one/two/needle.txt",
|
||||
SLASHLIT
|
||||
);
|
||||
nmatches!(
|
||||
matchrec32,
|
||||
"some/*/needle.txt", "some/one/two/three/needle.txt", SLASHLIT);
|
||||
"some/*/needle.txt",
|
||||
"some/one/two/three/needle.txt",
|
||||
SLASHLIT
|
||||
);
|
||||
|
||||
macro_rules! extract {
|
||||
($which:ident, $name:ident, $pat:expr, $expect:expr) => {
|
||||
@ -1458,19 +1461,27 @@ mod tests {
|
||||
literal!(extract_lit7, "foo/bar", Some(s("foo/bar")));
|
||||
literal!(extract_lit8, "**/foo/bar", None);
|
||||
|
||||
basetokens!(extract_basetoks1, "**/foo", Some(&*vec![
|
||||
Literal('f'), Literal('o'), Literal('o'),
|
||||
]));
|
||||
basetokens!(
|
||||
extract_basetoks1,
|
||||
"**/foo",
|
||||
Some(&*vec![Literal('f'), Literal('o'), Literal('o'),])
|
||||
);
|
||||
basetokens!(extract_basetoks2, "**/foo", None, CASEI);
|
||||
basetokens!(extract_basetoks3, "**/foo", Some(&*vec![
|
||||
Literal('f'), Literal('o'), Literal('o'),
|
||||
]), SLASHLIT);
|
||||
basetokens!(
|
||||
extract_basetoks3,
|
||||
"**/foo",
|
||||
Some(&*vec![Literal('f'), Literal('o'), Literal('o'),]),
|
||||
SLASHLIT
|
||||
);
|
||||
basetokens!(extract_basetoks4, "*foo", None, SLASHLIT);
|
||||
basetokens!(extract_basetoks5, "*foo", None);
|
||||
basetokens!(extract_basetoks6, "**/fo*o", None);
|
||||
basetokens!(extract_basetoks7, "**/fo*o", Some(&*vec![
|
||||
Literal('f'), Literal('o'), ZeroOrMore, Literal('o'),
|
||||
]), SLASHLIT);
|
||||
basetokens!(
|
||||
extract_basetoks7,
|
||||
"**/fo*o",
|
||||
Some(&*vec![Literal('f'), Literal('o'), ZeroOrMore, Literal('o'),]),
|
||||
SLASHLIT
|
||||
);
|
||||
|
||||
ext!(extract_ext1, "**/*.rs", Some(s(".rs")));
|
||||
ext!(extract_ext2, "**/*.rs.bak", None);
|
||||
|
@ -119,12 +119,12 @@ use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
use aho_corasick::AhoCorasick;
|
||||
use bstr::{B, ByteSlice, ByteVec};
|
||||
use bstr::{ByteSlice, ByteVec, B};
|
||||
use regex::bytes::{Regex, RegexBuilder, RegexSet};
|
||||
|
||||
use pathutil::{file_name, file_name_ext, normalize_path};
|
||||
use glob::MatchStrategy;
|
||||
pub use glob::{Glob, GlobBuilder, GlobMatcher};
|
||||
use pathutil::{file_name, file_name_ext, normalize_path};
|
||||
|
||||
mod glob;
|
||||
mod pathutil;
|
||||
@ -202,9 +202,7 @@ impl ErrorKind {
|
||||
ErrorKind::UnclosedClass => {
|
||||
"unclosed character class; missing ']'"
|
||||
}
|
||||
ErrorKind::InvalidRange(_, _) => {
|
||||
"invalid character range"
|
||||
}
|
||||
ErrorKind::InvalidRange(_, _) => "invalid character range",
|
||||
ErrorKind::UnopenedAlternates => {
|
||||
"unopened alternate group; missing '{' \
|
||||
(maybe escape '}' with '[}]'?)"
|
||||
@ -216,9 +214,7 @@ impl ErrorKind {
|
||||
ErrorKind::NestedAlternates => {
|
||||
"nested alternate groups are not allowed"
|
||||
}
|
||||
ErrorKind::DanglingEscape => {
|
||||
"dangling '\\'"
|
||||
}
|
||||
ErrorKind::DanglingEscape => "dangling '\\'",
|
||||
ErrorKind::Regex(ref err) => err,
|
||||
ErrorKind::__Nonexhaustive => unreachable!(),
|
||||
}
|
||||
@ -245,9 +241,7 @@ impl fmt::Display for ErrorKind {
|
||||
| ErrorKind::UnclosedAlternates
|
||||
| ErrorKind::NestedAlternates
|
||||
| ErrorKind::DanglingEscape
|
||||
| ErrorKind::Regex(_) => {
|
||||
write!(f, "{}", self.description())
|
||||
}
|
||||
| ErrorKind::Regex(_) => write!(f, "{}", self.description()),
|
||||
ErrorKind::InvalidRange(s, e) => {
|
||||
write!(f, "invalid range; '{}' > '{}'", s, e)
|
||||
}
|
||||
@ -262,21 +256,20 @@ fn new_regex(pat: &str) -> Result<Regex, Error> {
|
||||
.size_limit(10 * (1 << 20))
|
||||
.dfa_size_limit(10 * (1 << 20))
|
||||
.build()
|
||||
.map_err(|err| {
|
||||
Error {
|
||||
glob: Some(pat.to_string()),
|
||||
kind: ErrorKind::Regex(err.to_string()),
|
||||
}
|
||||
.map_err(|err| Error {
|
||||
glob: Some(pat.to_string()),
|
||||
kind: ErrorKind::Regex(err.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn new_regex_set<I, S>(pats: I) -> Result<RegexSet, Error>
|
||||
where S: AsRef<str>, I: IntoIterator<Item=S> {
|
||||
RegexSet::new(pats).map_err(|err| {
|
||||
Error {
|
||||
glob: None,
|
||||
kind: ErrorKind::Regex(err.to_string()),
|
||||
}
|
||||
where
|
||||
S: AsRef<str>,
|
||||
I: IntoIterator<Item = S>,
|
||||
{
|
||||
RegexSet::new(pats).map_err(|err| Error {
|
||||
glob: None,
|
||||
kind: ErrorKind::Regex(err.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
@ -294,10 +287,7 @@ impl GlobSet {
|
||||
/// Create an empty `GlobSet`. An empty set matches nothing.
|
||||
#[inline]
|
||||
pub fn empty() -> GlobSet {
|
||||
GlobSet {
|
||||
len: 0,
|
||||
strats: vec![],
|
||||
}
|
||||
GlobSet { len: 0, strats: vec![] }
|
||||
}
|
||||
|
||||
/// Returns true if this set is empty, and therefore matches nothing.
|
||||
@ -432,11 +422,17 @@ impl GlobSet {
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("built glob set; {} literals, {} basenames, {} extensions, \
|
||||
debug!(
|
||||
"built glob set; {} literals, {} basenames, {} extensions, \
|
||||
{} prefixes, {} suffixes, {} required extensions, {} regexes",
|
||||
lits.0.len(), base_lits.0.len(), exts.0.len(),
|
||||
prefixes.literals.len(), suffixes.literals.len(),
|
||||
required_exts.0.len(), regexes.literals.len());
|
||||
lits.0.len(),
|
||||
base_lits.0.len(),
|
||||
exts.0.len(),
|
||||
prefixes.literals.len(),
|
||||
suffixes.literals.len(),
|
||||
required_exts.0.len(),
|
||||
regexes.literals.len()
|
||||
);
|
||||
Ok(GlobSet {
|
||||
len: pats.len(),
|
||||
strats: vec![
|
||||
@ -446,7 +442,8 @@ impl GlobSet {
|
||||
GlobSetMatchStrategy::Suffix(suffixes.suffix()),
|
||||
GlobSetMatchStrategy::Prefix(prefixes.prefix()),
|
||||
GlobSetMatchStrategy::RequiredExtension(
|
||||
required_exts.build()?),
|
||||
required_exts.build()?,
|
||||
),
|
||||
GlobSetMatchStrategy::Regex(regexes.regex_set()?),
|
||||
],
|
||||
})
|
||||
@ -501,11 +498,7 @@ impl<'a> Candidate<'a> {
|
||||
let path = normalize_path(Vec::from_path_lossy(path.as_ref()));
|
||||
let basename = file_name(&path).unwrap_or(Cow::Borrowed(B("")));
|
||||
let ext = file_name_ext(&basename).unwrap_or(Cow::Borrowed(B("")));
|
||||
Candidate {
|
||||
path: path,
|
||||
basename: basename,
|
||||
ext: ext,
|
||||
}
|
||||
Candidate { path: path, basename: basename, ext: ext }
|
||||
}
|
||||
|
||||
fn path_prefix(&self, max: usize) -> &[u8] {
|
||||
@ -767,11 +760,7 @@ struct MultiStrategyBuilder {
|
||||
|
||||
impl MultiStrategyBuilder {
|
||||
fn new() -> MultiStrategyBuilder {
|
||||
MultiStrategyBuilder {
|
||||
literals: vec![],
|
||||
map: vec![],
|
||||
longest: 0,
|
||||
}
|
||||
MultiStrategyBuilder { literals: vec![], map: vec![], longest: 0 }
|
||||
}
|
||||
|
||||
fn add(&mut self, global_index: usize, literal: String) {
|
||||
|
@ -84,7 +84,7 @@ pub fn normalize_path(mut path: Cow<[u8]>) -> Cow<[u8]> {
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use bstr::{B, ByteVec};
|
||||
use bstr::{ByteVec, B};
|
||||
|
||||
use super::{file_name_ext, normalize_path};
|
||||
|
||||
|
@ -38,10 +38,7 @@ impl Default for DecompressionMatcherBuilder {
|
||||
impl DecompressionMatcherBuilder {
|
||||
/// Create a new builder for configuring a decompression matcher.
|
||||
pub fn new() -> DecompressionMatcherBuilder {
|
||||
DecompressionMatcherBuilder {
|
||||
commands: vec![],
|
||||
defaults: true,
|
||||
}
|
||||
DecompressionMatcherBuilder { commands: vec![], defaults: true }
|
||||
}
|
||||
|
||||
/// Build a matcher for determining how to decompress files.
|
||||
@ -49,12 +46,11 @@ impl DecompressionMatcherBuilder {
|
||||
/// If there was a problem compiling the matcher, then an error is
|
||||
/// returned.
|
||||
pub fn build(&self) -> Result<DecompressionMatcher, CommandError> {
|
||||
let defaults =
|
||||
if !self.defaults {
|
||||
vec![]
|
||||
} else {
|
||||
default_decompression_commands()
|
||||
};
|
||||
let defaults = if !self.defaults {
|
||||
vec![]
|
||||
} else {
|
||||
default_decompression_commands()
|
||||
};
|
||||
let mut glob_builder = GlobSetBuilder::new();
|
||||
let mut commands = vec![];
|
||||
for decomp_cmd in defaults.iter().chain(&self.commands) {
|
||||
@ -93,17 +89,15 @@ impl DecompressionMatcherBuilder {
|
||||
program: P,
|
||||
args: I,
|
||||
) -> &mut DecompressionMatcherBuilder
|
||||
where P: AsRef<OsStr>,
|
||||
I: IntoIterator<Item=A>,
|
||||
A: AsRef<OsStr>,
|
||||
where
|
||||
P: AsRef<OsStr>,
|
||||
I: IntoIterator<Item = A>,
|
||||
A: AsRef<OsStr>,
|
||||
{
|
||||
|
||||
let glob = glob.to_string();
|
||||
let bin = program.as_ref().to_os_string();
|
||||
let args = args
|
||||
.into_iter()
|
||||
.map(|a| a.as_ref().to_os_string())
|
||||
.collect();
|
||||
let args =
|
||||
args.into_iter().map(|a| a.as_ref().to_os_string()).collect();
|
||||
self.commands.push(DecompressionCommand { glob, bin, args });
|
||||
self
|
||||
}
|
||||
|
@ -95,51 +95,61 @@ pub fn unescape(s: &str) -> Vec<u8> {
|
||||
let mut state = Literal;
|
||||
for c in s.chars() {
|
||||
match state {
|
||||
Escape => {
|
||||
match c {
|
||||
'\\' => { bytes.push(b'\\'); state = Literal; }
|
||||
'n' => { bytes.push(b'\n'); state = Literal; }
|
||||
'r' => { bytes.push(b'\r'); state = Literal; }
|
||||
't' => { bytes.push(b'\t'); state = Literal; }
|
||||
'x' => { state = HexFirst; }
|
||||
c => {
|
||||
bytes.extend(format!(r"\{}", c).into_bytes());
|
||||
state = Literal;
|
||||
}
|
||||
Escape => match c {
|
||||
'\\' => {
|
||||
bytes.push(b'\\');
|
||||
state = Literal;
|
||||
}
|
||||
}
|
||||
HexFirst => {
|
||||
match c {
|
||||
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
||||
state = HexSecond(c);
|
||||
}
|
||||
c => {
|
||||
bytes.extend(format!(r"\x{}", c).into_bytes());
|
||||
state = Literal;
|
||||
}
|
||||
'n' => {
|
||||
bytes.push(b'\n');
|
||||
state = Literal;
|
||||
}
|
||||
}
|
||||
HexSecond(first) => {
|
||||
match c {
|
||||
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
||||
let ordinal = format!("{}{}", first, c);
|
||||
let byte = u8::from_str_radix(&ordinal, 16).unwrap();
|
||||
bytes.push(byte);
|
||||
state = Literal;
|
||||
}
|
||||
c => {
|
||||
let original = format!(r"\x{}{}", first, c);
|
||||
bytes.extend(original.into_bytes());
|
||||
state = Literal;
|
||||
}
|
||||
'r' => {
|
||||
bytes.push(b'\r');
|
||||
state = Literal;
|
||||
}
|
||||
}
|
||||
Literal => {
|
||||
match c {
|
||||
'\\' => { state = Escape; }
|
||||
c => { bytes.extend(c.to_string().as_bytes()); }
|
||||
't' => {
|
||||
bytes.push(b'\t');
|
||||
state = Literal;
|
||||
}
|
||||
}
|
||||
'x' => {
|
||||
state = HexFirst;
|
||||
}
|
||||
c => {
|
||||
bytes.extend(format!(r"\{}", c).into_bytes());
|
||||
state = Literal;
|
||||
}
|
||||
},
|
||||
HexFirst => match c {
|
||||
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
||||
state = HexSecond(c);
|
||||
}
|
||||
c => {
|
||||
bytes.extend(format!(r"\x{}", c).into_bytes());
|
||||
state = Literal;
|
||||
}
|
||||
},
|
||||
HexSecond(first) => match c {
|
||||
'0'..='9' | 'A'..='F' | 'a'..='f' => {
|
||||
let ordinal = format!("{}{}", first, c);
|
||||
let byte = u8::from_str_radix(&ordinal, 16).unwrap();
|
||||
bytes.push(byte);
|
||||
state = Literal;
|
||||
}
|
||||
c => {
|
||||
let original = format!(r"\x{}{}", first, c);
|
||||
bytes.extend(original.into_bytes());
|
||||
state = Literal;
|
||||
}
|
||||
},
|
||||
Literal => match c {
|
||||
'\\' => {
|
||||
state = Escape;
|
||||
}
|
||||
c => {
|
||||
bytes.extend(c.to_string().as_bytes());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
match state {
|
||||
|
@ -46,7 +46,9 @@ impl ParseSizeError {
|
||||
}
|
||||
|
||||
impl error::Error for ParseSizeError {
|
||||
fn description(&self) -> &str { "invalid size" }
|
||||
fn description(&self) -> &str {
|
||||
"invalid size"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseSizeError {
|
||||
@ -54,26 +56,19 @@ impl fmt::Display for ParseSizeError {
|
||||
use self::ParseSizeErrorKind::*;
|
||||
|
||||
match self.kind {
|
||||
InvalidFormat => {
|
||||
write!(
|
||||
f,
|
||||
"invalid format for size '{}', which should be a sequence \
|
||||
InvalidFormat => write!(
|
||||
f,
|
||||
"invalid format for size '{}', which should be a sequence \
|
||||
of digits followed by an optional 'K', 'M' or 'G' \
|
||||
suffix",
|
||||
self.original
|
||||
)
|
||||
}
|
||||
InvalidInt(ref err) => {
|
||||
write!(
|
||||
f,
|
||||
"invalid integer found in size '{}': {}",
|
||||
self.original,
|
||||
err
|
||||
)
|
||||
}
|
||||
Overflow => {
|
||||
write!(f, "size too big in '{}'", self.original)
|
||||
}
|
||||
self.original
|
||||
),
|
||||
InvalidInt(ref err) => write!(
|
||||
f,
|
||||
"invalid integer found in size '{}': {}",
|
||||
self.original, err
|
||||
),
|
||||
Overflow => write!(f, "size too big in '{}'", self.original),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,17 +99,16 @@ pub fn parse_human_readable_size(size: &str) -> Result<u64, ParseSizeError> {
|
||||
Some(caps) => caps,
|
||||
None => return Err(ParseSizeError::format(size)),
|
||||
};
|
||||
let value: u64 = caps[1].parse().map_err(|err| {
|
||||
ParseSizeError::int(size, err)
|
||||
})?;
|
||||
let value: u64 =
|
||||
caps[1].parse().map_err(|err| ParseSizeError::int(size, err))?;
|
||||
let suffix = match caps.get(2) {
|
||||
None => return Ok(value),
|
||||
Some(cap) => cap.as_str(),
|
||||
};
|
||||
let bytes = match suffix {
|
||||
"K" => value.checked_mul(1<<10),
|
||||
"M" => value.checked_mul(1<<20),
|
||||
"G" => value.checked_mul(1<<30),
|
||||
"K" => value.checked_mul(1 << 10),
|
||||
"M" => value.checked_mul(1 << 20),
|
||||
"G" => value.checked_mul(1 << 30),
|
||||
// Because if the regex matches this group, it must be [KMG].
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@ -134,19 +128,19 @@ mod tests {
|
||||
#[test]
|
||||
fn suffix_k() {
|
||||
let x = parse_human_readable_size("123K").unwrap();
|
||||
assert_eq!(123 * (1<<10), x);
|
||||
assert_eq!(123 * (1 << 10), x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suffix_m() {
|
||||
let x = parse_human_readable_size("123M").unwrap();
|
||||
assert_eq!(123 * (1<<20), x);
|
||||
assert_eq!(123 * (1 << 20), x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suffix_g() {
|
||||
let x = parse_human_readable_size("123G").unwrap();
|
||||
assert_eq!(123 * (1<<30), x);
|
||||
assert_eq!(123 * (1 << 30), x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -179,20 +179,18 @@ mod process;
|
||||
mod wtr;
|
||||
|
||||
pub use decompress::{
|
||||
DecompressionMatcher, DecompressionMatcherBuilder,
|
||||
DecompressionReader, DecompressionReaderBuilder,
|
||||
DecompressionMatcher, DecompressionMatcherBuilder, DecompressionReader,
|
||||
DecompressionReaderBuilder,
|
||||
};
|
||||
pub use escape::{escape, escape_os, unescape, unescape_os};
|
||||
pub use human::{ParseSizeError, parse_human_readable_size};
|
||||
pub use human::{parse_human_readable_size, ParseSizeError};
|
||||
pub use pattern::{
|
||||
InvalidPatternError,
|
||||
pattern_from_os, pattern_from_bytes,
|
||||
patterns_from_path, patterns_from_reader, patterns_from_stdin,
|
||||
pattern_from_bytes, pattern_from_os, patterns_from_path,
|
||||
patterns_from_reader, patterns_from_stdin, InvalidPatternError,
|
||||
};
|
||||
pub use process::{CommandError, CommandReader, CommandReaderBuilder};
|
||||
pub use wtr::{
|
||||
StandardStream,
|
||||
stdout, stdout_buffered_line, stdout_buffered_block,
|
||||
stdout, stdout_buffered_block, stdout_buffered_line, StandardStream,
|
||||
};
|
||||
|
||||
/// Returns true if and only if stdin is believed to be readable.
|
||||
@ -205,8 +203,8 @@ pub use wtr::{
|
||||
pub fn is_readable_stdin() -> bool {
|
||||
#[cfg(unix)]
|
||||
fn imp() -> bool {
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
use same_file::Handle;
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
|
||||
let ft = match Handle::stdin().and_then(|h| h.as_file().metadata()) {
|
||||
Err(_) => return false,
|
||||
|
@ -29,7 +29,9 @@ impl InvalidPatternError {
|
||||
}
|
||||
|
||||
impl error::Error for InvalidPatternError {
|
||||
fn description(&self) -> &str { "invalid pattern" }
|
||||
fn description(&self) -> &str {
|
||||
"invalid pattern"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidPatternError {
|
||||
@ -39,8 +41,7 @@ impl fmt::Display for InvalidPatternError {
|
||||
"found invalid UTF-8 in pattern at byte offset {} \
|
||||
(use hex escape sequences to match arbitrary bytes \
|
||||
in a pattern, e.g., \\xFF): '{}'",
|
||||
self.valid_up_to,
|
||||
self.original,
|
||||
self.valid_up_to, self.original,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -79,11 +80,9 @@ pub fn pattern_from_os(pattern: &OsStr) -> Result<&str, InvalidPatternError> {
|
||||
pub fn pattern_from_bytes(
|
||||
pattern: &[u8],
|
||||
) -> Result<&str, InvalidPatternError> {
|
||||
str::from_utf8(pattern).map_err(|err| {
|
||||
InvalidPatternError {
|
||||
original: escape(pattern),
|
||||
valid_up_to: err.valid_up_to(),
|
||||
}
|
||||
str::from_utf8(pattern).map_err(|err| InvalidPatternError {
|
||||
original: escape(pattern),
|
||||
valid_up_to: err.valid_up_to(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -119,10 +118,7 @@ pub fn patterns_from_stdin() -> io::Result<Vec<String>> {
|
||||
let stdin = io::stdin();
|
||||
let locked = stdin.lock();
|
||||
patterns_from_reader(locked).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("<stdin>:{}", err),
|
||||
)
|
||||
io::Error::new(io::ErrorKind::Other, format!("<stdin>:{}", err))
|
||||
})
|
||||
}
|
||||
|
||||
@ -166,12 +162,10 @@ pub fn patterns_from_reader<R: io::Read>(rdr: R) -> io::Result<Vec<String>> {
|
||||
patterns.push(pattern.to_string());
|
||||
Ok(true)
|
||||
}
|
||||
Err(err) => {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("{}: {}", line_number, err),
|
||||
))
|
||||
}
|
||||
Err(err) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("{}: {}", line_number, err),
|
||||
)),
|
||||
}
|
||||
})?;
|
||||
Ok(patterns)
|
||||
@ -191,8 +185,8 @@ mod tests {
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn os() {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
let pat = OsStr::from_bytes(b"abc\xFFxyz");
|
||||
let err = pattern_from_os(pat).unwrap_err();
|
||||
|
@ -33,7 +33,9 @@ impl CommandError {
|
||||
}
|
||||
|
||||
impl error::Error for CommandError {
|
||||
fn description(&self) -> &str { "command error" }
|
||||
fn description(&self) -> &str {
|
||||
"command error"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CommandError {
|
||||
@ -46,7 +48,12 @@ impl fmt::Display for CommandError {
|
||||
write!(f, "<stderr is empty>")
|
||||
} else {
|
||||
let div = iter::repeat('-').take(79).collect::<String>();
|
||||
write!(f, "\n{div}\n{msg}\n{div}", div=div, msg=msg.trim())
|
||||
write!(
|
||||
f,
|
||||
"\n{div}\n{msg}\n{div}",
|
||||
div = div,
|
||||
msg = msg.trim()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,12 +108,11 @@ impl CommandReaderBuilder {
|
||||
.stderr(process::Stdio::piped())
|
||||
.spawn()?;
|
||||
let stdout = child.stdout.take().unwrap();
|
||||
let stderr =
|
||||
if self.async_stderr {
|
||||
StderrReader::async(child.stderr.take().unwrap())
|
||||
} else {
|
||||
StderrReader::sync(child.stderr.take().unwrap())
|
||||
};
|
||||
let stderr = if self.async_stderr {
|
||||
StderrReader::async(child.stderr.take().unwrap())
|
||||
} else {
|
||||
StderrReader::sync(child.stderr.take().unwrap())
|
||||
};
|
||||
Ok(CommandReader {
|
||||
child: child,
|
||||
stdout: stdout,
|
||||
@ -226,9 +232,8 @@ enum StderrReader {
|
||||
impl StderrReader {
|
||||
/// Create a reader for stderr that reads contents asynchronously.
|
||||
fn async(mut stderr: process::ChildStderr) -> StderrReader {
|
||||
let handle = thread::spawn(move || {
|
||||
stderr_to_command_error(&mut stderr)
|
||||
});
|
||||
let handle =
|
||||
thread::spawn(move || stderr_to_command_error(&mut stderr));
|
||||
StderrReader::Async(Some(handle))
|
||||
}
|
||||
|
||||
@ -247,9 +252,7 @@ impl StderrReader {
|
||||
let handle = handle
|
||||
.take()
|
||||
.expect("read_to_end cannot be called more than once");
|
||||
handle
|
||||
.join()
|
||||
.expect("stderr reading thread does not panic")
|
||||
handle.join().expect("stderr reading thread does not panic")
|
||||
}
|
||||
StderrReader::Sync(ref mut stderr) => {
|
||||
stderr_to_command_error(stderr)
|
||||
|
@ -19,7 +19,7 @@ pub fn interpolate<A, N>(
|
||||
dst: &mut Vec<u8>,
|
||||
) where
|
||||
A: FnMut(usize, &mut Vec<u8>),
|
||||
N: FnMut(&str) -> Option<usize>
|
||||
N: FnMut(&str) -> Option<usize>,
|
||||
{
|
||||
while !replacement.is_empty() {
|
||||
match memchr(b'$', replacement) {
|
||||
@ -134,14 +134,14 @@ fn find_cap_ref(replacement: &[u8]) -> Option<CaptureRef> {
|
||||
/// Returns true if and only if the given byte is allowed in a capture name.
|
||||
fn is_valid_cap_letter(b: &u8) -> bool {
|
||||
match *b {
|
||||
b'0' ..= b'9' | b'a' ..= b'z' | b'A' ..= b'Z' | b'_' => true,
|
||||
b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'_' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{CaptureRef, find_cap_ref, interpolate};
|
||||
use super::{find_cap_ref, interpolate, CaptureRef};
|
||||
|
||||
macro_rules! find {
|
||||
($name:ident, $text:expr) => {
|
||||
@ -211,7 +211,7 @@ mod tests {
|
||||
fn $name() {
|
||||
assert_eq!($expected, interpolate_string($map, $caps, $hay));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interp!(
|
||||
|
@ -278,7 +278,7 @@ impl LineTerminator {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LineTerminator {
|
||||
impl Default for LineTerminator {
|
||||
#[inline]
|
||||
fn default() -> LineTerminator {
|
||||
LineTerminator::byte(b'\n')
|
||||
@ -439,7 +439,8 @@ pub trait Captures {
|
||||
haystack: &[u8],
|
||||
replacement: &[u8],
|
||||
dst: &mut Vec<u8>,
|
||||
) where F: FnMut(&str) -> Option<usize>
|
||||
) where
|
||||
F: FnMut(&str) -> Option<usize>,
|
||||
{
|
||||
interpolate(
|
||||
replacement,
|
||||
@ -463,12 +464,18 @@ pub struct NoCaptures(());
|
||||
|
||||
impl NoCaptures {
|
||||
/// Create an empty set of capturing groups.
|
||||
pub fn new() -> NoCaptures { NoCaptures(()) }
|
||||
pub fn new() -> NoCaptures {
|
||||
NoCaptures(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Captures for NoCaptures {
|
||||
fn len(&self) -> usize { 0 }
|
||||
fn get(&self, _: usize) -> Option<Match> { None }
|
||||
fn len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
fn get(&self, _: usize) -> Option<Match> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// NoError provides an error type for matchers that never produce errors.
|
||||
@ -481,7 +488,9 @@ impl Captures for NoCaptures {
|
||||
pub struct NoError(());
|
||||
|
||||
impl ::std::error::Error for NoError {
|
||||
fn description(&self) -> &str { "no error" }
|
||||
fn description(&self) -> &str {
|
||||
"no error"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NoError {
|
||||
@ -599,10 +608,7 @@ pub trait Matcher {
|
||||
///
|
||||
/// The text encoding of `haystack` is not strictly specified. Matchers are
|
||||
/// advised to assume UTF-8, or at worst, some ASCII compatible encoding.
|
||||
fn find(
|
||||
&self,
|
||||
haystack: &[u8],
|
||||
) -> Result<Option<Match>, Self::Error> {
|
||||
fn find(&self, haystack: &[u8]) -> Result<Option<Match>, Self::Error> {
|
||||
self.find_at(haystack, 0)
|
||||
}
|
||||
|
||||
@ -614,7 +620,8 @@ pub trait Matcher {
|
||||
haystack: &[u8],
|
||||
mut matched: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(Match) -> bool
|
||||
where
|
||||
F: FnMut(Match) -> bool,
|
||||
{
|
||||
self.try_find_iter(haystack, |m| Ok(matched(m)))
|
||||
.map(|r: Result<(), ()>| r.unwrap())
|
||||
@ -632,7 +639,8 @@ pub trait Matcher {
|
||||
haystack: &[u8],
|
||||
mut matched: F,
|
||||
) -> Result<Result<(), E>, Self::Error>
|
||||
where F: FnMut(Match) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(Match) -> Result<bool, E>,
|
||||
{
|
||||
let mut last_end = 0;
|
||||
let mut last_match = None;
|
||||
@ -690,7 +698,8 @@ pub trait Matcher {
|
||||
caps: &mut Self::Captures,
|
||||
mut matched: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(&Self::Captures) -> bool
|
||||
where
|
||||
F: FnMut(&Self::Captures) -> bool,
|
||||
{
|
||||
self.try_captures_iter(haystack, caps, |caps| Ok(matched(caps)))
|
||||
.map(|r: Result<(), ()>| r.unwrap())
|
||||
@ -709,7 +718,8 @@ pub trait Matcher {
|
||||
caps: &mut Self::Captures,
|
||||
mut matched: F,
|
||||
) -> Result<Result<(), E>, Self::Error>
|
||||
where F: FnMut(&Self::Captures) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(&Self::Captures) -> Result<bool, E>,
|
||||
{
|
||||
let mut last_end = 0;
|
||||
let mut last_match = None;
|
||||
@ -787,7 +797,8 @@ pub trait Matcher {
|
||||
dst: &mut Vec<u8>,
|
||||
mut append: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(Match, &mut Vec<u8>) -> bool
|
||||
where
|
||||
F: FnMut(Match, &mut Vec<u8>) -> bool,
|
||||
{
|
||||
let mut last_match = 0;
|
||||
self.find_iter(haystack, |m| {
|
||||
@ -810,7 +821,8 @@ pub trait Matcher {
|
||||
dst: &mut Vec<u8>,
|
||||
mut append: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool
|
||||
where
|
||||
F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool,
|
||||
{
|
||||
let mut last_match = 0;
|
||||
self.captures_iter(haystack, caps, |caps| {
|
||||
@ -1012,10 +1024,7 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
(*self).capture_count()
|
||||
}
|
||||
|
||||
fn find(
|
||||
&self,
|
||||
haystack: &[u8]
|
||||
) -> Result<Option<Match>, Self::Error> {
|
||||
fn find(&self, haystack: &[u8]) -> Result<Option<Match>, Self::Error> {
|
||||
(*self).find(haystack)
|
||||
}
|
||||
|
||||
@ -1024,7 +1033,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
haystack: &[u8],
|
||||
matched: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(Match) -> bool
|
||||
where
|
||||
F: FnMut(Match) -> bool,
|
||||
{
|
||||
(*self).find_iter(haystack, matched)
|
||||
}
|
||||
@ -1034,7 +1044,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
haystack: &[u8],
|
||||
matched: F,
|
||||
) -> Result<Result<(), E>, Self::Error>
|
||||
where F: FnMut(Match) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(Match) -> Result<bool, E>,
|
||||
{
|
||||
(*self).try_find_iter(haystack, matched)
|
||||
}
|
||||
@ -1053,7 +1064,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
caps: &mut Self::Captures,
|
||||
matched: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(&Self::Captures) -> bool
|
||||
where
|
||||
F: FnMut(&Self::Captures) -> bool,
|
||||
{
|
||||
(*self).captures_iter(haystack, caps, matched)
|
||||
}
|
||||
@ -1064,7 +1076,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
caps: &mut Self::Captures,
|
||||
matched: F,
|
||||
) -> Result<Result<(), E>, Self::Error>
|
||||
where F: FnMut(&Self::Captures) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(&Self::Captures) -> Result<bool, E>,
|
||||
{
|
||||
(*self).try_captures_iter(haystack, caps, matched)
|
||||
}
|
||||
@ -1075,7 +1088,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
dst: &mut Vec<u8>,
|
||||
append: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(Match, &mut Vec<u8>) -> bool
|
||||
where
|
||||
F: FnMut(Match, &mut Vec<u8>) -> bool,
|
||||
{
|
||||
(*self).replace(haystack, dst, append)
|
||||
}
|
||||
@ -1087,7 +1101,8 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
dst: &mut Vec<u8>,
|
||||
append: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool
|
||||
where
|
||||
F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool,
|
||||
{
|
||||
(*self).replace_with_captures(haystack, caps, dst, append)
|
||||
}
|
||||
@ -1099,7 +1114,7 @@ impl<'a, M: Matcher> Matcher for &'a M {
|
||||
fn is_match_at(
|
||||
&self,
|
||||
haystack: &[u8],
|
||||
at: usize
|
||||
at: usize,
|
||||
) -> Result<bool, Self::Error> {
|
||||
(*self).is_match_at(haystack, at)
|
||||
}
|
||||
|
@ -25,18 +25,22 @@ fn find() {
|
||||
fn find_iter() {
|
||||
let matcher = matcher(r"(\w+)\s+(\w+)");
|
||||
let mut matches = vec![];
|
||||
matcher.find_iter(b"aa bb cc dd", |m| {
|
||||
matches.push(m);
|
||||
true
|
||||
}).unwrap();
|
||||
matcher
|
||||
.find_iter(b"aa bb cc dd", |m| {
|
||||
matches.push(m);
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(matches, vec![m(0, 5), m(6, 11)]);
|
||||
|
||||
// Test that find_iter respects short circuiting.
|
||||
matches.clear();
|
||||
matcher.find_iter(b"aa bb cc dd", |m| {
|
||||
matches.push(m);
|
||||
false
|
||||
}).unwrap();
|
||||
matcher
|
||||
.find_iter(b"aa bb cc dd", |m| {
|
||||
matches.push(m);
|
||||
false
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(matches, vec![m(0, 5)]);
|
||||
}
|
||||
|
||||
@ -47,14 +51,17 @@ fn try_find_iter() {
|
||||
|
||||
let matcher = matcher(r"(\w+)\s+(\w+)");
|
||||
let mut matches = vec![];
|
||||
let err = matcher.try_find_iter(b"aa bb cc dd", |m| {
|
||||
if matches.is_empty() {
|
||||
matches.push(m);
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(MyError)
|
||||
}
|
||||
}).unwrap().unwrap_err();
|
||||
let err = matcher
|
||||
.try_find_iter(b"aa bb cc dd", |m| {
|
||||
if matches.is_empty() {
|
||||
matches.push(m);
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(MyError)
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.unwrap_err();
|
||||
assert_eq!(matches, vec![m(0, 5)]);
|
||||
assert_eq!(err, MyError);
|
||||
}
|
||||
@ -89,28 +96,30 @@ fn captures_iter() {
|
||||
let matcher = matcher(r"(?P<a>\w+)\s+(?P<b>\w+)");
|
||||
let mut caps = matcher.new_captures().unwrap();
|
||||
let mut matches = vec![];
|
||||
matcher.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||
matches.push(caps.get(0).unwrap());
|
||||
matches.push(caps.get(1).unwrap());
|
||||
matches.push(caps.get(2).unwrap());
|
||||
true
|
||||
}).unwrap();
|
||||
assert_eq!(matches, vec![
|
||||
m(0, 5), m(0, 2), m(3, 5),
|
||||
m(6, 11), m(6, 8), m(9, 11),
|
||||
]);
|
||||
matcher
|
||||
.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||
matches.push(caps.get(0).unwrap());
|
||||
matches.push(caps.get(1).unwrap());
|
||||
matches.push(caps.get(2).unwrap());
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
matches,
|
||||
vec![m(0, 5), m(0, 2), m(3, 5), m(6, 11), m(6, 8), m(9, 11),]
|
||||
);
|
||||
|
||||
// Test that captures_iter respects short circuiting.
|
||||
matches.clear();
|
||||
matcher.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||
matches.push(caps.get(0).unwrap());
|
||||
matches.push(caps.get(1).unwrap());
|
||||
matches.push(caps.get(2).unwrap());
|
||||
false
|
||||
}).unwrap();
|
||||
assert_eq!(matches, vec![
|
||||
m(0, 5), m(0, 2), m(3, 5),
|
||||
]);
|
||||
matcher
|
||||
.captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||
matches.push(caps.get(0).unwrap());
|
||||
matches.push(caps.get(1).unwrap());
|
||||
matches.push(caps.get(2).unwrap());
|
||||
false
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(matches, vec![m(0, 5), m(0, 2), m(3, 5),]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -121,16 +130,19 @@ fn try_captures_iter() {
|
||||
let matcher = matcher(r"(?P<a>\w+)\s+(?P<b>\w+)");
|
||||
let mut caps = matcher.new_captures().unwrap();
|
||||
let mut matches = vec![];
|
||||
let err = matcher.try_captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||
if matches.is_empty() {
|
||||
matches.push(caps.get(0).unwrap());
|
||||
matches.push(caps.get(1).unwrap());
|
||||
matches.push(caps.get(2).unwrap());
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(MyError)
|
||||
}
|
||||
}).unwrap().unwrap_err();
|
||||
let err = matcher
|
||||
.try_captures_iter(b"aa bb cc dd", &mut caps, |caps| {
|
||||
if matches.is_empty() {
|
||||
matches.push(caps.get(0).unwrap());
|
||||
matches.push(caps.get(1).unwrap());
|
||||
matches.push(caps.get(2).unwrap());
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(MyError)
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.unwrap_err();
|
||||
assert_eq!(matches, vec![m(0, 5), m(0, 2), m(3, 5)]);
|
||||
assert_eq!(err, MyError);
|
||||
}
|
||||
@ -150,10 +162,12 @@ fn no_captures() {
|
||||
assert!(!matcher.captures(b"homer simpson", &mut caps).unwrap());
|
||||
|
||||
let mut called = false;
|
||||
matcher.captures_iter(b"homer simpson", &mut caps, |_| {
|
||||
called = true;
|
||||
true
|
||||
}).unwrap();
|
||||
matcher
|
||||
.captures_iter(b"homer simpson", &mut caps, |_| {
|
||||
called = true;
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
assert!(!called);
|
||||
}
|
||||
|
||||
@ -161,18 +175,22 @@ fn no_captures() {
|
||||
fn replace() {
|
||||
let matcher = matcher(r"(\w+)\s+(\w+)");
|
||||
let mut dst = vec![];
|
||||
matcher.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
||||
dst.push(b'z');
|
||||
true
|
||||
}).unwrap();
|
||||
matcher
|
||||
.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
||||
dst.push(b'z');
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(dst, b"z z");
|
||||
|
||||
// Test that replacements respect short circuiting.
|
||||
dst.clear();
|
||||
matcher.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
||||
dst.push(b'z');
|
||||
false
|
||||
}).unwrap();
|
||||
matcher
|
||||
.replace(b"aa bb cc dd", &mut dst, |_, dst| {
|
||||
dst.push(b'z');
|
||||
false
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(dst, b"z cc dd");
|
||||
}
|
||||
|
||||
@ -182,27 +200,31 @@ fn replace_with_captures() {
|
||||
let haystack = b"aa bb cc dd";
|
||||
let mut caps = matcher.new_captures().unwrap();
|
||||
let mut dst = vec![];
|
||||
matcher.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
||||
caps.interpolate(
|
||||
|name| matcher.capture_index(name),
|
||||
haystack,
|
||||
b"$2 $1",
|
||||
dst,
|
||||
);
|
||||
true
|
||||
}).unwrap();
|
||||
matcher
|
||||
.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
||||
caps.interpolate(
|
||||
|name| matcher.capture_index(name),
|
||||
haystack,
|
||||
b"$2 $1",
|
||||
dst,
|
||||
);
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(dst, b"bb aa dd cc");
|
||||
|
||||
// Test that replacements respect short circuiting.
|
||||
dst.clear();
|
||||
matcher.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
||||
caps.interpolate(
|
||||
|name| matcher.capture_index(name),
|
||||
haystack,
|
||||
b"$2 $1",
|
||||
dst,
|
||||
);
|
||||
false
|
||||
}).unwrap();
|
||||
matcher
|
||||
.replace_with_captures(haystack, &mut caps, &mut dst, |caps, dst| {
|
||||
caps.interpolate(
|
||||
|name| matcher.capture_index(name),
|
||||
haystack,
|
||||
b"$2 $1",
|
||||
dst,
|
||||
);
|
||||
false
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(dst, b"bb aa cc dd");
|
||||
}
|
||||
|
@ -18,10 +18,7 @@ impl RegexMatcher {
|
||||
names.insert(name.to_string(), i);
|
||||
}
|
||||
}
|
||||
RegexMatcher {
|
||||
re: re,
|
||||
names: names,
|
||||
}
|
||||
RegexMatcher { re: re, names: names }
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,12 +28,9 @@ impl Matcher for RegexMatcher {
|
||||
type Captures = RegexCaptures;
|
||||
type Error = NoError;
|
||||
|
||||
fn find_at(
|
||||
&self,
|
||||
haystack: &[u8],
|
||||
at: usize,
|
||||
) -> Result<Option<Match>> {
|
||||
Ok(self.re
|
||||
fn find_at(&self, haystack: &[u8], at: usize) -> Result<Option<Match>> {
|
||||
Ok(self
|
||||
.re
|
||||
.find_at(haystack, at)
|
||||
.map(|m| Match::new(m.start(), m.end())))
|
||||
}
|
||||
@ -75,12 +69,9 @@ impl Matcher for RegexMatcherNoCaps {
|
||||
type Captures = NoCaptures;
|
||||
type Error = NoError;
|
||||
|
||||
fn find_at(
|
||||
&self,
|
||||
haystack: &[u8],
|
||||
at: usize,
|
||||
) -> Result<Option<Match>> {
|
||||
Ok(self.0
|
||||
fn find_at(&self, haystack: &[u8], at: usize) -> Result<Option<Match>> {
|
||||
Ok(self
|
||||
.0
|
||||
.find_at(haystack, at)
|
||||
.map(|m| Match::new(m.start(), m.end())))
|
||||
}
|
||||
|
@ -33,13 +33,12 @@ impl RegexMatcherBuilder {
|
||||
if self.case_smart && !has_uppercase_literal(pattern) {
|
||||
builder.caseless(true);
|
||||
}
|
||||
let res =
|
||||
if self.word {
|
||||
let pattern = format!(r"(?<!\w)(?:{})(?!\w)", pattern);
|
||||
builder.build(&pattern)
|
||||
} else {
|
||||
builder.build(pattern)
|
||||
};
|
||||
let res = if self.word {
|
||||
let pattern = format!(r"(?<!\w)(?:{})(?!\w)", pattern);
|
||||
builder.build(&pattern)
|
||||
} else {
|
||||
builder.build(pattern)
|
||||
};
|
||||
res.map_err(Error::regex).map(|regex| {
|
||||
let mut names = HashMap::new();
|
||||
for (i, name) in regex.capture_names().iter().enumerate() {
|
||||
@ -274,7 +273,8 @@ impl Matcher for RegexMatcher {
|
||||
haystack: &[u8],
|
||||
at: usize,
|
||||
) -> Result<Option<Match>, Error> {
|
||||
Ok(self.regex
|
||||
Ok(self
|
||||
.regex
|
||||
.find_at(haystack, at)
|
||||
.map_err(Error::regex)?
|
||||
.map(|m| Match::new(m.start(), m.end())))
|
||||
@ -297,7 +297,8 @@ impl Matcher for RegexMatcher {
|
||||
haystack: &[u8],
|
||||
mut matched: F,
|
||||
) -> Result<Result<(), E>, Error>
|
||||
where F: FnMut(Match) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(Match) -> Result<bool, E>,
|
||||
{
|
||||
for result in self.regex.find_iter(haystack) {
|
||||
let m = result.map_err(Error::regex)?;
|
||||
@ -316,10 +317,11 @@ impl Matcher for RegexMatcher {
|
||||
at: usize,
|
||||
caps: &mut RegexCaptures,
|
||||
) -> Result<bool, Error> {
|
||||
Ok(self.regex
|
||||
.captures_read_at(&mut caps.locs, haystack, at)
|
||||
.map_err(Error::regex)?
|
||||
.is_some())
|
||||
Ok(self
|
||||
.regex
|
||||
.captures_read_at(&mut caps.locs, haystack, at)
|
||||
.map_err(Error::regex)?
|
||||
.is_some())
|
||||
}
|
||||
}
|
||||
|
||||
@ -383,23 +385,19 @@ fn has_uppercase_literal(pattern: &str) -> bool {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use grep_matcher::{LineMatchKind, Matcher};
|
||||
use super::*;
|
||||
use grep_matcher::{LineMatchKind, Matcher};
|
||||
|
||||
// Test that enabling word matches does the right thing and demonstrate
|
||||
// the difference between it and surrounding the regex in `\b`.
|
||||
#[test]
|
||||
fn word() {
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.word(true)
|
||||
.build(r"-2")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().word(true).build(r"-2").unwrap();
|
||||
assert!(matcher.is_match(b"abc -2 foo").unwrap());
|
||||
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.word(false)
|
||||
.build(r"\b-2\b")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().word(false).build(r"\b-2\b").unwrap();
|
||||
assert!(!matcher.is_match(b"abc -2 foo").unwrap());
|
||||
}
|
||||
|
||||
@ -432,16 +430,12 @@ mod tests {
|
||||
// Test that smart case works.
|
||||
#[test]
|
||||
fn case_smart() {
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.case_smart(true)
|
||||
.build(r"abc")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().case_smart(true).build(r"abc").unwrap();
|
||||
assert!(matcher.is_match(b"ABC").unwrap());
|
||||
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.case_smart(true)
|
||||
.build(r"aBc")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().case_smart(true).build(r"aBc").unwrap();
|
||||
assert!(!matcher.is_match(b"ABC").unwrap());
|
||||
}
|
||||
|
||||
@ -455,9 +449,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.build(r"\wfoo\s")
|
||||
.unwrap();
|
||||
let matcher = RegexMatcherBuilder::new().build(r"\wfoo\s").unwrap();
|
||||
let m = matcher.find_candidate_line(b"afoo ").unwrap().unwrap();
|
||||
assert!(is_confirmed(m));
|
||||
}
|
||||
|
@ -62,42 +62,32 @@ impl ColorError {
|
||||
impl fmt::Display for ColorError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ColorError::UnrecognizedOutType(ref name) => {
|
||||
write!(
|
||||
f,
|
||||
"unrecognized output type '{}'. Choose from: \
|
||||
ColorError::UnrecognizedOutType(ref name) => write!(
|
||||
f,
|
||||
"unrecognized output type '{}'. Choose from: \
|
||||
path, line, column, match.",
|
||||
name,
|
||||
)
|
||||
}
|
||||
ColorError::UnrecognizedSpecType(ref name) => {
|
||||
write!(
|
||||
f,
|
||||
"unrecognized spec type '{}'. Choose from: \
|
||||
name,
|
||||
),
|
||||
ColorError::UnrecognizedSpecType(ref name) => write!(
|
||||
f,
|
||||
"unrecognized spec type '{}'. Choose from: \
|
||||
fg, bg, style, none.",
|
||||
name,
|
||||
)
|
||||
}
|
||||
ColorError::UnrecognizedColor(_, ref msg) => {
|
||||
write!(f, "{}", msg)
|
||||
}
|
||||
ColorError::UnrecognizedStyle(ref name) => {
|
||||
write!(
|
||||
f,
|
||||
"unrecognized style attribute '{}'. Choose from: \
|
||||
name,
|
||||
),
|
||||
ColorError::UnrecognizedColor(_, ref msg) => write!(f, "{}", msg),
|
||||
ColorError::UnrecognizedStyle(ref name) => write!(
|
||||
f,
|
||||
"unrecognized style attribute '{}'. Choose from: \
|
||||
nobold, bold, nointense, intense, nounderline, \
|
||||
underline.",
|
||||
name,
|
||||
)
|
||||
}
|
||||
ColorError::InvalidFormat(ref original) => {
|
||||
write!(
|
||||
f,
|
||||
"invalid color spec format: '{}'. Valid format \
|
||||
name,
|
||||
),
|
||||
ColorError::InvalidFormat(ref original) => write!(
|
||||
f,
|
||||
"invalid color spec format: '{}'. Valid format \
|
||||
is '(path|line|column|match):(fg|bg|style):(value)'.",
|
||||
original,
|
||||
)
|
||||
}
|
||||
original,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,7 +217,7 @@ enum Style {
|
||||
Intense,
|
||||
NoIntense,
|
||||
Underline,
|
||||
NoUnderline
|
||||
NoUnderline,
|
||||
}
|
||||
|
||||
impl ColorSpecs {
|
||||
@ -288,18 +278,32 @@ impl SpecValue {
|
||||
fn merge_into(&self, cspec: &mut ColorSpec) {
|
||||
match *self {
|
||||
SpecValue::None => cspec.clear(),
|
||||
SpecValue::Fg(ref color) => { cspec.set_fg(Some(color.clone())); }
|
||||
SpecValue::Bg(ref color) => { cspec.set_bg(Some(color.clone())); }
|
||||
SpecValue::Style(ref style) => {
|
||||
match *style {
|
||||
Style::Bold => { cspec.set_bold(true); }
|
||||
Style::NoBold => { cspec.set_bold(false); }
|
||||
Style::Intense => { cspec.set_intense(true); }
|
||||
Style::NoIntense => { cspec.set_intense(false); }
|
||||
Style::Underline => { cspec.set_underline(true); }
|
||||
Style::NoUnderline => { cspec.set_underline(false); }
|
||||
}
|
||||
SpecValue::Fg(ref color) => {
|
||||
cspec.set_fg(Some(color.clone()));
|
||||
}
|
||||
SpecValue::Bg(ref color) => {
|
||||
cspec.set_bg(Some(color.clone()));
|
||||
}
|
||||
SpecValue::Style(ref style) => match *style {
|
||||
Style::Bold => {
|
||||
cspec.set_bold(true);
|
||||
}
|
||||
Style::NoBold => {
|
||||
cspec.set_bold(false);
|
||||
}
|
||||
Style::Intense => {
|
||||
cspec.set_intense(true);
|
||||
}
|
||||
Style::NoIntense => {
|
||||
cspec.set_intense(false);
|
||||
}
|
||||
Style::Underline => {
|
||||
cspec.set_underline(true);
|
||||
}
|
||||
Style::NoUnderline => {
|
||||
cspec.set_underline(false);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -315,10 +319,7 @@ impl FromStr for UserColorSpec {
|
||||
let otype: OutType = pieces[0].parse()?;
|
||||
match pieces[1].parse()? {
|
||||
SpecType::None => {
|
||||
Ok(UserColorSpec {
|
||||
ty: otype,
|
||||
value: SpecValue::None,
|
||||
})
|
||||
Ok(UserColorSpec { ty: otype, value: SpecValue::None })
|
||||
}
|
||||
SpecType::Style => {
|
||||
if pieces.len() < 3 {
|
||||
@ -331,18 +332,16 @@ impl FromStr for UserColorSpec {
|
||||
if pieces.len() < 3 {
|
||||
return Err(ColorError::InvalidFormat(s.to_string()));
|
||||
}
|
||||
let color: Color = pieces[2]
|
||||
.parse()
|
||||
.map_err(ColorError::from_parse_error)?;
|
||||
let color: Color =
|
||||
pieces[2].parse().map_err(ColorError::from_parse_error)?;
|
||||
Ok(UserColorSpec { ty: otype, value: SpecValue::Fg(color) })
|
||||
}
|
||||
SpecType::Bg => {
|
||||
if pieces.len() < 3 {
|
||||
return Err(ColorError::InvalidFormat(s.to_string()));
|
||||
}
|
||||
let color: Color = pieces[2]
|
||||
.parse()
|
||||
.map_err(ColorError::from_parse_error)?;
|
||||
let color: Color =
|
||||
pieces[2].parse().map_err(ColorError::from_parse_error)?;
|
||||
Ok(UserColorSpec { ty: otype, value: SpecValue::Bg(color) })
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ use std::time::Instant;
|
||||
|
||||
use grep_matcher::{Match, Matcher};
|
||||
use grep_searcher::{
|
||||
Searcher,
|
||||
Sink, SinkError, SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
||||
Searcher, Sink, SinkContext, SinkContextKind, SinkError, SinkFinish,
|
||||
SinkMatch,
|
||||
};
|
||||
use serde_json as json;
|
||||
|
||||
@ -27,11 +27,7 @@ struct Config {
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
pretty: false,
|
||||
max_matches: None,
|
||||
always_begin_end: false,
|
||||
}
|
||||
Config { pretty: false, max_matches: None, always_begin_end: false }
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,8 +488,9 @@ impl<W: io::Write> JSON<W> {
|
||||
matcher: M,
|
||||
path: &'p P,
|
||||
) -> JSONSink<'p, 's, M, W>
|
||||
where M: Matcher,
|
||||
P: ?Sized + AsRef<Path>,
|
||||
where
|
||||
M: Matcher,
|
||||
P: ?Sized + AsRef<Path>,
|
||||
{
|
||||
JSONSink {
|
||||
matcher: matcher,
|
||||
@ -615,10 +612,12 @@ impl<'p, 's, M: Matcher, W: io::Write> JSONSink<'p, 's, M, W> {
|
||||
// the extent that it's easy to ensure that we never do more than
|
||||
// one search to find the matches.
|
||||
let matches = &mut self.json.matches;
|
||||
self.matcher.find_iter(bytes, |m| {
|
||||
matches.push(m);
|
||||
true
|
||||
}).map_err(io::Error::error_message)?;
|
||||
self.matcher
|
||||
.find_iter(bytes, |m| {
|
||||
matches.push(m);
|
||||
true
|
||||
})
|
||||
.map_err(io::Error::error_message)?;
|
||||
// Don't report empty matches appearing at the end of the bytes.
|
||||
if !matches.is_empty()
|
||||
&& matches.last().unwrap().is_empty()
|
||||
@ -650,9 +649,7 @@ impl<'p, 's, M: Matcher, W: io::Write> JSONSink<'p, 's, M, W> {
|
||||
if self.begin_printed {
|
||||
return Ok(());
|
||||
}
|
||||
let msg = jsont::Message::Begin(jsont::Begin {
|
||||
path: self.path,
|
||||
});
|
||||
let msg = jsont::Message::Begin(jsont::Begin { path: self.path });
|
||||
self.json.write_message(&msg)?;
|
||||
self.begin_printed = true;
|
||||
Ok(())
|
||||
@ -699,13 +696,12 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
||||
self.after_context_remaining =
|
||||
self.after_context_remaining.saturating_sub(1);
|
||||
}
|
||||
let submatches =
|
||||
if searcher.invert_match() {
|
||||
self.record_matches(ctx.bytes())?;
|
||||
SubMatches::new(ctx.bytes(), &self.json.matches)
|
||||
} else {
|
||||
SubMatches::empty()
|
||||
};
|
||||
let submatches = if searcher.invert_match() {
|
||||
self.record_matches(ctx.bytes())?;
|
||||
SubMatches::new(ctx.bytes(), &self.json.matches)
|
||||
} else {
|
||||
SubMatches::empty()
|
||||
};
|
||||
let msg = jsont::Message::Context(jsont::Context {
|
||||
path: self.path,
|
||||
lines: ctx.bytes(),
|
||||
@ -717,10 +713,7 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
||||
Ok(!self.should_quit())
|
||||
}
|
||||
|
||||
fn begin(
|
||||
&mut self,
|
||||
_searcher: &Searcher,
|
||||
) -> Result<bool, io::Error> {
|
||||
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, io::Error> {
|
||||
self.json.wtr.reset_count();
|
||||
self.start_time = Instant::now();
|
||||
self.match_count = 0;
|
||||
@ -779,7 +772,7 @@ enum SubMatches<'a> {
|
||||
impl<'a> SubMatches<'a> {
|
||||
/// Create a new set of match ranges from a set of matches and the
|
||||
/// corresponding bytes that those matches apply to.
|
||||
fn new(bytes: &'a[u8], matches: &[Match]) -> SubMatches<'a> {
|
||||
fn new(bytes: &'a [u8], matches: &[Match]) -> SubMatches<'a> {
|
||||
if matches.len() == 1 {
|
||||
let mat = matches[0];
|
||||
SubMatches::Small([jsont::SubMatch {
|
||||
@ -817,11 +810,11 @@ impl<'a> SubMatches<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use grep_regex::{RegexMatcher, RegexMatcherBuilder};
|
||||
use grep_matcher::LineTerminator;
|
||||
use grep_regex::{RegexMatcher, RegexMatcherBuilder};
|
||||
use grep_searcher::SearcherBuilder;
|
||||
|
||||
use super::{JSON, JSONBuilder};
|
||||
use super::{JSONBuilder, JSON};
|
||||
|
||||
const SHERLOCK: &'static [u8] = b"\
|
||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
@ -832,9 +825,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
and exhibited clearly, with a label attached.
|
||||
";
|
||||
|
||||
fn printer_contents(
|
||||
printer: &mut JSON<Vec<u8>>,
|
||||
) -> String {
|
||||
fn printer_contents(printer: &mut JSON<Vec<u8>>) -> String {
|
||||
String::from_utf8(printer.get_mut().to_owned()).unwrap()
|
||||
}
|
||||
|
||||
@ -851,11 +842,8 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
and exhibited clearly, with a label attached.\
|
||||
";
|
||||
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let mut printer = JSONBuilder::new()
|
||||
.build(vec![]);
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = JSONBuilder::new().build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.binary_detection(BinaryDetection::quit(b'\x00'))
|
||||
.heap_limit(Some(80))
|
||||
@ -871,12 +859,9 @@ and exhibited clearly, with a label attached.\
|
||||
|
||||
#[test]
|
||||
fn max_matches() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let mut printer = JSONBuilder::new()
|
||||
.max_matches(Some(1))
|
||||
.build(vec![]);
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer =
|
||||
JSONBuilder::new().max_matches(Some(1)).build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.build()
|
||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||
@ -888,11 +873,8 @@ and exhibited clearly, with a label attached.\
|
||||
|
||||
#[test]
|
||||
fn no_match() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"DOES NOT MATCH"
|
||||
).unwrap();
|
||||
let mut printer = JSONBuilder::new()
|
||||
.build(vec![]);
|
||||
let matcher = RegexMatcher::new(r"DOES NOT MATCH").unwrap();
|
||||
let mut printer = JSONBuilder::new().build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.build()
|
||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||
@ -904,12 +886,9 @@ and exhibited clearly, with a label attached.\
|
||||
|
||||
#[test]
|
||||
fn always_begin_end_no_match() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"DOES NOT MATCH"
|
||||
).unwrap();
|
||||
let mut printer = JSONBuilder::new()
|
||||
.always_begin_end(true)
|
||||
.build(vec![]);
|
||||
let matcher = RegexMatcher::new(r"DOES NOT MATCH").unwrap();
|
||||
let mut printer =
|
||||
JSONBuilder::new().always_begin_end(true).build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.build()
|
||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher))
|
||||
@ -924,11 +903,8 @@ and exhibited clearly, with a label attached.\
|
||||
fn missing_crlf() {
|
||||
let haystack = "test\r\n".as_bytes();
|
||||
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.build("test")
|
||||
.unwrap();
|
||||
let mut printer = JSONBuilder::new()
|
||||
.build(vec![]);
|
||||
let matcher = RegexMatcherBuilder::new().build("test").unwrap();
|
||||
let mut printer = JSONBuilder::new().build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.build()
|
||||
.search_reader(&matcher, haystack, printer.sink(&matcher))
|
||||
@ -941,12 +917,9 @@ and exhibited clearly, with a label attached.\
|
||||
got.lines().nth(1).unwrap(),
|
||||
);
|
||||
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.crlf(true)
|
||||
.build("test")
|
||||
.unwrap();
|
||||
let mut printer = JSONBuilder::new()
|
||||
.build(vec![]);
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().crlf(true).build("test").unwrap();
|
||||
let mut printer = JSONBuilder::new().build(vec![]);
|
||||
SearcherBuilder::new()
|
||||
.line_terminator(LineTerminator::crlf())
|
||||
.build()
|
||||
|
@ -80,7 +80,9 @@ pub struct SubMatch<'a> {
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum Data<'a> {
|
||||
Text { text: Cow<'a, str> },
|
||||
Text {
|
||||
text: Cow<'a, str>,
|
||||
},
|
||||
Bytes {
|
||||
#[serde(serialize_with = "to_base64")]
|
||||
bytes: &'a [u8],
|
||||
@ -116,32 +118,26 @@ impl<'a> Data<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_base64<T, S>(
|
||||
bytes: T,
|
||||
ser: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where T: AsRef<[u8]>,
|
||||
S: Serializer
|
||||
fn to_base64<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
S: Serializer,
|
||||
{
|
||||
ser.serialize_str(&base64::encode(&bytes))
|
||||
}
|
||||
|
||||
fn ser_bytes<T, S>(
|
||||
bytes: T,
|
||||
ser: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where T: AsRef<[u8]>,
|
||||
S: Serializer
|
||||
fn ser_bytes<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
S: Serializer,
|
||||
{
|
||||
Data::from_bytes(bytes.as_ref()).serialize(ser)
|
||||
}
|
||||
|
||||
fn ser_path<P, S>(
|
||||
path: &Option<P>,
|
||||
ser: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where P: AsRef<Path>,
|
||||
S: Serializer
|
||||
fn ser_path<P, S>(path: &Option<P>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
S: Serializer,
|
||||
{
|
||||
path.as_ref().map(|p| Data::from_path(p.as_ref())).serialize(ser)
|
||||
}
|
||||
|
@ -84,9 +84,9 @@ extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate termcolor;
|
||||
|
||||
pub use color::{ColorError, ColorSpecs, UserColorSpec, default_color_specs};
|
||||
pub use color::{default_color_specs, ColorError, ColorSpecs, UserColorSpec};
|
||||
#[cfg(feature = "serde1")]
|
||||
pub use json::{JSON, JSONBuilder, JSONSink};
|
||||
pub use json::{JSONBuilder, JSONSink, JSON};
|
||||
pub use standard::{Standard, StandardBuilder, StandardSink};
|
||||
pub use stats::Stats;
|
||||
pub use summary::{Summary, SummaryBuilder, SummaryKind, SummarySink};
|
||||
|
@ -8,16 +8,15 @@ use std::time::Instant;
|
||||
use bstr::ByteSlice;
|
||||
use grep_matcher::{Match, Matcher};
|
||||
use grep_searcher::{
|
||||
LineStep, Searcher,
|
||||
Sink, SinkError,
|
||||
SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
||||
LineStep, Searcher, Sink, SinkContext, SinkContextKind, SinkError,
|
||||
SinkFinish, SinkMatch,
|
||||
};
|
||||
use termcolor::{ColorSpec, NoColor, WriteColor};
|
||||
|
||||
use color::ColorSpecs;
|
||||
use counter::CounterWriter;
|
||||
use stats::Stats;
|
||||
use util::{PrinterPath, Replacer, Sunk, trim_ascii_prefix};
|
||||
use util::{trim_ascii_prefix, PrinterPath, Replacer, Sunk};
|
||||
|
||||
/// The configuration for the standard printer.
|
||||
///
|
||||
@ -151,10 +150,7 @@ impl StandardBuilder {
|
||||
/// This completely overrides any previous color specifications. This does
|
||||
/// not add to any previously provided color specifications on this
|
||||
/// builder.
|
||||
pub fn color_specs(
|
||||
&mut self,
|
||||
specs: ColorSpecs,
|
||||
) -> &mut StandardBuilder {
|
||||
pub fn color_specs(&mut self, specs: ColorSpecs) -> &mut StandardBuilder {
|
||||
self.config.colors = specs;
|
||||
self
|
||||
}
|
||||
@ -409,10 +405,7 @@ impl StandardBuilder {
|
||||
/// A typical use for this option is to permit cygwin users on Windows to
|
||||
/// set the path separator to `/` instead of using the system default of
|
||||
/// `\`.
|
||||
pub fn separator_path(
|
||||
&mut self,
|
||||
sep: Option<u8>,
|
||||
) -> &mut StandardBuilder {
|
||||
pub fn separator_path(&mut self, sep: Option<u8>) -> &mut StandardBuilder {
|
||||
self.config.separator_path = sep;
|
||||
self
|
||||
}
|
||||
@ -487,12 +480,7 @@ impl<W: WriteColor> Standard<W> {
|
||||
&'s mut self,
|
||||
matcher: M,
|
||||
) -> StandardSink<'static, 's, M, W> {
|
||||
let stats =
|
||||
if self.config.stats {
|
||||
Some(Stats::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let stats = if self.config.stats { Some(Stats::new()) } else { None };
|
||||
let needs_match_granularity = self.needs_match_granularity();
|
||||
StandardSink {
|
||||
matcher: matcher,
|
||||
@ -517,20 +505,18 @@ impl<W: WriteColor> Standard<W> {
|
||||
matcher: M,
|
||||
path: &'p P,
|
||||
) -> StandardSink<'p, 's, M, W>
|
||||
where M: Matcher,
|
||||
P: ?Sized + AsRef<Path>,
|
||||
where
|
||||
M: Matcher,
|
||||
P: ?Sized + AsRef<Path>,
|
||||
{
|
||||
if !self.config.path {
|
||||
return self.sink(matcher);
|
||||
}
|
||||
let stats =
|
||||
if self.config.stats {
|
||||
Some(Stats::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let stats = if self.config.stats { Some(Stats::new()) } else { None };
|
||||
let ppath = PrinterPath::with_separator(
|
||||
path.as_ref(), self.config.separator_path);
|
||||
path.as_ref(),
|
||||
self.config.separator_path,
|
||||
);
|
||||
let needs_match_granularity = self.needs_match_granularity();
|
||||
StandardSink {
|
||||
matcher: matcher,
|
||||
@ -689,10 +675,12 @@ impl<'p, 's, M: Matcher, W: WriteColor> StandardSink<'p, 's, M, W> {
|
||||
// one search to find the matches (well, for replacements, we do one
|
||||
// additional search to perform the actual replacement).
|
||||
let matches = &mut self.standard.matches;
|
||||
self.matcher.find_iter(bytes, |m| {
|
||||
matches.push(m);
|
||||
true
|
||||
}).map_err(io::Error::error_message)?;
|
||||
self.matcher
|
||||
.find_iter(bytes, |m| {
|
||||
matches.push(m);
|
||||
true
|
||||
})
|
||||
.map_err(io::Error::error_message)?;
|
||||
// Don't report empty matches appearing at the end of the bytes.
|
||||
if !matches.is_empty()
|
||||
&& matches.last().unwrap().is_empty()
|
||||
@ -714,11 +702,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> StandardSink<'p, 's, M, W> {
|
||||
.as_ref()
|
||||
.map(|r| &*r)
|
||||
.unwrap();
|
||||
self.replacer.replace_all(
|
||||
&self.matcher,
|
||||
bytes,
|
||||
replacement,
|
||||
)?;
|
||||
self.replacer.replace_all(&self.matcher, bytes, replacement)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -811,10 +795,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn begin(
|
||||
&mut self,
|
||||
_searcher: &Searcher,
|
||||
) -> Result<bool, io::Error> {
|
||||
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, io::Error> {
|
||||
self.standard.wtr.borrow_mut().reset_count();
|
||||
self.start_time = Instant::now();
|
||||
self.match_count = 0;
|
||||
@ -887,10 +868,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
&sink.standard.matches,
|
||||
sink.replacer.replacement(),
|
||||
);
|
||||
StandardImpl {
|
||||
sunk: sunk,
|
||||
..StandardImpl::new(searcher, sink)
|
||||
}
|
||||
StandardImpl { sunk: sunk, ..StandardImpl::new(searcher, sink) }
|
||||
}
|
||||
|
||||
/// Bundle self with a searcher and return the core implementation of Sink
|
||||
@ -905,10 +883,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
&sink.standard.matches,
|
||||
sink.replacer.replacement(),
|
||||
);
|
||||
StandardImpl {
|
||||
sunk: sunk,
|
||||
..StandardImpl::new(searcher, sink)
|
||||
}
|
||||
StandardImpl { sunk: sunk, ..StandardImpl::new(searcher, sink) }
|
||||
}
|
||||
|
||||
fn sink(&self) -> io::Result<()> {
|
||||
@ -1084,10 +1059,7 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
line = line.with_start(upto);
|
||||
if self.exceeds_max_columns(&bytes[this_line]) {
|
||||
self.write_exceeded_line(
|
||||
bytes,
|
||||
this_line,
|
||||
matches,
|
||||
&mut midx,
|
||||
bytes, this_line, matches, &mut midx,
|
||||
)?;
|
||||
} else {
|
||||
self.write_spec(spec, &bytes[this_line])?;
|
||||
@ -1178,14 +1150,14 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_line(
|
||||
&self,
|
||||
line: &[u8],
|
||||
) -> io::Result<()> {
|
||||
fn write_line(&self, line: &[u8]) -> io::Result<()> {
|
||||
if self.exceeds_max_columns(line) {
|
||||
let range = Match::new(0, line.len());
|
||||
self.write_exceeded_line(
|
||||
line, range, self.sunk.matches(), &mut 0,
|
||||
line,
|
||||
range,
|
||||
self.sunk.matches(),
|
||||
&mut 0,
|
||||
)?;
|
||||
} else {
|
||||
self.write_trim(line)?;
|
||||
@ -1279,7 +1251,8 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
.map(|(_, end, _)| end)
|
||||
.take(self.config().max_columns.unwrap_or(0) as usize)
|
||||
.last()
|
||||
.unwrap_or(0) + line.start();
|
||||
.unwrap_or(0)
|
||||
+ line.start();
|
||||
line = line.with_end(end);
|
||||
self.write_colored_matches(bytes, line, matches, match_index)?;
|
||||
|
||||
@ -1292,16 +1265,12 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
m.start() >= line.end() && m.start() < original.end()
|
||||
})
|
||||
.count();
|
||||
let tense =
|
||||
if remaining == 1 {
|
||||
"match"
|
||||
} else {
|
||||
"matches"
|
||||
};
|
||||
let tense = if remaining == 1 { "match" } else { "matches" };
|
||||
write!(
|
||||
self.wtr().borrow_mut(),
|
||||
" [... {} more {}]",
|
||||
remaining, tense,
|
||||
remaining,
|
||||
tense,
|
||||
)?;
|
||||
}
|
||||
self.write_line_term()?;
|
||||
@ -1396,7 +1365,8 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
}
|
||||
let remainder = format!(
|
||||
"after match (found {:?} byte around offset {})\n",
|
||||
[byte].as_bstr(), offset,
|
||||
[byte].as_bstr(),
|
||||
offset,
|
||||
);
|
||||
self.write(remainder.as_bytes())?;
|
||||
} else if let Some(byte) = bin.convert_byte() {
|
||||
@ -1407,7 +1377,8 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
|
||||
}
|
||||
let remainder = format!(
|
||||
"matches (found {:?} byte around offset {})\n",
|
||||
[byte].as_bstr(), offset,
|
||||
[byte].as_bstr(),
|
||||
offset,
|
||||
);
|
||||
self.write(remainder.as_bytes())?;
|
||||
}
|
||||
@ -1600,17 +1571,14 @@ but Doctor Watson has to have it taken out for him and dusted,\r
|
||||
and exhibited clearly, with a label attached.\
|
||||
";
|
||||
|
||||
fn printer_contents(
|
||||
printer: &mut Standard<NoColor<Vec<u8>>>,
|
||||
) -> String {
|
||||
fn printer_contents(printer: &mut Standard<NoColor<Vec<u8>>>) -> String {
|
||||
String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reports_match() {
|
||||
let matcher = RegexMatcher::new("Sherlock").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
let mut sink = printer.sink(&matcher);
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
@ -1620,8 +1588,7 @@ and exhibited clearly, with a label attached.\
|
||||
assert!(sink.has_match());
|
||||
|
||||
let matcher = RegexMatcher::new("zzzzz").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
let mut sink = printer.sink(&matcher);
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
@ -1636,8 +1603,7 @@ and exhibited clearly, with a label attached.\
|
||||
use grep_searcher::BinaryDetection;
|
||||
|
||||
let matcher = RegexMatcher::new("Sherlock").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
let mut sink = printer.sink(&matcher);
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
@ -1647,8 +1613,7 @@ and exhibited clearly, with a label attached.\
|
||||
assert!(sink.binary_byte_offset().is_none());
|
||||
|
||||
let matcher = RegexMatcher::new(".+").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
let mut sink = printer.sink(&matcher);
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
@ -1664,9 +1629,8 @@ and exhibited clearly, with a label attached.\
|
||||
use std::time::Duration;
|
||||
|
||||
let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.stats(true)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().stats(true).build(NoColor::new(vec![]));
|
||||
let stats = {
|
||||
let mut sink = printer.sink(&matcher);
|
||||
SearcherBuilder::new()
|
||||
@ -1685,7 +1649,6 @@ and exhibited clearly, with a label attached.\
|
||||
assert_eq!(stats.bytes_printed(), buf.len() as u64);
|
||||
assert_eq!(stats.matched_lines(), 2);
|
||||
assert_eq!(stats.matches(), 3);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1693,9 +1656,8 @@ and exhibited clearly, with a label attached.\
|
||||
use std::time::Duration;
|
||||
|
||||
let matcher = RegexMatcher::new("Sherlock|opposed").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.stats(true)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().stats(true).build(NoColor::new(vec![]));
|
||||
let stats = {
|
||||
let mut sink = printer.sink(&matcher);
|
||||
SearcherBuilder::new()
|
||||
@ -1860,9 +1822,8 @@ and exhibited clearly, with a label attached.
|
||||
#[test]
|
||||
fn path() {
|
||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.path(false)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().path(false).build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(true)
|
||||
.build()
|
||||
@ -1963,9 +1924,8 @@ books/sherlockZbut Doctor Watson has to have it taken out for him and dusted,
|
||||
#[test]
|
||||
fn heading() {
|
||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.heading(true)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().heading(true).build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
.build()
|
||||
@ -1988,9 +1948,8 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
#[test]
|
||||
fn no_heading() {
|
||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.heading(false)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().heading(false).build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
.build()
|
||||
@ -2012,9 +1971,8 @@ sherlock:but Doctor Watson has to have it taken out for him and dusted,
|
||||
#[test]
|
||||
fn no_heading_multiple() {
|
||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.heading(false)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().heading(false).build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
.build()
|
||||
@ -2049,9 +2007,8 @@ sherlock:be, to a very large extent, the result of luck. Sherlock Holmes
|
||||
#[test]
|
||||
fn heading_multiple() {
|
||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.heading(true)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().heading(true).build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
.build()
|
||||
@ -2161,8 +2118,7 @@ Watson
|
||||
#[test]
|
||||
fn line_number() {
|
||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(true)
|
||||
.build()
|
||||
@ -2184,8 +2140,7 @@ Watson
|
||||
#[test]
|
||||
fn line_number_multi_line() {
|
||||
let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(true)
|
||||
.multi_line(true)
|
||||
@ -2211,9 +2166,8 @@ Watson
|
||||
#[test]
|
||||
fn column_number() {
|
||||
let matcher = RegexMatcher::new("Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.column(true)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().column(true).build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
.build()
|
||||
@ -2235,9 +2189,8 @@ Watson
|
||||
#[test]
|
||||
fn column_number_multi_line() {
|
||||
let matcher = RegexMatcher::new("(?s)Watson.+Watson").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.column(true)
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer =
|
||||
StandardBuilder::new().column(true).build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(false)
|
||||
.multi_line(true)
|
||||
@ -2440,9 +2393,8 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn max_columns_with_count_preview_two_matches() {
|
||||
let matcher = RegexMatcher::new(
|
||||
"exhibited|dusted|has to have it",
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new("exhibited|dusted|has to have it").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.stats(true)
|
||||
.max_columns(Some(46))
|
||||
@ -2493,9 +2445,9 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
|
||||
#[test]
|
||||
fn max_columns_multi_line_preview() {
|
||||
let matcher = RegexMatcher::new(
|
||||
"(?s)clew|cigar ash.+have it|exhibited",
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new("(?s)clew|cigar ash.+have it|exhibited")
|
||||
.unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.stats(true)
|
||||
.max_columns(Some(46))
|
||||
@ -2673,9 +2625,8 @@ For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
|
||||
#[test]
|
||||
fn max_matches_multi_line2() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.max_matches(Some(1))
|
||||
.build(NoColor::new(vec![]));
|
||||
@ -2726,9 +2677,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn only_matching_multi_line1() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.only_matching(true)
|
||||
.column(true)
|
||||
@ -2755,9 +2705,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn only_matching_multi_line2() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.only_matching(true)
|
||||
.column(true)
|
||||
@ -2844,9 +2793,8 @@ Holmeses, success in the province of detective work must always
|
||||
// can match across multiple lines without actually doing so. This is
|
||||
// so we can test multi-line handling in the case of a match on only
|
||||
// one line.
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.only_matching(true)
|
||||
.max_columns(Some(10))
|
||||
@ -2878,9 +2826,8 @@ Holmeses, success in the province of detective work must always
|
||||
// can match across multiple lines without actually doing so. This is
|
||||
// so we can test multi-line handling in the case of a match on only
|
||||
// one line.
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.only_matching(true)
|
||||
.max_columns(Some(10))
|
||||
@ -2909,9 +2856,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn only_matching_max_columns_multi_line2() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.only_matching(true)
|
||||
.max_columns(Some(50))
|
||||
@ -2940,9 +2886,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn only_matching_max_columns_preview_multi_line2() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s)Watson.+?(Holmeses|clearly)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.only_matching(true)
|
||||
.max_columns(Some(50))
|
||||
@ -2998,9 +2943,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn per_match_multi_line1() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s:.{0})(Doctor Watsons|Sherlock)"
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s:.{0})(Doctor Watsons|Sherlock)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.per_match(true)
|
||||
.column(true)
|
||||
@ -3027,9 +2971,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn per_match_multi_line2() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s)Watson.+?(Holmeses|clearly)",
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s)Watson.+?(Holmeses|clearly)").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.per_match(true)
|
||||
.column(true)
|
||||
@ -3057,9 +3000,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn per_match_multi_line3() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"(?s)Watson.+?Holmeses|always.+?be",
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new(r"(?s)Watson.+?Holmeses|always.+?be").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.per_match(true)
|
||||
.column(true)
|
||||
@ -3194,9 +3136,8 @@ Holmeses, success in the province of detective work must always
|
||||
|
||||
#[test]
|
||||
fn replacement_max_columns_preview2() {
|
||||
let matcher = RegexMatcher::new(
|
||||
"exhibited|dusted|has to have it",
|
||||
).unwrap();
|
||||
let matcher =
|
||||
RegexMatcher::new("exhibited|dusted|has to have it").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.max_columns(Some(43))
|
||||
.max_columns_preview(true)
|
||||
@ -3277,8 +3218,7 @@ and xxx clearly, with a label attached.
|
||||
#[test]
|
||||
fn invert() {
|
||||
let matcher = RegexMatcher::new(r"Sherlock").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(true)
|
||||
.invert_match(true)
|
||||
@ -3303,8 +3243,7 @@ and xxx clearly, with a label attached.
|
||||
#[test]
|
||||
fn invert_multi_line() {
|
||||
let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.multi_line(true)
|
||||
.line_number(true)
|
||||
@ -3330,8 +3269,7 @@ and xxx clearly, with a label attached.
|
||||
#[test]
|
||||
fn invert_context() {
|
||||
let matcher = RegexMatcher::new(r"Sherlock").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.line_number(true)
|
||||
.invert_match(true)
|
||||
@ -3360,8 +3298,7 @@ and xxx clearly, with a label attached.
|
||||
#[test]
|
||||
fn invert_context_multi_line() {
|
||||
let matcher = RegexMatcher::new(r"(?s:.{0})Sherlock").unwrap();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.build(NoColor::new(vec![]));
|
||||
let mut printer = StandardBuilder::new().build(NoColor::new(vec![]));
|
||||
SearcherBuilder::new()
|
||||
.multi_line(true)
|
||||
.line_number(true)
|
||||
|
@ -34,8 +34,8 @@ impl<'a> Add<&'a Stats> for Stats {
|
||||
Stats {
|
||||
elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0),
|
||||
searches: self.searches + rhs.searches,
|
||||
searches_with_match:
|
||||
self.searches_with_match + rhs.searches_with_match,
|
||||
searches_with_match: self.searches_with_match
|
||||
+ rhs.searches_with_match,
|
||||
bytes_searched: self.bytes_searched + rhs.bytes_searched,
|
||||
bytes_printed: self.bytes_printed + rhs.bytes_printed,
|
||||
matched_lines: self.matched_lines + rhs.matched_lines,
|
||||
|
@ -168,10 +168,7 @@ impl SummaryBuilder {
|
||||
///
|
||||
/// This is a convenience routine for
|
||||
/// `SummaryBuilder::build(termcolor::NoColor::new(wtr))`.
|
||||
pub fn build_no_color<W: io::Write>(
|
||||
&self,
|
||||
wtr: W,
|
||||
) -> Summary<NoColor<W>> {
|
||||
pub fn build_no_color<W: io::Write>(&self, wtr: W) -> Summary<NoColor<W>> {
|
||||
self.build(NoColor::new(wtr))
|
||||
}
|
||||
|
||||
@ -204,10 +201,7 @@ impl SummaryBuilder {
|
||||
/// builder.
|
||||
///
|
||||
/// The default color specifications provide no styling.
|
||||
pub fn color_specs(
|
||||
&mut self,
|
||||
specs: ColorSpecs,
|
||||
) -> &mut SummaryBuilder {
|
||||
pub fn color_specs(&mut self, specs: ColorSpecs) -> &mut SummaryBuilder {
|
||||
self.config.colors = specs;
|
||||
self
|
||||
}
|
||||
@ -281,10 +275,7 @@ impl SummaryBuilder {
|
||||
/// `CountMatches` modes.
|
||||
///
|
||||
/// By default, this is set to `:`.
|
||||
pub fn separator_field(
|
||||
&mut self,
|
||||
sep: Vec<u8>,
|
||||
) -> &mut SummaryBuilder {
|
||||
pub fn separator_field(&mut self, sep: Vec<u8>) -> &mut SummaryBuilder {
|
||||
self.config.separator_field = Arc::new(sep);
|
||||
self
|
||||
}
|
||||
@ -300,10 +291,7 @@ impl SummaryBuilder {
|
||||
/// `\`.
|
||||
///
|
||||
/// This is disabled by default.
|
||||
pub fn separator_path(
|
||||
&mut self,
|
||||
sep: Option<u8>,
|
||||
) -> &mut SummaryBuilder {
|
||||
pub fn separator_path(&mut self, sep: Option<u8>) -> &mut SummaryBuilder {
|
||||
self.config.separator_path = sep;
|
||||
self
|
||||
}
|
||||
@ -382,12 +370,11 @@ impl<W: WriteColor> Summary<W> {
|
||||
&'s mut self,
|
||||
matcher: M,
|
||||
) -> SummarySink<'static, 's, M, W> {
|
||||
let stats =
|
||||
if self.config.stats || self.config.kind.requires_stats() {
|
||||
Some(Stats::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let stats = if self.config.stats || self.config.kind.requires_stats() {
|
||||
Some(Stats::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
SummarySink {
|
||||
matcher: matcher,
|
||||
summary: self,
|
||||
@ -408,20 +395,22 @@ impl<W: WriteColor> Summary<W> {
|
||||
matcher: M,
|
||||
path: &'p P,
|
||||
) -> SummarySink<'p, 's, M, W>
|
||||
where M: Matcher,
|
||||
P: ?Sized + AsRef<Path>,
|
||||
where
|
||||
M: Matcher,
|
||||
P: ?Sized + AsRef<Path>,
|
||||
{
|
||||
if !self.config.path && !self.config.kind.requires_path() {
|
||||
return self.sink(matcher);
|
||||
}
|
||||
let stats =
|
||||
if self.config.stats || self.config.kind.requires_stats() {
|
||||
Some(Stats::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let stats = if self.config.stats || self.config.kind.requires_stats() {
|
||||
Some(Stats::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ppath = PrinterPath::with_separator(
|
||||
path.as_ref(), self.config.separator_path);
|
||||
path.as_ref(),
|
||||
self.config.separator_path,
|
||||
);
|
||||
SummarySink {
|
||||
matcher: matcher,
|
||||
summary: self,
|
||||
@ -596,10 +585,12 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
||||
self.match_count += 1;
|
||||
if let Some(ref mut stats) = self.stats {
|
||||
let mut match_count = 0;
|
||||
self.matcher.find_iter(mat.bytes(), |_| {
|
||||
match_count += 1;
|
||||
true
|
||||
}).map_err(io::Error::error_message)?;
|
||||
self.matcher
|
||||
.find_iter(mat.bytes(), |_| {
|
||||
match_count += 1;
|
||||
true
|
||||
})
|
||||
.map_err(io::Error::error_message)?;
|
||||
stats.add_matches(match_count);
|
||||
stats.add_matched_lines(mat.lines().count() as u64);
|
||||
} else if self.summary.config.kind.quit_early() {
|
||||
@ -608,10 +599,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
||||
Ok(!self.should_quit())
|
||||
}
|
||||
|
||||
fn begin(
|
||||
&mut self,
|
||||
_searcher: &Searcher,
|
||||
) -> Result<bool, io::Error> {
|
||||
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, io::Error> {
|
||||
if self.path.is_none() && self.summary.config.kind.requires_path() {
|
||||
return Err(io::Error::error_message(format!(
|
||||
"output kind {:?} requires a file path",
|
||||
@ -674,8 +662,7 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
||||
}
|
||||
|
||||
let show_count =
|
||||
!self.summary.config.exclude_zero
|
||||
|| self.match_count > 0;
|
||||
!self.summary.config.exclude_zero || self.match_count > 0;
|
||||
match self.summary.config.kind {
|
||||
SummaryKind::Count => {
|
||||
if show_count {
|
||||
@ -686,7 +673,8 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
|
||||
}
|
||||
SummaryKind::CountMatches => {
|
||||
if show_count {
|
||||
let stats = self.stats
|
||||
let stats = self
|
||||
.stats
|
||||
.as_ref()
|
||||
.expect("CountMatches should enable stats tracking");
|
||||
self.write_path_field()?;
|
||||
@ -716,7 +704,7 @@ mod tests {
|
||||
use grep_searcher::SearcherBuilder;
|
||||
use termcolor::NoColor;
|
||||
|
||||
use super::{Summary, SummaryKind, SummaryBuilder};
|
||||
use super::{Summary, SummaryBuilder, SummaryKind};
|
||||
|
||||
const SHERLOCK: &'static [u8] = b"\
|
||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
@ -727,45 +715,41 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
and exhibited clearly, with a label attached.
|
||||
";
|
||||
|
||||
fn printer_contents(
|
||||
printer: &mut Summary<NoColor<Vec<u8>>>,
|
||||
) -> String {
|
||||
fn printer_contents(printer: &mut Summary<NoColor<Vec<u8>>>) -> String {
|
||||
String::from_utf8(printer.get_mut().get_ref().to_owned()).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_with_match_error() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::PathWithMatch)
|
||||
.build_no_color(vec![]);
|
||||
let res = SearcherBuilder::new()
|
||||
.build()
|
||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher));
|
||||
let res = SearcherBuilder::new().build().search_reader(
|
||||
&matcher,
|
||||
SHERLOCK,
|
||||
printer.sink(&matcher),
|
||||
);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_without_match_error() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::PathWithoutMatch)
|
||||
.build_no_color(vec![]);
|
||||
let res = SearcherBuilder::new()
|
||||
.build()
|
||||
.search_reader(&matcher, SHERLOCK, printer.sink(&matcher));
|
||||
let res = SearcherBuilder::new().build().search_reader(
|
||||
&matcher,
|
||||
SHERLOCK,
|
||||
printer.sink(&matcher),
|
||||
);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_no_path() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.build_no_color(vec![]);
|
||||
@ -780,9 +764,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_no_path_even_with_path() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.path(false)
|
||||
@ -802,9 +784,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_path() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.build_no_color(vec![]);
|
||||
@ -823,9 +803,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_path_with_zero() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"NO MATCH"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"NO MATCH").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.exclude_zero(false)
|
||||
@ -845,9 +823,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_path_without_zero() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"NO MATCH"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"NO MATCH").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.exclude_zero(true)
|
||||
@ -867,9 +843,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_path_field_separator() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.separator_field(b"ZZ".to_vec())
|
||||
@ -889,9 +863,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_path_terminator() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.path_terminator(Some(b'\x00'))
|
||||
@ -911,9 +883,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_path_separator() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.separator_path(Some(b'\\'))
|
||||
@ -933,9 +903,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_max_matches() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Count)
|
||||
.max_matches(Some(1))
|
||||
@ -951,9 +919,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn count_matches() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson|Sherlock"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::CountMatches)
|
||||
.build_no_color(vec![]);
|
||||
@ -972,9 +938,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn path_with_match_found() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::PathWithMatch)
|
||||
.build_no_color(vec![]);
|
||||
@ -993,9 +957,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn path_with_match_not_found() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"ZZZZZZZZ"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"ZZZZZZZZ").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::PathWithMatch)
|
||||
.build_no_color(vec![]);
|
||||
@ -1012,12 +974,9 @@ and exhibited clearly, with a label attached.
|
||||
assert_eq_printed!("", got);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn path_without_match_found() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"ZZZZZZZZZ"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"ZZZZZZZZZ").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::PathWithoutMatch)
|
||||
.build_no_color(vec![]);
|
||||
@ -1036,9 +995,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn path_without_match_not_found() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::PathWithoutMatch)
|
||||
.build_no_color(vec![]);
|
||||
@ -1057,9 +1014,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn quiet() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson|Sherlock"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Quiet)
|
||||
.build_no_color(vec![]);
|
||||
@ -1081,9 +1036,7 @@ and exhibited clearly, with a label attached.
|
||||
|
||||
#[test]
|
||||
fn quiet_with_stats() {
|
||||
let matcher = RegexMatcher::new(
|
||||
r"Watson|Sherlock"
|
||||
).unwrap();
|
||||
let matcher = RegexMatcher::new(r"Watson|Sherlock").unwrap();
|
||||
let mut printer = SummaryBuilder::new()
|
||||
.kind(SummaryKind::Quiet)
|
||||
.stats(true)
|
||||
|
@ -7,8 +7,7 @@ use std::time;
|
||||
use bstr::{ByteSlice, ByteVec};
|
||||
use grep_matcher::{Captures, LineTerminator, Match, Matcher};
|
||||
use grep_searcher::{
|
||||
LineIter,
|
||||
SinkError, SinkContext, SinkContextKind, SinkMatch,
|
||||
LineIter, SinkContext, SinkContextKind, SinkError, SinkMatch,
|
||||
};
|
||||
#[cfg(feature = "serde1")]
|
||||
use serde::{Serialize, Serializer};
|
||||
@ -58,19 +57,13 @@ impl<M: Matcher> Replacer<M> {
|
||||
replacement: &[u8],
|
||||
) -> io::Result<()> {
|
||||
{
|
||||
let &mut Space {
|
||||
ref mut dst,
|
||||
ref mut caps,
|
||||
ref mut matches,
|
||||
} = self.allocate(matcher)?;
|
||||
let &mut Space { ref mut dst, ref mut caps, ref mut matches } =
|
||||
self.allocate(matcher)?;
|
||||
dst.clear();
|
||||
matches.clear();
|
||||
|
||||
matcher.replace_with_captures(
|
||||
subject,
|
||||
caps,
|
||||
dst,
|
||||
|caps, dst| {
|
||||
matcher
|
||||
.replace_with_captures(subject, caps, dst, |caps, dst| {
|
||||
let start = dst.len();
|
||||
caps.interpolate(
|
||||
|name| matcher.capture_index(name),
|
||||
@ -81,8 +74,8 @@ impl<M: Matcher> Replacer<M> {
|
||||
let end = dst.len();
|
||||
matches.push(Match::new(start, end));
|
||||
true
|
||||
},
|
||||
).map_err(io::Error::error_message)?;
|
||||
})
|
||||
.map_err(io::Error::error_message)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -122,14 +115,10 @@ impl<M: Matcher> Replacer<M> {
|
||||
/// matcher fails.
|
||||
fn allocate(&mut self, matcher: &M) -> io::Result<&mut Space<M>> {
|
||||
if self.space.is_none() {
|
||||
let caps = matcher
|
||||
.new_captures()
|
||||
.map_err(io::Error::error_message)?;
|
||||
self.space = Some(Space {
|
||||
caps: caps,
|
||||
dst: vec![],
|
||||
matches: vec![],
|
||||
});
|
||||
let caps =
|
||||
matcher.new_captures().map_err(io::Error::error_message)?;
|
||||
self.space =
|
||||
Some(Space { caps: caps, dst: vec![], matches: vec![] });
|
||||
}
|
||||
Ok(self.space.as_mut().unwrap())
|
||||
}
|
||||
@ -176,9 +165,8 @@ impl<'a> Sunk<'a> {
|
||||
original_matches: &'a [Match],
|
||||
replacement: Option<(&'a [u8], &'a [Match])>,
|
||||
) -> Sunk<'a> {
|
||||
let (bytes, matches) = replacement.unwrap_or_else(|| {
|
||||
(sunk.bytes(), original_matches)
|
||||
});
|
||||
let (bytes, matches) =
|
||||
replacement.unwrap_or_else(|| (sunk.bytes(), original_matches));
|
||||
Sunk {
|
||||
bytes: bytes,
|
||||
absolute_byte_offset: sunk.absolute_byte_offset(),
|
||||
@ -195,9 +183,8 @@ impl<'a> Sunk<'a> {
|
||||
original_matches: &'a [Match],
|
||||
replacement: Option<(&'a [u8], &'a [Match])>,
|
||||
) -> Sunk<'a> {
|
||||
let (bytes, matches) = replacement.unwrap_or_else(|| {
|
||||
(sunk.bytes(), original_matches)
|
||||
});
|
||||
let (bytes, matches) =
|
||||
replacement.unwrap_or_else(|| (sunk.bytes(), original_matches));
|
||||
Sunk {
|
||||
bytes: bytes,
|
||||
absolute_byte_offset: sunk.absolute_byte_offset(),
|
||||
@ -289,13 +276,17 @@ impl<'a> PrinterPath<'a> {
|
||||
/// path separators that are both replaced by `new_sep`. In all other
|
||||
/// environments, only `/` is treated as a path separator.
|
||||
fn replace_separator(&mut self, new_sep: u8) {
|
||||
let transformed_path: Vec<u8> = self.0.bytes().map(|b| {
|
||||
if b == b'/' || (cfg!(windows) && b == b'\\') {
|
||||
new_sep
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}).collect();
|
||||
let transformed_path: Vec<u8> = self
|
||||
.0
|
||||
.bytes()
|
||||
.map(|b| {
|
||||
if b == b'/' || (cfg!(windows) && b == b'\\') {
|
||||
new_sep
|
||||
} else {
|
||||
b
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.0 = Cow::Owned(transformed_path);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use regex_syntax::ast::{self, Ast};
|
||||
use regex_syntax::ast::parse::Parser;
|
||||
use regex_syntax::ast::{self, Ast};
|
||||
|
||||
/// The results of analyzing AST of a regular expression (e.g., for supporting
|
||||
/// smart case).
|
||||
|
@ -51,8 +51,8 @@ impl Default for Config {
|
||||
octal: false,
|
||||
// These size limits are much bigger than what's in the regex
|
||||
// crate.
|
||||
size_limit: 100 * (1<<20),
|
||||
dfa_size_limit: 1000 * (1<<20),
|
||||
size_limit: 100 * (1 << 20),
|
||||
dfa_size_limit: 1000 * (1 << 20),
|
||||
nest_limit: 250,
|
||||
line_terminator: None,
|
||||
crlf: false,
|
||||
@ -95,10 +95,7 @@ impl Config {
|
||||
|
||||
/// Accounting for the `smart_case` config knob, return true if and only if
|
||||
/// this pattern should be matched case insensitively.
|
||||
fn is_case_insensitive(
|
||||
&self,
|
||||
analysis: &AstAnalysis,
|
||||
) -> bool {
|
||||
fn is_case_insensitive(&self, analysis: &AstAnalysis) -> bool {
|
||||
if self.case_insensitive {
|
||||
return true;
|
||||
}
|
||||
@ -116,9 +113,7 @@ impl Config {
|
||||
/// are enabled, since if multi-line can impact the match semantics of a
|
||||
/// regex, then it is by definition not a simple alternation of literals.
|
||||
pub fn can_plain_aho_corasick(&self) -> bool {
|
||||
!self.word
|
||||
&& !self.case_insensitive
|
||||
&& !self.case_smart
|
||||
!self.word && !self.case_insensitive && !self.case_smart
|
||||
}
|
||||
|
||||
/// Perform analysis on the AST of this pattern.
|
||||
@ -203,8 +198,7 @@ impl ConfiguredHIR {
|
||||
pub fn with_pattern<F: FnMut(&str) -> String>(
|
||||
&self,
|
||||
mut f: F,
|
||||
) -> Result<ConfiguredHIR, Error>
|
||||
{
|
||||
) -> Result<ConfiguredHIR, Error> {
|
||||
self.pattern_to_hir(&f(&self.expr.to_string()))
|
||||
}
|
||||
|
||||
|
@ -76,9 +76,8 @@ impl Matcher for CRLFMatcher {
|
||||
caps: &mut RegexCaptures,
|
||||
) -> Result<bool, NoError> {
|
||||
caps.strip_crlf(false);
|
||||
let r = self.regex.captures_read_at(
|
||||
caps.locations_mut(), haystack, at,
|
||||
);
|
||||
let r =
|
||||
self.regex.captures_read_at(caps.locations_mut(), haystack, at);
|
||||
if !r.is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
@ -163,8 +162,8 @@ pub fn crlfify(expr: Hir) -> Hir {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use regex_syntax::Parser;
|
||||
use super::crlfify;
|
||||
use regex_syntax::Parser;
|
||||
|
||||
fn roundtrip(pattern: &str) -> String {
|
||||
let expr1 = Parser::new().parse(pattern).unwrap();
|
||||
|
@ -5,8 +5,8 @@ the regex engine doesn't look for inner literals. Since we're doing line based
|
||||
searching, we can use them, so we need to do it ourselves.
|
||||
*/
|
||||
|
||||
use regex_syntax::hir::{self, Hir, HirKind};
|
||||
use regex_syntax::hir::literal::{Literal, Literals};
|
||||
use regex_syntax::hir::{self, Hir, HirKind};
|
||||
|
||||
use util;
|
||||
|
||||
@ -159,10 +159,8 @@ impl LiteralSets {
|
||||
};
|
||||
|
||||
debug!("prefix/suffix literals found: {:?}", lits);
|
||||
let alts: Vec<String> = lits
|
||||
.into_iter()
|
||||
.map(|x| util::bytes_to_regex(x))
|
||||
.collect();
|
||||
let alts: Vec<String> =
|
||||
lits.into_iter().map(|x| util::bytes_to_regex(x)).collect();
|
||||
// We're matching raw bytes, so disable Unicode mode.
|
||||
Some(format!("(?-u:{})", alts.join("|")))
|
||||
} else {
|
||||
@ -194,24 +192,28 @@ fn union_required(expr: &Hir, lits: &mut Literals) {
|
||||
HirKind::Group(hir::Group { ref hir, .. }) => {
|
||||
union_required(&**hir, lits);
|
||||
}
|
||||
HirKind::Repetition(ref x) => {
|
||||
match x.kind {
|
||||
hir::RepetitionKind::ZeroOrOne => lits.cut(),
|
||||
hir::RepetitionKind::ZeroOrMore => lits.cut(),
|
||||
hir::RepetitionKind::OneOrMore => {
|
||||
union_required(&x.hir, lits);
|
||||
}
|
||||
hir::RepetitionKind::Range(ref rng) => {
|
||||
let (min, max) = match *rng {
|
||||
hir::RepetitionRange::Exactly(m) => (m, Some(m)),
|
||||
hir::RepetitionRange::AtLeast(m) => (m, None),
|
||||
hir::RepetitionRange::Bounded(m, n) => (m, Some(n)),
|
||||
};
|
||||
repeat_range_literals(
|
||||
&x.hir, min, max, x.greedy, lits, union_required);
|
||||
}
|
||||
HirKind::Repetition(ref x) => match x.kind {
|
||||
hir::RepetitionKind::ZeroOrOne => lits.cut(),
|
||||
hir::RepetitionKind::ZeroOrMore => lits.cut(),
|
||||
hir::RepetitionKind::OneOrMore => {
|
||||
union_required(&x.hir, lits);
|
||||
}
|
||||
}
|
||||
hir::RepetitionKind::Range(ref rng) => {
|
||||
let (min, max) = match *rng {
|
||||
hir::RepetitionRange::Exactly(m) => (m, Some(m)),
|
||||
hir::RepetitionRange::AtLeast(m) => (m, None),
|
||||
hir::RepetitionRange::Bounded(m, n) => (m, Some(n)),
|
||||
};
|
||||
repeat_range_literals(
|
||||
&x.hir,
|
||||
min,
|
||||
max,
|
||||
x.greedy,
|
||||
lits,
|
||||
union_required,
|
||||
);
|
||||
}
|
||||
},
|
||||
HirKind::Concat(ref es) if es.is_empty() => {}
|
||||
HirKind::Concat(ref es) if es.len() == 1 => {
|
||||
union_required(&es[0], lits)
|
||||
@ -310,9 +312,9 @@ fn is_simple(expr: &Hir) -> bool {
|
||||
| HirKind::Repetition(_)
|
||||
| HirKind::Concat(_)
|
||||
| HirKind::Alternation(_) => true,
|
||||
HirKind::Anchor(_)
|
||||
| HirKind::WordBoundary(_)
|
||||
| HirKind::Group(_) => false,
|
||||
HirKind::Anchor(_) | HirKind::WordBoundary(_) | HirKind::Group(_) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,8 +330,8 @@ fn count_byte_class(cls: &hir::ClassBytes) -> u32 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use regex_syntax::Parser;
|
||||
use super::LiteralSets;
|
||||
use regex_syntax::Parser;
|
||||
|
||||
fn sets(pattern: &str) -> LiteralSets {
|
||||
let hir = Parser::new().parse(pattern).unwrap();
|
||||
@ -380,8 +382,10 @@ mod tests {
|
||||
fn regression_1319() {
|
||||
// Regression from:
|
||||
// https://github.com/BurntSushi/ripgrep/issues/1319
|
||||
assert_eq!(one_regex(r"TTGAGTCCAGGAG[ATCG]{2}C"),
|
||||
assert_eq!(
|
||||
one_regex(r"TTGAGTCCAGGAG[ATCG]{2}C"),
|
||||
pat("TTGAGTCCAGGAGA|TTGAGTCCAGGAGC|\
|
||||
TTGAGTCCAGGAGG|TTGAGTCCAGGAGT"));
|
||||
TTGAGTCCAGGAGG|TTGAGTCCAGGAGT")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use grep_matcher::{
|
||||
Captures, LineMatchKind, LineTerminator, Match, Matcher, NoError, ByteSet,
|
||||
ByteSet, Captures, LineMatchKind, LineTerminator, Match, Matcher, NoError,
|
||||
};
|
||||
use regex::bytes::{CaptureLocations, Regex};
|
||||
|
||||
@ -34,9 +34,7 @@ impl Default for RegexMatcherBuilder {
|
||||
impl RegexMatcherBuilder {
|
||||
/// Create a new builder for configuring a regex matcher.
|
||||
pub fn new() -> RegexMatcherBuilder {
|
||||
RegexMatcherBuilder {
|
||||
config: Config::default(),
|
||||
}
|
||||
RegexMatcherBuilder { config: Config::default() }
|
||||
}
|
||||
|
||||
/// Build a new matcher using the current configuration for the provided
|
||||
@ -382,9 +380,7 @@ impl RegexMatcher {
|
||||
/// given pattern contains a literal `\n`. Other uses of `\n` (such as in
|
||||
/// `\s`) are removed transparently.
|
||||
pub fn new_line_matcher(pattern: &str) -> Result<RegexMatcher, Error> {
|
||||
RegexMatcherBuilder::new()
|
||||
.line_terminator(Some(b'\n'))
|
||||
.build(pattern)
|
||||
RegexMatcherBuilder::new().line_terminator(Some(b'\n')).build(pattern)
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,12 +495,9 @@ impl Matcher for RegexMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_iter<F>(
|
||||
&self,
|
||||
haystack: &[u8],
|
||||
matched: F,
|
||||
) -> Result<(), NoError>
|
||||
where F: FnMut(Match) -> bool
|
||||
fn find_iter<F>(&self, haystack: &[u8], matched: F) -> Result<(), NoError>
|
||||
where
|
||||
F: FnMut(Match) -> bool,
|
||||
{
|
||||
use self::RegexMatcherImpl::*;
|
||||
match self.matcher {
|
||||
@ -520,7 +513,8 @@ impl Matcher for RegexMatcher {
|
||||
haystack: &[u8],
|
||||
matched: F,
|
||||
) -> Result<Result<(), E>, NoError>
|
||||
where F: FnMut(Match) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(Match) -> Result<bool, E>,
|
||||
{
|
||||
use self::RegexMatcherImpl::*;
|
||||
match self.matcher {
|
||||
@ -551,7 +545,8 @@ impl Matcher for RegexMatcher {
|
||||
caps: &mut RegexCaptures,
|
||||
matched: F,
|
||||
) -> Result<(), NoError>
|
||||
where F: FnMut(&RegexCaptures) -> bool
|
||||
where
|
||||
F: FnMut(&RegexCaptures) -> bool,
|
||||
{
|
||||
use self::RegexMatcherImpl::*;
|
||||
match self.matcher {
|
||||
@ -568,7 +563,8 @@ impl Matcher for RegexMatcher {
|
||||
caps: &mut RegexCaptures,
|
||||
matched: F,
|
||||
) -> Result<Result<(), E>, NoError>
|
||||
where F: FnMut(&RegexCaptures) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(&RegexCaptures) -> Result<bool, E>,
|
||||
{
|
||||
use self::RegexMatcherImpl::*;
|
||||
match self.matcher {
|
||||
@ -602,7 +598,8 @@ impl Matcher for RegexMatcher {
|
||||
dst: &mut Vec<u8>,
|
||||
append: F,
|
||||
) -> Result<(), NoError>
|
||||
where F: FnMut(Match, &mut Vec<u8>) -> bool
|
||||
where
|
||||
F: FnMut(Match, &mut Vec<u8>) -> bool,
|
||||
{
|
||||
use self::RegexMatcherImpl::*;
|
||||
match self.matcher {
|
||||
@ -620,7 +617,8 @@ impl Matcher for RegexMatcher {
|
||||
dst: &mut Vec<u8>,
|
||||
append: F,
|
||||
) -> Result<(), NoError>
|
||||
where F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool
|
||||
where
|
||||
F: FnMut(&Self::Captures, &mut Vec<u8>) -> bool,
|
||||
{
|
||||
use self::RegexMatcherImpl::*;
|
||||
match self.matcher {
|
||||
@ -745,7 +743,8 @@ impl Matcher for StandardMatcher {
|
||||
haystack: &[u8],
|
||||
at: usize,
|
||||
) -> Result<Option<Match>, NoError> {
|
||||
Ok(self.regex
|
||||
Ok(self
|
||||
.regex
|
||||
.find_at(haystack, at)
|
||||
.map(|m| Match::new(m.start(), m.end())))
|
||||
}
|
||||
@ -767,7 +766,8 @@ impl Matcher for StandardMatcher {
|
||||
haystack: &[u8],
|
||||
mut matched: F,
|
||||
) -> Result<Result<(), E>, NoError>
|
||||
where F: FnMut(Match) -> Result<bool, E>
|
||||
where
|
||||
F: FnMut(Match) -> Result<bool, E>,
|
||||
{
|
||||
for m in self.regex.find_iter(haystack) {
|
||||
match matched(Match::new(m.start(), m.end())) {
|
||||
@ -785,9 +785,10 @@ impl Matcher for StandardMatcher {
|
||||
at: usize,
|
||||
caps: &mut RegexCaptures,
|
||||
) -> Result<bool, NoError> {
|
||||
Ok(self.regex.captures_read_at(
|
||||
&mut caps.locations_mut(), haystack, at,
|
||||
).is_some())
|
||||
Ok(self
|
||||
.regex
|
||||
.captures_read_at(&mut caps.locations_mut(), haystack, at)
|
||||
.is_some())
|
||||
}
|
||||
|
||||
fn shortest_match_at(
|
||||
@ -901,7 +902,9 @@ impl RegexCaptures {
|
||||
offset: usize,
|
||||
) -> RegexCaptures {
|
||||
RegexCaptures(RegexCapturesImp::Regex {
|
||||
locs, offset, strip_crlf: false,
|
||||
locs,
|
||||
offset,
|
||||
strip_crlf: false,
|
||||
})
|
||||
}
|
||||
|
||||
@ -910,9 +913,7 @@ impl RegexCaptures {
|
||||
RegexCapturesImp::AhoCorasick { .. } => {
|
||||
panic!("getting locations for simple captures is invalid")
|
||||
}
|
||||
RegexCapturesImp::Regex { ref locs, .. } => {
|
||||
locs
|
||||
}
|
||||
RegexCapturesImp::Regex { ref locs, .. } => locs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,9 +922,7 @@ impl RegexCaptures {
|
||||
RegexCapturesImp::AhoCorasick { .. } => {
|
||||
panic!("getting locations for simple captures is invalid")
|
||||
}
|
||||
RegexCapturesImp::Regex { ref mut locs, .. } => {
|
||||
locs
|
||||
}
|
||||
RegexCapturesImp::Regex { ref mut locs, .. } => locs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -952,23 +951,19 @@ impl RegexCaptures {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use grep_matcher::{LineMatchKind, Matcher};
|
||||
use super::*;
|
||||
use grep_matcher::{LineMatchKind, Matcher};
|
||||
|
||||
// Test that enabling word matches does the right thing and demonstrate
|
||||
// the difference between it and surrounding the regex in `\b`.
|
||||
#[test]
|
||||
fn word() {
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.word(true)
|
||||
.build(r"-2")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().word(true).build(r"-2").unwrap();
|
||||
assert!(matcher.is_match(b"abc -2 foo").unwrap());
|
||||
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.word(false)
|
||||
.build(r"\b-2\b")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().word(false).build(r"\b-2\b").unwrap();
|
||||
assert!(!matcher.is_match(b"abc -2 foo").unwrap());
|
||||
}
|
||||
|
||||
@ -977,9 +972,7 @@ mod tests {
|
||||
#[test]
|
||||
fn line_terminator() {
|
||||
// This works, because there's no line terminator specified.
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.build(r"abc\sxyz")
|
||||
.unwrap();
|
||||
let matcher = RegexMatcherBuilder::new().build(r"abc\sxyz").unwrap();
|
||||
assert!(matcher.is_match(b"abc\nxyz").unwrap());
|
||||
|
||||
// This doesn't.
|
||||
@ -1029,16 +1022,12 @@ mod tests {
|
||||
// Test that smart case works.
|
||||
#[test]
|
||||
fn case_smart() {
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.case_smart(true)
|
||||
.build(r"abc")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().case_smart(true).build(r"abc").unwrap();
|
||||
assert!(matcher.is_match(b"ABC").unwrap());
|
||||
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.case_smart(true)
|
||||
.build(r"aBc")
|
||||
.unwrap();
|
||||
let matcher =
|
||||
RegexMatcherBuilder::new().case_smart(true).build(r"aBc").unwrap();
|
||||
assert!(!matcher.is_match(b"ABC").unwrap());
|
||||
}
|
||||
|
||||
@ -1060,9 +1049,7 @@ mod tests {
|
||||
|
||||
// With no line terminator set, we can't employ any optimizations,
|
||||
// so we get a confirmed match.
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.build(r"\wfoo\s")
|
||||
.unwrap();
|
||||
let matcher = RegexMatcherBuilder::new().build(r"\wfoo\s").unwrap();
|
||||
let m = matcher.find_candidate_line(b"afoo ").unwrap().unwrap();
|
||||
assert!(is_confirmed(m));
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use aho_corasick::{AhoCorasick, AhoCorasickBuilder, MatchKind};
|
||||
use grep_matcher::{Matcher, Match, NoError};
|
||||
use grep_matcher::{Match, Matcher, NoError};
|
||||
use regex_syntax::hir::Hir;
|
||||
|
||||
use error::Error;
|
||||
@ -93,15 +93,13 @@ pub fn alternation_literals(expr: &Hir) -> Option<Vec<Vec<u8>>> {
|
||||
_ => return None, // one literal isn't worth it
|
||||
};
|
||||
|
||||
let extendlit = |lit: &Literal, dst: &mut Vec<u8>| {
|
||||
match *lit {
|
||||
Literal::Unicode(c) => {
|
||||
let mut buf = [0; 4];
|
||||
dst.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
|
||||
}
|
||||
Literal::Byte(b) => {
|
||||
dst.push(b);
|
||||
}
|
||||
let extendlit = |lit: &Literal, dst: &mut Vec<u8>| match *lit {
|
||||
Literal::Unicode(c) => {
|
||||
let mut buf = [0; 4];
|
||||
dst.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
|
||||
}
|
||||
Literal::Byte(b) => {
|
||||
dst.push(b);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,14 +11,9 @@ pub fn non_matching_bytes(expr: &Hir) -> ByteSet {
|
||||
|
||||
/// Remove any bytes from the given set that can occur in a matched produced by
|
||||
/// the given expression.
|
||||
fn remove_matching_bytes(
|
||||
expr: &Hir,
|
||||
set: &mut ByteSet,
|
||||
) {
|
||||
fn remove_matching_bytes(expr: &Hir, set: &mut ByteSet) {
|
||||
match *expr.kind() {
|
||||
HirKind::Empty
|
||||
| HirKind::Anchor(_)
|
||||
| HirKind::WordBoundary(_) => {}
|
||||
HirKind::Empty | HirKind::Anchor(_) | HirKind::WordBoundary(_) => {}
|
||||
HirKind::Literal(hir::Literal::Unicode(c)) => {
|
||||
for &b in c.encode_utf8(&mut [0; 4]).as_bytes() {
|
||||
set.remove(b);
|
||||
@ -105,15 +100,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn dot() {
|
||||
assert_eq!(sparse(&extract(".")), vec![
|
||||
b'\n',
|
||||
192, 193, 245, 246, 247, 248, 249,
|
||||
250, 251, 252, 253, 254, 255,
|
||||
]);
|
||||
assert_eq!(sparse(&extract("(?s).")), vec![
|
||||
192, 193, 245, 246, 247, 248, 249,
|
||||
250, 251, 252, 253, 254, 255,
|
||||
]);
|
||||
assert_eq!(
|
||||
sparse(&extract(".")),
|
||||
vec![
|
||||
b'\n', 192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253,
|
||||
254, 255,
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
sparse(&extract("(?s).")),
|
||||
vec![
|
||||
192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
|
||||
255,
|
||||
]
|
||||
);
|
||||
assert_eq!(sparse(&extract("(?-u).")), vec![b'\n']);
|
||||
assert_eq!(sparse(&extract("(?s-u).")), vec![]);
|
||||
}
|
||||
|
@ -33,10 +33,7 @@ pub fn strip_from_match(
|
||||
|
||||
/// The implementation of strip_from_match. The given byte must be ASCII. This
|
||||
/// function panics otherwise.
|
||||
fn strip_from_match_ascii(
|
||||
expr: Hir,
|
||||
byte: u8,
|
||||
) -> Result<Hir, Error> {
|
||||
fn strip_from_match_ascii(expr: Hir, byte: u8) -> Result<Hir, Error> {
|
||||
assert!(byte <= 0x7F);
|
||||
let chr = byte as char;
|
||||
assert_eq!(chr.len_utf8(), 1);
|
||||
@ -88,13 +85,15 @@ fn strip_from_match_ascii(
|
||||
Hir::group(x)
|
||||
}
|
||||
HirKind::Concat(xs) => {
|
||||
let xs = xs.into_iter()
|
||||
let xs = xs
|
||||
.into_iter()
|
||||
.map(|e| strip_from_match_ascii(e, byte))
|
||||
.collect::<Result<Vec<Hir>, Error>>()?;
|
||||
Hir::concat(xs)
|
||||
}
|
||||
HirKind::Alternation(xs) => {
|
||||
let xs = xs.into_iter()
|
||||
let xs = xs
|
||||
.into_iter()
|
||||
.map(|e| strip_from_match_ascii(e, byte))
|
||||
.collect::<Result<Vec<Hir>, Error>>()?;
|
||||
Hir::alternation(xs)
|
||||
@ -106,8 +105,8 @@ fn strip_from_match_ascii(
|
||||
mod tests {
|
||||
use regex_syntax::Parser;
|
||||
|
||||
use super::{strip_from_match, LineTerminator};
|
||||
use error::Error;
|
||||
use super::{LineTerminator, strip_from_match};
|
||||
|
||||
fn roundtrip(pattern: &str, byte: u8) -> String {
|
||||
roundtrip_line_term(pattern, LineTerminator::byte(byte)).unwrap()
|
||||
|
@ -1,8 +1,8 @@
|
||||
/// Converts an arbitrary sequence of bytes to a literal suitable for building
|
||||
/// a regular expression.
|
||||
pub fn bytes_to_regex(bs: &[u8]) -> String {
|
||||
use std::fmt::Write;
|
||||
use regex_syntax::is_meta_character;
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut s = String::with_capacity(bs.len());
|
||||
for &b in bs {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use grep_matcher::{Match, Matcher, NoError};
|
||||
@ -45,9 +45,8 @@ impl WordMatcher {
|
||||
/// The given options are used to construct the regular expression
|
||||
/// internally.
|
||||
pub fn new(expr: &ConfiguredHIR) -> Result<WordMatcher, Error> {
|
||||
let original = expr.with_pattern(|pat| {
|
||||
format!("^(?:{})$", pat)
|
||||
})?.regex()?;
|
||||
let original =
|
||||
expr.with_pattern(|pat| format!("^(?:{})$", pat))?.regex()?;
|
||||
let word_expr = expr.with_pattern(|pat| {
|
||||
let pat = format!(r"(?:(?-m:^)|\W)({})(?:(?-m:$)|\W)", pat);
|
||||
debug!("word regex: {:?}", pat);
|
||||
@ -112,9 +111,8 @@ impl WordMatcher {
|
||||
}
|
||||
let (_, slen) = bstr::decode_utf8(&haystack[cand]);
|
||||
let (_, elen) = bstr::decode_last_utf8(&haystack[cand]);
|
||||
cand = cand
|
||||
.with_start(cand.start() + slen)
|
||||
.with_end(cand.end() - elen);
|
||||
cand =
|
||||
cand.with_start(cand.start() + slen).with_end(cand.end() - elen);
|
||||
if self.original.is_match(&haystack[cand]) {
|
||||
Ok(Some(cand))
|
||||
} else {
|
||||
@ -148,9 +146,8 @@ impl Matcher for WordMatcher {
|
||||
Err(()) => {}
|
||||
}
|
||||
|
||||
let cell = self.locs.get_or(|| {
|
||||
RefCell::new(self.regex.capture_locations())
|
||||
});
|
||||
let cell =
|
||||
self.locs.get_or(|| RefCell::new(self.regex.capture_locations()));
|
||||
let mut caps = cell.borrow_mut();
|
||||
self.regex.captures_read_at(&mut caps, haystack, at);
|
||||
Ok(caps.get(1).map(|m| Match::new(m.0, m.1)))
|
||||
@ -174,9 +171,8 @@ impl Matcher for WordMatcher {
|
||||
at: usize,
|
||||
caps: &mut RegexCaptures,
|
||||
) -> Result<bool, NoError> {
|
||||
let r = self.regex.captures_read_at(
|
||||
caps.locations_mut(), haystack, at,
|
||||
);
|
||||
let r =
|
||||
self.regex.captures_read_at(caps.locations_mut(), haystack, at);
|
||||
Ok(r.is_some())
|
||||
}
|
||||
|
||||
@ -187,9 +183,9 @@ impl Matcher for WordMatcher {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use grep_matcher::{Captures, Match, Matcher};
|
||||
use config::Config;
|
||||
use super::WordMatcher;
|
||||
use config::Config;
|
||||
use grep_matcher::{Captures, Match, Matcher};
|
||||
|
||||
fn matcher(pattern: &str) -> WordMatcher {
|
||||
let chir = Config::default().hir(pattern).unwrap();
|
||||
|
@ -7,8 +7,8 @@ use std::io;
|
||||
use std::process;
|
||||
|
||||
use grep_regex::RegexMatcher;
|
||||
use grep_searcher::Searcher;
|
||||
use grep_searcher::sinks::UTF8;
|
||||
use grep_searcher::Searcher;
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = example() {
|
||||
@ -20,14 +20,18 @@ fn main() {
|
||||
fn example() -> Result<(), Box<dyn Error>> {
|
||||
let pattern = match env::args().nth(1) {
|
||||
Some(pattern) => pattern,
|
||||
None => return Err(From::from(format!(
|
||||
"Usage: search-stdin <pattern>"
|
||||
))),
|
||||
None => {
|
||||
return Err(From::from(format!("Usage: search-stdin <pattern>")))
|
||||
}
|
||||
};
|
||||
let matcher = RegexMatcher::new(&pattern)?;
|
||||
Searcher::new().search_reader(&matcher, io::stdin(), UTF8(|lnum, line| {
|
||||
print!("{}:{}", lnum, line);
|
||||
Ok(true)
|
||||
}))?;
|
||||
Searcher::new().search_reader(
|
||||
&matcher,
|
||||
io::stdin(),
|
||||
UTF8(|lnum, line| {
|
||||
print!("{}:{}", lnum, line);
|
||||
Ok(true)
|
||||
}),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -112,14 +112,13 @@ extern crate regex;
|
||||
|
||||
pub use lines::{LineIter, LineStep};
|
||||
pub use searcher::{
|
||||
BinaryDetection, ConfigError, Encoding, MmapChoice,
|
||||
Searcher, SearcherBuilder,
|
||||
};
|
||||
pub use sink::{
|
||||
Sink, SinkError,
|
||||
SinkContext, SinkContextKind, SinkFinish, SinkMatch,
|
||||
BinaryDetection, ConfigError, Encoding, MmapChoice, Searcher,
|
||||
SearcherBuilder,
|
||||
};
|
||||
pub use sink::sinks;
|
||||
pub use sink::{
|
||||
Sink, SinkContext, SinkContextKind, SinkError, SinkFinish, SinkMatch,
|
||||
};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
@ -4,7 +4,7 @@ use std::io;
|
||||
use bstr::ByteSlice;
|
||||
|
||||
/// The default buffer capacity that we use for the line buffer.
|
||||
pub(crate) const DEFAULT_BUFFER_CAPACITY: usize = 8 * (1<<10); // 8 KB
|
||||
pub(crate) const DEFAULT_BUFFER_CAPACITY: usize = 8 * (1 << 10); // 8 KB
|
||||
|
||||
/// The behavior of a searcher in the face of long lines and big contexts.
|
||||
///
|
||||
@ -442,16 +442,15 @@ impl LineBuffer {
|
||||
}
|
||||
}
|
||||
BinaryDetection::Convert(byte) => {
|
||||
if let Some(i) = replace_bytes(
|
||||
newbytes,
|
||||
byte,
|
||||
self.config.lineterm,
|
||||
) {
|
||||
if let Some(i) =
|
||||
replace_bytes(newbytes, byte, self.config.lineterm)
|
||||
{
|
||||
// Record only the first binary offset.
|
||||
if self.binary_byte_offset.is_none() {
|
||||
self.binary_byte_offset =
|
||||
Some(self.absolute_byte_offset
|
||||
+ (oldend + i) as u64);
|
||||
self.binary_byte_offset = Some(
|
||||
self.absolute_byte_offset
|
||||
+ (oldend + i) as u64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -542,9 +541,9 @@ fn replace_bytes(bytes: &mut [u8], src: u8, replacement: u8) -> Option<usize> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str;
|
||||
use bstr::{ByteSlice, ByteVec};
|
||||
use super::*;
|
||||
use bstr::{ByteSlice, ByteVec};
|
||||
use std::str;
|
||||
|
||||
const SHERLOCK: &'static str = "\
|
||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
@ -858,10 +857,13 @@ and exhibited clearly, with a label attached.\
|
||||
assert!(rdr.buffer().is_empty());
|
||||
|
||||
assert!(rdr.fill().unwrap());
|
||||
assert_eq!(rdr.bstr(), "\
|
||||
assert_eq!(
|
||||
rdr.bstr(),
|
||||
"\
|
||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
Holmeses, s\
|
||||
");
|
||||
"
|
||||
);
|
||||
rdr.consume_all();
|
||||
|
||||
assert!(!rdr.fill().unwrap());
|
||||
|
@ -130,14 +130,9 @@ pub fn without_terminator(bytes: &[u8], line_term: LineTerminator) -> &[u8] {
|
||||
///
|
||||
/// Line terminators are considered part of the line they terminate.
|
||||
#[inline(always)]
|
||||
pub fn locate(
|
||||
bytes: &[u8],
|
||||
line_term: u8,
|
||||
range: Match,
|
||||
) -> Match {
|
||||
let line_start = bytes[..range.start()]
|
||||
.rfind_byte(line_term)
|
||||
.map_or(0, |i| i + 1);
|
||||
pub fn locate(bytes: &[u8], line_term: u8, range: Match) -> Match {
|
||||
let line_start =
|
||||
bytes[..range.start()].rfind_byte(line_term).map_or(0, |i| i + 1);
|
||||
let line_end =
|
||||
if range.end() > line_start && bytes[range.end() - 1] == line_term {
|
||||
range.end()
|
||||
@ -201,10 +196,10 @@ fn preceding_by_pos(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use grep_matcher::Match;
|
||||
use std::ops::Range;
|
||||
use std::str;
|
||||
use grep_matcher::Match;
|
||||
use super::*;
|
||||
|
||||
const SHERLOCK: &'static str = "\
|
||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
@ -260,29 +255,37 @@ and exhibited clearly, with a label attached.\
|
||||
|
||||
assert_eq!(
|
||||
loc(t, lines[0].start, lines[0].end),
|
||||
m(lines[0].start, lines[0].end));
|
||||
m(lines[0].start, lines[0].end)
|
||||
);
|
||||
assert_eq!(
|
||||
loc(t, lines[0].start + 1, lines[0].end),
|
||||
m(lines[0].start, lines[0].end));
|
||||
m(lines[0].start, lines[0].end)
|
||||
);
|
||||
assert_eq!(
|
||||
loc(t, lines[0].end - 1, lines[0].end),
|
||||
m(lines[0].start, lines[0].end));
|
||||
m(lines[0].start, lines[0].end)
|
||||
);
|
||||
assert_eq!(
|
||||
loc(t, lines[0].end, lines[0].end),
|
||||
m(lines[1].start, lines[1].end));
|
||||
m(lines[1].start, lines[1].end)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
loc(t, lines[5].start, lines[5].end),
|
||||
m(lines[5].start, lines[5].end));
|
||||
m(lines[5].start, lines[5].end)
|
||||
);
|
||||
assert_eq!(
|
||||
loc(t, lines[5].start + 1, lines[5].end),
|
||||
m(lines[5].start, lines[5].end));
|
||||
m(lines[5].start, lines[5].end)
|
||||
);
|
||||
assert_eq!(
|
||||
loc(t, lines[5].end - 1, lines[5].end),
|
||||
m(lines[5].start, lines[5].end));
|
||||
m(lines[5].start, lines[5].end)
|
||||
);
|
||||
assert_eq!(
|
||||
loc(t, lines[5].end, lines[5].end),
|
||||
m(lines[5].start, lines[5].end));
|
||||
m(lines[5].start, lines[5].end)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3,12 +3,11 @@ use std::cmp;
|
||||
use bstr::ByteSlice;
|
||||
|
||||
use grep_matcher::{LineMatchKind, Matcher};
|
||||
use lines::{self, LineStep};
|
||||
use line_buffer::BinaryDetection;
|
||||
use lines::{self, LineStep};
|
||||
use searcher::{Config, Range, Searcher};
|
||||
use sink::{
|
||||
Sink, SinkError,
|
||||
SinkFinish, SinkContext, SinkContextKind, SinkMatch,
|
||||
Sink, SinkContext, SinkContextKind, SinkError, SinkFinish, SinkMatch,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -36,11 +35,7 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
||||
binary: bool,
|
||||
) -> Core<'s, M, S> {
|
||||
let line_number =
|
||||
if searcher.config.line_number {
|
||||
Some(1)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if searcher.config.line_number { Some(1) } else { None };
|
||||
let core = Core {
|
||||
config: &searcher.config,
|
||||
matcher: matcher,
|
||||
@ -108,10 +103,8 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
||||
) -> Result<(), S::Error> {
|
||||
self.sink.finish(
|
||||
&self.searcher,
|
||||
&SinkFinish {
|
||||
byte_count,
|
||||
binary_byte_offset,
|
||||
})
|
||||
&SinkFinish { byte_count, binary_byte_offset },
|
||||
)
|
||||
}
|
||||
|
||||
pub fn match_by_line(&mut self, buf: &[u8]) -> Result<bool, S::Error> {
|
||||
@ -123,23 +116,22 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
||||
}
|
||||
|
||||
pub fn roll(&mut self, buf: &[u8]) -> usize {
|
||||
let consumed =
|
||||
if self.config.max_context() == 0 {
|
||||
buf.len()
|
||||
} else {
|
||||
// It might seem like all we need to care about here is just
|
||||
// the "before context," but in order to sink the context
|
||||
// separator (when before_context==0 and after_context>0), we
|
||||
// need to know something about the position of the previous
|
||||
// line visited, even if we're at the beginning of the buffer.
|
||||
let context_start = lines::preceding(
|
||||
buf,
|
||||
self.config.line_term.as_byte(),
|
||||
self.config.max_context(),
|
||||
);
|
||||
let consumed = cmp::max(context_start, self.last_line_visited);
|
||||
consumed
|
||||
};
|
||||
let consumed = if self.config.max_context() == 0 {
|
||||
buf.len()
|
||||
} else {
|
||||
// It might seem like all we need to care about here is just
|
||||
// the "before context," but in order to sink the context
|
||||
// separator (when before_context==0 and after_context>0), we
|
||||
// need to know something about the position of the previous
|
||||
// line visited, even if we're at the beginning of the buffer.
|
||||
let context_start = lines::preceding(
|
||||
buf,
|
||||
self.config.line_term.as_byte(),
|
||||
self.config.max_context(),
|
||||
);
|
||||
let consumed = cmp::max(context_start, self.last_line_visited);
|
||||
consumed
|
||||
};
|
||||
self.count_lines(buf, consumed);
|
||||
self.absolute_byte_offset += consumed as u64;
|
||||
self.last_line_counted = 0;
|
||||
@ -185,11 +177,12 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
||||
if range.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
let before_context_start = range.start() + lines::preceding(
|
||||
&buf[range],
|
||||
self.config.line_term.as_byte(),
|
||||
self.config.before_context - 1,
|
||||
);
|
||||
let before_context_start = range.start()
|
||||
+ lines::preceding(
|
||||
&buf[range],
|
||||
self.config.line_term.as_byte(),
|
||||
self.config.before_context - 1,
|
||||
);
|
||||
|
||||
let range = Range::new(before_context_start, range.end());
|
||||
let mut stepper = LineStep::new(
|
||||
@ -552,8 +545,7 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
||||
) -> Result<bool, S::Error> {
|
||||
let is_gap = self.last_line_visited < start_of_line;
|
||||
let any_context =
|
||||
self.config.before_context > 0
|
||||
|| self.config.after_context > 0;
|
||||
self.config.before_context > 0 || self.config.after_context > 0;
|
||||
|
||||
if !any_context || !self.has_sunk || !is_gap {
|
||||
Ok(true)
|
||||
|
@ -2,12 +2,12 @@ use std::cmp;
|
||||
use std::io;
|
||||
|
||||
use grep_matcher::Matcher;
|
||||
use line_buffer::{LineBufferReader, DEFAULT_BUFFER_CAPACITY};
|
||||
use lines::{self, LineStep};
|
||||
use line_buffer::{DEFAULT_BUFFER_CAPACITY, LineBufferReader};
|
||||
use sink::{Sink, SinkError};
|
||||
|
||||
use searcher::{Config, Range, Searcher};
|
||||
use searcher::core::Core;
|
||||
use searcher::{Config, Range, Searcher};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReadByLine<'s, M: 's, R, S> {
|
||||
@ -17,9 +17,10 @@ pub struct ReadByLine<'s, M: 's, R, S> {
|
||||
}
|
||||
|
||||
impl<'s, M, R, S> ReadByLine<'s, M, R, S>
|
||||
where M: Matcher,
|
||||
R: io::Read,
|
||||
S: Sink
|
||||
where
|
||||
M: Matcher,
|
||||
R: io::Read,
|
||||
S: Sink,
|
||||
{
|
||||
pub fn new(
|
||||
searcher: &'s Searcher,
|
||||
@ -38,9 +39,8 @@ where M: Matcher,
|
||||
|
||||
pub fn run(mut self) -> Result<(), S::Error> {
|
||||
if self.core.begin()? {
|
||||
while
|
||||
self.fill()? && self.core.match_by_line(self.rdr.buffer())?
|
||||
{}
|
||||
while self.fill()? && self.core.match_by_line(self.rdr.buffer())? {
|
||||
}
|
||||
}
|
||||
self.core.finish(
|
||||
self.rdr.absolute_byte_offset(),
|
||||
@ -82,7 +82,7 @@ where M: Matcher,
|
||||
|
||||
fn should_binary_quit(&self) -> bool {
|
||||
self.rdr.binary_byte_offset().is_some()
|
||||
&& self.config.binary.quit_byte().is_some()
|
||||
&& self.config.binary.quit_byte().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,14 +111,11 @@ impl<'s, M: Matcher, S: Sink> SliceByLine<'s, M, S> {
|
||||
|
||||
pub fn run(mut self) -> Result<(), S::Error> {
|
||||
if self.core.begin()? {
|
||||
let binary_upto = cmp::min(
|
||||
self.slice.len(),
|
||||
DEFAULT_BUFFER_CAPACITY,
|
||||
);
|
||||
let binary_upto =
|
||||
cmp::min(self.slice.len(), DEFAULT_BUFFER_CAPACITY);
|
||||
let binary_range = Range::new(0, binary_upto);
|
||||
if !self.core.detect_binary(self.slice, &binary_range)? {
|
||||
while
|
||||
!self.slice[self.core.pos()..].is_empty()
|
||||
while !self.slice[self.core.pos()..].is_empty()
|
||||
&& self.core.match_by_line(self.slice)?
|
||||
{}
|
||||
}
|
||||
@ -163,10 +160,8 @@ impl<'s, M: Matcher, S: Sink> MultiLine<'s, M, S> {
|
||||
|
||||
pub fn run(mut self) -> Result<(), S::Error> {
|
||||
if self.core.begin()? {
|
||||
let binary_upto = cmp::min(
|
||||
self.slice.len(),
|
||||
DEFAULT_BUFFER_CAPACITY,
|
||||
);
|
||||
let binary_upto =
|
||||
cmp::min(self.slice.len(), DEFAULT_BUFFER_CAPACITY);
|
||||
let binary_range = Range::new(0, binary_upto);
|
||||
if !self.core.detect_binary(self.slice, &binary_range)? {
|
||||
let mut keepgoing = true;
|
||||
@ -218,11 +213,8 @@ impl<'s, M: Matcher, S: Sink> MultiLine<'s, M, S> {
|
||||
};
|
||||
self.advance(&mat);
|
||||
|
||||
let line = lines::locate(
|
||||
self.slice,
|
||||
self.config.line_term.as_byte(),
|
||||
mat,
|
||||
);
|
||||
let line =
|
||||
lines::locate(self.slice, self.config.line_term.as_byte(), mat);
|
||||
// We delay sinking the match to make sure we group adjacent matches
|
||||
// together in a single sink. Adjacent matches are distinct matches
|
||||
// that start and end on the same line, respectively. This guarantees
|
||||
@ -502,7 +494,8 @@ byte count:366
|
||||
let byte_count = haystack.len();
|
||||
let exp = format!(
|
||||
"4:abc\n8:defxxxabc\n18:defxxx\n\nbyte count:{}\n",
|
||||
byte_count);
|
||||
byte_count
|
||||
);
|
||||
|
||||
SearcherTester::new(haystack, "abc\ndef")
|
||||
.by_line(false)
|
||||
@ -517,7 +510,8 @@ byte count:366
|
||||
let byte_count = haystack.len();
|
||||
let exp = format!(
|
||||
"4:abc\n8:defabc\n15:defxxx\n\nbyte count:{}\n",
|
||||
byte_count);
|
||||
byte_count
|
||||
);
|
||||
|
||||
SearcherTester::new(haystack, "abc\ndef")
|
||||
.by_line(false)
|
||||
@ -571,9 +565,8 @@ d
|
||||
";
|
||||
let byte_count = haystack.len();
|
||||
let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
|
||||
let exp_line = format!(
|
||||
"3:4:\n5:7:\n6:8:\n\nbyte count:{}\n",
|
||||
byte_count);
|
||||
let exp_line =
|
||||
format!("3:4:\n5:7:\n6:8:\n\nbyte count:{}\n", byte_count);
|
||||
|
||||
SearcherTester::new(haystack, r"^$")
|
||||
.expected_no_line_number(&exp)
|
||||
@ -595,9 +588,8 @@ c
|
||||
d";
|
||||
let byte_count = haystack.len();
|
||||
let exp = format!("4:\n7:\n8:\n\nbyte count:{}\n", byte_count);
|
||||
let exp_line = format!(
|
||||
"3:4:\n5:7:\n6:8:\n\nbyte count:{}\n",
|
||||
byte_count);
|
||||
let exp_line =
|
||||
format!("3:4:\n5:7:\n6:8:\n\nbyte count:{}\n", byte_count);
|
||||
|
||||
SearcherTester::new(haystack, r"^$")
|
||||
.expected_no_line_number(&exp)
|
||||
@ -620,12 +612,9 @@ d
|
||||
|
||||
";
|
||||
let byte_count = haystack.len();
|
||||
let exp = format!(
|
||||
"4:\n7:\n8:\n11:\n\nbyte count:{}\n",
|
||||
byte_count);
|
||||
let exp_line = format!(
|
||||
"3:4:\n5:7:\n6:8:\n8:11:\n\nbyte count:{}\n",
|
||||
byte_count);
|
||||
let exp = format!("4:\n7:\n8:\n11:\n\nbyte count:{}\n", byte_count);
|
||||
let exp_line =
|
||||
format!("3:4:\n5:7:\n6:8:\n8:11:\n\nbyte count:{}\n", byte_count);
|
||||
|
||||
SearcherTester::new(haystack, r"^$")
|
||||
.expected_no_line_number(&exp)
|
||||
@ -667,11 +656,8 @@ d
|
||||
let mut searcher = SearcherBuilder::new()
|
||||
.heap_limit(Some(3)) // max line length is 4, one byte short
|
||||
.build();
|
||||
let result = searcher.search_reader(
|
||||
&matcher,
|
||||
haystack.as_bytes(),
|
||||
&mut sink,
|
||||
);
|
||||
let result =
|
||||
searcher.search_reader(&matcher, haystack.as_bytes(), &mut sink);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@ -691,11 +677,8 @@ d
|
||||
.multi_line(true)
|
||||
.heap_limit(Some(haystack.len())) // actually need one more byte
|
||||
.build();
|
||||
let result = searcher.search_reader(
|
||||
&matcher,
|
||||
haystack.as_bytes(),
|
||||
&mut sink,
|
||||
);
|
||||
let result =
|
||||
searcher.search_reader(&matcher, haystack.as_bytes(), &mut sink);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@ -1508,12 +1491,16 @@ and exhibited clearly, with a label attached.\
|
||||
|
||||
let haystack = SHERLOCK;
|
||||
let matcher = RegexMatcher::new("Sherlock");
|
||||
let mut searcher = SearcherBuilder::new()
|
||||
.line_number(true)
|
||||
.build();
|
||||
searcher.search_reader(&matcher, haystack, sinks::Lossy(|n, line| {
|
||||
print!("{}:{}", n, line);
|
||||
Ok(true)
|
||||
})).unwrap();
|
||||
let mut searcher = SearcherBuilder::new().line_number(true).build();
|
||||
searcher
|
||||
.search_reader(
|
||||
&matcher,
|
||||
haystack,
|
||||
sinks::Lossy(|n, line| {
|
||||
print!("{}:{}", n, line);
|
||||
Ok(true)
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ use encoding_rs;
|
||||
use encoding_rs_io::DecodeReaderBytesBuilder;
|
||||
use grep_matcher::{LineTerminator, Match, Matcher};
|
||||
use line_buffer::{
|
||||
self, BufferAllocation, LineBuffer, LineBufferBuilder, LineBufferReader,
|
||||
DEFAULT_BUFFER_CAPACITY, alloc_error,
|
||||
self, alloc_error, BufferAllocation, LineBuffer, LineBufferBuilder,
|
||||
LineBufferReader, DEFAULT_BUFFER_CAPACITY,
|
||||
};
|
||||
use searcher::glue::{ReadByLine, SliceByLine, MultiLine};
|
||||
use searcher::glue::{MultiLine, ReadByLine, SliceByLine};
|
||||
use sink::{Sink, SinkError};
|
||||
|
||||
pub use self::mmap::MmapChoice;
|
||||
@ -211,12 +211,11 @@ impl Config {
|
||||
.binary_detection(self.binary.0);
|
||||
|
||||
if let Some(limit) = self.heap_limit {
|
||||
let (capacity, additional) =
|
||||
if limit <= DEFAULT_BUFFER_CAPACITY {
|
||||
(limit, 0)
|
||||
} else {
|
||||
(DEFAULT_BUFFER_CAPACITY, limit - DEFAULT_BUFFER_CAPACITY)
|
||||
};
|
||||
let (capacity, additional) = if limit <= DEFAULT_BUFFER_CAPACITY {
|
||||
(limit, 0)
|
||||
} else {
|
||||
(DEFAULT_BUFFER_CAPACITY, limit - DEFAULT_BUFFER_CAPACITY)
|
||||
};
|
||||
builder
|
||||
.capacity(capacity)
|
||||
.buffer_alloc(BufferAllocation::Error(additional));
|
||||
@ -258,7 +257,9 @@ pub enum ConfigError {
|
||||
}
|
||||
|
||||
impl ::std::error::Error for ConfigError {
|
||||
fn description(&self) -> &str { "grep-searcher configuration error" }
|
||||
fn description(&self) -> &str {
|
||||
"grep-searcher configuration error"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ConfigError {
|
||||
@ -272,17 +273,14 @@ impl fmt::Display for ConfigError {
|
||||
f,
|
||||
"grep config error: mismatched line terminators, \
|
||||
matcher has {:?} but searcher has {:?}",
|
||||
matcher,
|
||||
searcher
|
||||
)
|
||||
}
|
||||
ConfigError::UnknownEncoding { ref label } => {
|
||||
write!(
|
||||
f,
|
||||
"grep config error: unknown encoding: {}",
|
||||
String::from_utf8_lossy(label),
|
||||
matcher, searcher
|
||||
)
|
||||
}
|
||||
ConfigError::UnknownEncoding { ref label } => write!(
|
||||
f,
|
||||
"grep config error: unknown encoding: {}",
|
||||
String::from_utf8_lossy(label),
|
||||
),
|
||||
_ => panic!("BUG: unexpected variant found"),
|
||||
}
|
||||
}
|
||||
@ -310,9 +308,7 @@ impl Default for SearcherBuilder {
|
||||
impl SearcherBuilder {
|
||||
/// Create a new searcher builder with a default configuration.
|
||||
pub fn new() -> SearcherBuilder {
|
||||
SearcherBuilder {
|
||||
config: Config::default(),
|
||||
}
|
||||
SearcherBuilder { config: Config::default() }
|
||||
}
|
||||
|
||||
/// Build a searcher with the given matcher.
|
||||
@ -334,7 +330,7 @@ impl SearcherBuilder {
|
||||
Searcher {
|
||||
config: config,
|
||||
decode_builder: decode_builder,
|
||||
decode_buffer: RefCell::new(vec![0; 8 * (1<<10)]),
|
||||
decode_buffer: RefCell::new(vec![0; 8 * (1 << 10)]),
|
||||
line_buffer: RefCell::new(self.config.line_buffer()),
|
||||
multi_line_buffer: RefCell::new(vec![]),
|
||||
}
|
||||
@ -622,9 +618,10 @@ impl Searcher {
|
||||
path: P,
|
||||
write_to: S,
|
||||
) -> Result<(), S::Error>
|
||||
where P: AsRef<Path>,
|
||||
M: Matcher,
|
||||
S: Sink,
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
M: Matcher,
|
||||
S: Sink,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
let file = File::open(path).map_err(S::Error::error_io)?;
|
||||
@ -643,8 +640,9 @@ impl Searcher {
|
||||
file: &File,
|
||||
write_to: S,
|
||||
) -> Result<(), S::Error>
|
||||
where M: Matcher,
|
||||
S: Sink,
|
||||
where
|
||||
M: Matcher,
|
||||
S: Sink,
|
||||
{
|
||||
self.search_file_maybe_path(matcher, None, file, write_to)
|
||||
}
|
||||
@ -656,8 +654,9 @@ impl Searcher {
|
||||
file: &File,
|
||||
write_to: S,
|
||||
) -> Result<(), S::Error>
|
||||
where M: Matcher,
|
||||
S: Sink,
|
||||
where
|
||||
M: Matcher,
|
||||
S: Sink,
|
||||
{
|
||||
if let Some(mmap) = self.config.mmap.open(file, path) {
|
||||
trace!("{:?}: searching via memory map", path);
|
||||
@ -675,7 +674,8 @@ impl Searcher {
|
||||
matcher,
|
||||
&*self.multi_line_buffer.borrow(),
|
||||
write_to,
|
||||
).run()
|
||||
)
|
||||
.run()
|
||||
} else {
|
||||
trace!("{:?}: searching using generic reader", path);
|
||||
self.search_reader(matcher, file, write_to)
|
||||
@ -699,14 +699,16 @@ impl Searcher {
|
||||
read_from: R,
|
||||
write_to: S,
|
||||
) -> Result<(), S::Error>
|
||||
where M: Matcher,
|
||||
R: io::Read,
|
||||
S: Sink,
|
||||
where
|
||||
M: Matcher,
|
||||
R: io::Read,
|
||||
S: Sink,
|
||||
{
|
||||
self.check_config(&matcher).map_err(S::Error::error_config)?;
|
||||
|
||||
let mut decode_buffer = self.decode_buffer.borrow_mut();
|
||||
let read_from = self.decode_builder
|
||||
let read_from = self
|
||||
.decode_builder
|
||||
.build_with_buffer(read_from, &mut *decode_buffer)
|
||||
.map_err(S::Error::error_io)?;
|
||||
|
||||
@ -719,7 +721,8 @@ impl Searcher {
|
||||
matcher,
|
||||
&*self.multi_line_buffer.borrow(),
|
||||
write_to,
|
||||
).run()
|
||||
)
|
||||
.run()
|
||||
} else {
|
||||
let mut line_buffer = self.line_buffer.borrow_mut();
|
||||
let rdr = LineBufferReader::new(read_from, &mut *line_buffer);
|
||||
@ -736,8 +739,9 @@ impl Searcher {
|
||||
slice: &[u8],
|
||||
write_to: S,
|
||||
) -> Result<(), S::Error>
|
||||
where M: Matcher,
|
||||
S: Sink,
|
||||
where
|
||||
M: Matcher,
|
||||
S: Sink,
|
||||
{
|
||||
self.check_config(&matcher).map_err(S::Error::error_config)?;
|
||||
|
||||
@ -764,8 +768,7 @@ impl Searcher {
|
||||
/// Check that the searcher's configuration and the matcher are consistent
|
||||
/// with each other.
|
||||
fn check_config<M: Matcher>(&self, matcher: M) -> Result<(), ConfigError> {
|
||||
if self.config.heap_limit == Some(0)
|
||||
&& !self.config.mmap.is_enabled()
|
||||
if self.config.heap_limit == Some(0) && !self.config.mmap.is_enabled()
|
||||
{
|
||||
return Err(ConfigError::SearchUnavailable);
|
||||
}
|
||||
@ -785,7 +788,7 @@ impl Searcher {
|
||||
/// Returns true if and only if the given slice needs to be transcoded.
|
||||
fn slice_needs_transcoding(&self, slice: &[u8]) -> bool {
|
||||
self.config.encoding.is_some()
|
||||
|| (self.config.bom_sniffing && slice_has_utf16_bom(slice))
|
||||
|| (self.config.bom_sniffing && slice_has_utf16_bom(slice))
|
||||
}
|
||||
}
|
||||
|
||||
@ -886,7 +889,8 @@ impl Searcher {
|
||||
assert!(self.config.multi_line);
|
||||
|
||||
let mut decode_buffer = self.decode_buffer.borrow_mut();
|
||||
let mut read_from = self.decode_builder
|
||||
let mut read_from = self
|
||||
.decode_builder
|
||||
.build_with_buffer(file, &mut *decode_buffer)
|
||||
.map_err(S::Error::error_io)?;
|
||||
|
||||
@ -900,10 +904,8 @@ impl Searcher {
|
||||
if self.config.heap_limit.is_none() {
|
||||
let mut buf = self.multi_line_buffer.borrow_mut();
|
||||
buf.clear();
|
||||
let cap = file
|
||||
.metadata()
|
||||
.map(|m| m.len() as usize + 1)
|
||||
.unwrap_or(0);
|
||||
let cap =
|
||||
file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0);
|
||||
buf.reserve(cap);
|
||||
read_from.read_to_end(&mut *buf).map_err(S::Error::error_io)?;
|
||||
return Ok(());
|
||||
@ -929,7 +931,9 @@ impl Searcher {
|
||||
let heap_limit = match self.config.heap_limit {
|
||||
Some(heap_limit) => heap_limit,
|
||||
None => {
|
||||
read_from.read_to_end(&mut *buf).map_err(S::Error::error_io)?;
|
||||
read_from
|
||||
.read_to_end(&mut *buf)
|
||||
.map_err(S::Error::error_io)?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
@ -983,16 +987,14 @@ fn slice_has_utf16_bom(slice: &[u8]) -> bool {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use testutil::{KitchenSink, RegexMatcher};
|
||||
use super::*;
|
||||
use testutil::{KitchenSink, RegexMatcher};
|
||||
|
||||
#[test]
|
||||
fn config_error_heap_limit() {
|
||||
let matcher = RegexMatcher::new("");
|
||||
let sink = KitchenSink::new();
|
||||
let mut searcher = SearcherBuilder::new()
|
||||
.heap_limit(Some(0))
|
||||
.build();
|
||||
let mut searcher = SearcherBuilder::new().heap_limit(Some(0)).build();
|
||||
let res = searcher.search_slice(matcher, &[], sink);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
@ -200,10 +200,7 @@ pub trait Sink {
|
||||
/// `finish` is not called and the error is bubbled back up to the caller
|
||||
/// of the searcher.
|
||||
#[inline]
|
||||
fn begin(
|
||||
&mut self,
|
||||
_searcher: &Searcher,
|
||||
) -> Result<bool, Self::Error> {
|
||||
fn begin(&mut self, _searcher: &Searcher) -> Result<bool, Self::Error> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@ -261,10 +258,7 @@ impl<'a, S: Sink> Sink for &'a mut S {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn begin(
|
||||
&mut self,
|
||||
searcher: &Searcher,
|
||||
) -> Result<bool, S::Error> {
|
||||
fn begin(&mut self, searcher: &Searcher) -> Result<bool, S::Error> {
|
||||
(**self).begin(searcher)
|
||||
}
|
||||
|
||||
@ -317,10 +311,7 @@ impl<S: Sink + ?Sized> Sink for Box<S> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn begin(
|
||||
&mut self,
|
||||
searcher: &Searcher,
|
||||
) -> Result<bool, S::Error> {
|
||||
fn begin(&mut self, searcher: &Searcher) -> Result<bool, S::Error> {
|
||||
(**self).begin(searcher)
|
||||
}
|
||||
|
||||
@ -508,8 +499,8 @@ pub mod sinks {
|
||||
use std::io;
|
||||
use std::str;
|
||||
|
||||
use searcher::Searcher;
|
||||
use super::{Sink, SinkError, SinkMatch};
|
||||
use searcher::Searcher;
|
||||
|
||||
/// A sink that provides line numbers and matches as strings while ignoring
|
||||
/// everything else.
|
||||
@ -528,10 +519,12 @@ pub mod sinks {
|
||||
/// number of the first line in the match.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UTF8<F>(pub F)
|
||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
||||
where
|
||||
F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
||||
|
||||
impl<F> Sink for UTF8<F>
|
||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>
|
||||
where
|
||||
F: FnMut(u64, &str) -> Result<bool, io::Error>,
|
||||
{
|
||||
type Error = io::Error;
|
||||
|
||||
@ -574,10 +567,12 @@ pub mod sinks {
|
||||
/// number of the first line in the match.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Lossy<F>(pub F)
|
||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
||||
where
|
||||
F: FnMut(u64, &str) -> Result<bool, io::Error>;
|
||||
|
||||
impl<F> Sink for Lossy<F>
|
||||
where F: FnMut(u64, &str) -> Result<bool, io::Error>
|
||||
where
|
||||
F: FnMut(u64, &str) -> Result<bool, io::Error>,
|
||||
{
|
||||
type Error = io::Error;
|
||||
|
||||
@ -622,10 +617,12 @@ pub mod sinks {
|
||||
/// number of the first line in the match.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Bytes<F>(pub F)
|
||||
where F: FnMut(u64, &[u8]) -> Result<bool, io::Error>;
|
||||
where
|
||||
F: FnMut(u64, &[u8]) -> Result<bool, io::Error>;
|
||||
|
||||
impl<F> Sink for Bytes<F>
|
||||
where F: FnMut(u64, &[u8]) -> Result<bool, io::Error>
|
||||
where
|
||||
F: FnMut(u64, &[u8]) -> Result<bool, io::Error>,
|
||||
{
|
||||
type Error = io::Error;
|
||||
|
||||
|
@ -52,10 +52,7 @@ impl RegexMatcher {
|
||||
/// Whether to return every line as a candidate or not.
|
||||
///
|
||||
/// This forces searchers to handle the case of reporting a false positive.
|
||||
pub fn every_line_is_candidate(
|
||||
&mut self,
|
||||
yes: bool,
|
||||
) -> &mut RegexMatcher {
|
||||
pub fn every_line_is_candidate(&mut self, yes: bool) -> &mut RegexMatcher {
|
||||
self.every_line_is_candidate = yes;
|
||||
self
|
||||
}
|
||||
@ -70,9 +67,10 @@ impl Matcher for RegexMatcher {
|
||||
haystack: &[u8],
|
||||
at: usize,
|
||||
) -> Result<Option<Match>, NoError> {
|
||||
Ok(self.regex
|
||||
.find_at(haystack, at)
|
||||
.map(|m| Match::new(m.start(), m.end())))
|
||||
Ok(self
|
||||
.regex
|
||||
.find_at(haystack, at)
|
||||
.map(|m| Match::new(m.start(), m.end())))
|
||||
}
|
||||
|
||||
fn new_captures(&self) -> Result<NoCaptures, NoError> {
|
||||
@ -253,8 +251,10 @@ impl SearcherTester {
|
||||
panic!("an 'expected' string with NO line numbers must be given");
|
||||
}
|
||||
if self.line_number && self.expected_with_line_number.is_none() {
|
||||
panic!("an 'expected' string with line numbers must be given, \
|
||||
or disable testing with line numbers");
|
||||
panic!(
|
||||
"an 'expected' string with line numbers must be given, \
|
||||
or disable testing with line numbers"
|
||||
);
|
||||
}
|
||||
|
||||
let configs = self.configs();
|
||||
@ -465,18 +465,17 @@ impl SearcherTester {
|
||||
lens.sort();
|
||||
lens.reverse();
|
||||
|
||||
let context_count =
|
||||
if self.passthru {
|
||||
self.haystack.lines().count()
|
||||
} else {
|
||||
// Why do we add 2 here? Well, we need to add 1 in order to
|
||||
// have room to search at least one line. We add another
|
||||
// because the implementation will occasionally include
|
||||
// an additional line when handling the context. There's
|
||||
// no particularly good reason, other than keeping the
|
||||
// implementation simple.
|
||||
2 + self.before_context + self.after_context
|
||||
};
|
||||
let context_count = if self.passthru {
|
||||
self.haystack.lines().count()
|
||||
} else {
|
||||
// Why do we add 2 here? Well, we need to add 1 in order to
|
||||
// have room to search at least one line. We add another
|
||||
// because the implementation will occasionally include
|
||||
// an additional line when handling the context. There's
|
||||
// no particularly good reason, other than keeping the
|
||||
// implementation simple.
|
||||
2 + self.before_context + self.after_context
|
||||
};
|
||||
|
||||
// We add 1 to each line since `str::lines` doesn't include the
|
||||
// line terminator.
|
||||
@ -635,10 +634,11 @@ impl SearcherTester {
|
||||
if self.multi_line && self.line_number {
|
||||
let mut builder = builder.clone();
|
||||
let expected_slice = match self.expected_slice_with_line_number {
|
||||
None => {
|
||||
self.expected_with_line_number
|
||||
.as_ref().unwrap().to_string()
|
||||
}
|
||||
None => self
|
||||
.expected_with_line_number
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
Some(ref e) => e.to_string(),
|
||||
};
|
||||
|
||||
|
@ -40,13 +40,11 @@ fn search(pattern: &str, paths: &[OsString]) -> Result<(), Box<dyn Error>> {
|
||||
.build();
|
||||
let mut printer = StandardBuilder::new()
|
||||
.color_specs(ColorSpecs::default_with_color())
|
||||
.build(cli::stdout(
|
||||
if cli::is_tty_stdout() {
|
||||
ColorChoice::Auto
|
||||
} else {
|
||||
ColorChoice::Never
|
||||
}
|
||||
));
|
||||
.build(cli::stdout(if cli::is_tty_stdout() {
|
||||
ColorChoice::Auto
|
||||
} else {
|
||||
ColorChoice::Never
|
||||
}));
|
||||
|
||||
for path in paths {
|
||||
for result in WalkDir::new(path) {
|
||||
|
244
ignore/src/default_types.rs
Normal file
244
ignore/src/default_types.rs
Normal file
@ -0,0 +1,244 @@
|
||||
/// This list represents the default file types that ripgrep ships with. In
|
||||
/// general, any file format is fair game, although it should generally be
|
||||
/// limited to reasonably popular open formats. For other cases, you can add
|
||||
/// types to each invocation of ripgrep with the '--type-add' flag.
|
||||
///
|
||||
/// If you would like to add or improve this list, please file a PR:
|
||||
/// https://github.com/BurntSushi/ripgrep
|
||||
///
|
||||
/// Please try to keep this list sorted lexicographically and wrapped to 79
|
||||
/// columns (inclusive).
|
||||
#[rustfmt::skip]
|
||||
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
|
||||
("agda", &["*.agda", "*.lagda"]),
|
||||
("aidl", &["*.aidl"]),
|
||||
("amake", &["*.mk", "*.bp"]),
|
||||
("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
|
||||
("asm", &["*.asm", "*.s", "*.S"]),
|
||||
("asp", &[
|
||||
"*.aspx", "*.aspx.cs", "*.aspx.cs", "*.ascx", "*.ascx.cs", "*.ascx.vb",
|
||||
]),
|
||||
("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
|
||||
("avro", &["*.avdl", "*.avpr", "*.avsc"]),
|
||||
("awk", &["*.awk"]),
|
||||
("bazel", &["*.bzl", "WORKSPACE", "BUILD", "BUILD.bazel"]),
|
||||
("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
|
||||
("brotli", &["*.br"]),
|
||||
("buildstream", &["*.bst"]),
|
||||
("bzip2", &["*.bz2", "*.tbz2"]),
|
||||
("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
|
||||
("cabal", &["*.cabal"]),
|
||||
("cbor", &["*.cbor"]),
|
||||
("ceylon", &["*.ceylon"]),
|
||||
("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
|
||||
("cmake", &["*.cmake", "CMakeLists.txt"]),
|
||||
("coffeescript", &["*.coffee"]),
|
||||
("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
|
||||
("cpp", &[
|
||||
"*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh", "*.inl",
|
||||
"*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
|
||||
]),
|
||||
("creole", &["*.creole"]),
|
||||
("crystal", &["Projectfile", "*.cr"]),
|
||||
("cs", &["*.cs"]),
|
||||
("csharp", &["*.cs"]),
|
||||
("cshtml", &["*.cshtml"]),
|
||||
("css", &["*.css", "*.scss"]),
|
||||
("csv", &["*.csv"]),
|
||||
("cython", &["*.pyx", "*.pxi", "*.pxd"]),
|
||||
("d", &["*.d"]),
|
||||
("dart", &["*.dart"]),
|
||||
("dhall", &["*.dhall"]),
|
||||
("diff", &["*.patch", "*.diff"]),
|
||||
("docker", &["*Dockerfile*"]),
|
||||
("edn", &["*.edn"]),
|
||||
("elisp", &["*.el"]),
|
||||
("elixir", &["*.ex", "*.eex", "*.exs"]),
|
||||
("elm", &["*.elm"]),
|
||||
("erb", &["*.erb"]),
|
||||
("erlang", &["*.erl", "*.hrl"]),
|
||||
("fidl", &["*.fidl"]),
|
||||
("fish", &["*.fish"]),
|
||||
("fortran", &[
|
||||
"*.f", "*.F", "*.f77", "*.F77", "*.pfo",
|
||||
"*.f90", "*.F90", "*.f95", "*.F95",
|
||||
]),
|
||||
("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
|
||||
("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
|
||||
("gn", &["*.gn", "*.gni"]),
|
||||
("go", &["*.go"]),
|
||||
("gradle", &["*.gradle"]),
|
||||
("groovy", &["*.groovy", "*.gradle"]),
|
||||
("gzip", &["*.gz", "*.tgz"]),
|
||||
("h", &["*.h", "*.hpp"]),
|
||||
("haml", &["*.haml"]),
|
||||
("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
|
||||
("hbs", &["*.hbs"]),
|
||||
("hs", &["*.hs", "*.lhs"]),
|
||||
("html", &["*.htm", "*.html", "*.ejs"]),
|
||||
("idris", &["*.idr", "*.lidr"]),
|
||||
("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
|
||||
("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
|
||||
("jl", &["*.jl"]),
|
||||
("js", &["*.js", "*.jsx", "*.vue"]),
|
||||
("json", &["*.json", "composer.lock"]),
|
||||
("jsonl", &["*.jsonl"]),
|
||||
("julia", &["*.jl"]),
|
||||
("jupyter", &["*.ipynb", "*.jpynb"]),
|
||||
("kotlin", &["*.kt", "*.kts"]),
|
||||
("less", &["*.less"]),
|
||||
("license", &[
|
||||
// General
|
||||
"COPYING", "COPYING[.-]*",
|
||||
"COPYRIGHT", "COPYRIGHT[.-]*",
|
||||
"EULA", "EULA[.-]*",
|
||||
"licen[cs]e", "licen[cs]e.*",
|
||||
"LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
|
||||
"NOTICE", "NOTICE[.-]*",
|
||||
"PATENTS", "PATENTS[.-]*",
|
||||
"UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
|
||||
// GPL (gpl.txt, etc.)
|
||||
"agpl[.-]*",
|
||||
"gpl[.-]*",
|
||||
"lgpl[.-]*",
|
||||
// Other license-specific (APACHE-2.0.txt, etc.)
|
||||
"AGPL-*[0-9]*",
|
||||
"APACHE-*[0-9]*",
|
||||
"BSD-*[0-9]*",
|
||||
"CC-BY-*",
|
||||
"GFDL-*[0-9]*",
|
||||
"GNU-*[0-9]*",
|
||||
"GPL-*[0-9]*",
|
||||
"LGPL-*[0-9]*",
|
||||
"MIT-*[0-9]*",
|
||||
"MPL-*[0-9]*",
|
||||
"OFL-*[0-9]*",
|
||||
]),
|
||||
("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
|
||||
("lock", &["*.lock", "package-lock.json"]),
|
||||
("log", &["*.log"]),
|
||||
("lua", &["*.lua"]),
|
||||
("lz4", &["*.lz4"]),
|
||||
("lzma", &["*.lzma"]),
|
||||
("m4", &["*.ac", "*.m4"]),
|
||||
("make", &[
|
||||
"[Gg][Nn][Uu]makefile", "[Mm]akefile",
|
||||
"[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
|
||||
"[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
|
||||
"*.mk", "*.mak"
|
||||
]),
|
||||
("mako", &["*.mako", "*.mao"]),
|
||||
("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
|
||||
("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
||||
("matlab", &["*.m"]),
|
||||
("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
||||
("mk", &["mkfile"]),
|
||||
("ml", &["*.ml"]),
|
||||
("msbuild", &[
|
||||
"*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
|
||||
]),
|
||||
("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
|
||||
("nix", &["*.nix"]),
|
||||
("objc", &["*.h", "*.m"]),
|
||||
("objcpp", &["*.h", "*.mm"]),
|
||||
("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
|
||||
("org", &["*.org", "*.org_archive"]),
|
||||
("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
|
||||
("pdf", &["*.pdf"]),
|
||||
("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
|
||||
("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
|
||||
("pod", &["*.pod"]),
|
||||
("postscript", &["*.eps", "*.ps"]),
|
||||
("protobuf", &["*.proto"]),
|
||||
("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
|
||||
("puppet", &["*.erb", "*.pp", "*.rb"]),
|
||||
("purs", &["*.purs"]),
|
||||
("py", &["*.py"]),
|
||||
("qmake", &["*.pro", "*.pri", "*.prf"]),
|
||||
("qml", &["*.qml"]),
|
||||
("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
|
||||
("rdoc", &["*.rdoc"]),
|
||||
("readme", &["README*", "*README"]),
|
||||
("robot", &["*.robot"]),
|
||||
("rst", &["*.rst"]),
|
||||
("ruby", &["Gemfile", "*.gemspec", ".irbrc", "Rakefile", "*.rb"]),
|
||||
("rust", &["*.rs"]),
|
||||
("sass", &["*.sass", "*.scss"]),
|
||||
("scala", &["*.scala", "*.sbt"]),
|
||||
("sh", &[
|
||||
// Portable/misc. init files
|
||||
".login", ".logout", ".profile", "profile",
|
||||
// bash-specific init files
|
||||
".bash_login", "bash_login",
|
||||
".bash_logout", "bash_logout",
|
||||
".bash_profile", "bash_profile",
|
||||
".bashrc", "bashrc", "*.bashrc",
|
||||
// csh-specific init files
|
||||
".cshrc", "*.cshrc",
|
||||
// ksh-specific init files
|
||||
".kshrc", "*.kshrc",
|
||||
// tcsh-specific init files
|
||||
".tcshrc",
|
||||
// zsh-specific init files
|
||||
".zshenv", "zshenv",
|
||||
".zlogin", "zlogin",
|
||||
".zlogout", "zlogout",
|
||||
".zprofile", "zprofile",
|
||||
".zshrc", "zshrc",
|
||||
// Extensions
|
||||
"*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
|
||||
]),
|
||||
("slim", &["*.skim", "*.slim", "*.slime"]),
|
||||
("smarty", &["*.tpl"]),
|
||||
("sml", &["*.sml", "*.sig"]),
|
||||
("soy", &["*.soy"]),
|
||||
("spark", &["*.spark"]),
|
||||
("spec", &["*.spec"]),
|
||||
("sql", &["*.sql", "*.psql"]),
|
||||
("stylus", &["*.styl"]),
|
||||
("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
|
||||
("svg", &["*.svg"]),
|
||||
("swift", &["*.swift"]),
|
||||
("swig", &["*.def", "*.i"]),
|
||||
("systemd", &[
|
||||
"*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
|
||||
"*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
|
||||
"*.timer",
|
||||
]),
|
||||
("taskpaper", &["*.taskpaper"]),
|
||||
("tcl", &["*.tcl"]),
|
||||
("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
|
||||
("textile", &["*.textile"]),
|
||||
("tf", &["*.tf"]),
|
||||
("thrift", &["*.thrift"]),
|
||||
("toml", &["*.toml", "Cargo.lock"]),
|
||||
("ts", &["*.ts", "*.tsx"]),
|
||||
("twig", &["*.twig"]),
|
||||
("txt", &["*.txt"]),
|
||||
("typoscript", &["*.typoscript", "*.ts"]),
|
||||
("vala", &["*.vala"]),
|
||||
("vb", &["*.vb"]),
|
||||
("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
|
||||
("vhdl", &["*.vhd", "*.vhdl"]),
|
||||
("vim", &["*.vim"]),
|
||||
("vimscript", &["*.vim"]),
|
||||
("webidl", &["*.idl", "*.webidl", "*.widl"]),
|
||||
("wiki", &["*.mediawiki", "*.wiki"]),
|
||||
("xml", &[
|
||||
"*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
|
||||
"*.rng", "*.sch", "*.xhtml",
|
||||
]),
|
||||
("xz", &["*.xz", "*.txz"]),
|
||||
("yacc", &["*.y"]),
|
||||
("yaml", &["*.yaml", "*.yml"]),
|
||||
("zig", &["*.zig"]),
|
||||
("zsh", &[
|
||||
".zshenv", "zshenv",
|
||||
".zlogin", "zlogin",
|
||||
".zlogout", "zlogout",
|
||||
".zprofile", "zprofile",
|
||||
".zshrc", "zshrc",
|
||||
"*.zsh",
|
||||
]),
|
||||
("zstd", &["*.zst", "*.zstd"]),
|
||||
];
|
@ -157,7 +157,10 @@ impl Ignore {
|
||||
///
|
||||
/// Note that this can only be called on an `Ignore` matcher with no
|
||||
/// parents (i.e., `is_root` returns `true`). This will panic otherwise.
|
||||
pub fn add_parents<P: AsRef<Path>>(&self, path: P) -> (Ignore, Option<Error>) {
|
||||
pub fn add_parents<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
) -> (Ignore, Option<Error>) {
|
||||
if !self.0.opts.parents
|
||||
&& !self.0.opts.git_ignore
|
||||
&& !self.0.opts.git_exclude
|
||||
@ -218,7 +221,10 @@ impl Ignore {
|
||||
/// returned if it exists.
|
||||
///
|
||||
/// Note that all I/O errors are completely ignored.
|
||||
pub fn add_child<P: AsRef<Path>>(&self, dir: P) -> (Ignore, Option<Error>) {
|
||||
pub fn add_child<P: AsRef<Path>>(
|
||||
&self,
|
||||
dir: P,
|
||||
) -> (Ignore, Option<Error>) {
|
||||
let (ig, err) = self.add_child_path(dir.as_ref());
|
||||
(Ignore(Arc::new(ig)), err)
|
||||
}
|
||||
@ -313,7 +319,8 @@ impl Ignore {
|
||||
/// Returns true if at least one type of ignore rule should be matched.
|
||||
fn has_any_ignore_rules(&self) -> bool {
|
||||
let opts = self.0.opts;
|
||||
let has_custom_ignore_files = !self.0.custom_ignore_filenames.is_empty();
|
||||
let has_custom_ignore_files =
|
||||
!self.0.custom_ignore_filenames.is_empty();
|
||||
let has_explicit_ignores = !self.0.explicit_ignores.is_empty();
|
||||
|
||||
opts.ignore
|
||||
@ -325,7 +332,10 @@ impl Ignore {
|
||||
}
|
||||
|
||||
/// Like `matched`, but works with a directory entry instead.
|
||||
pub fn matched_dir_entry<'a>(&'a self, dent: &DirEntry) -> Match<IgnoreMatch<'a>> {
|
||||
pub fn matched_dir_entry<'a>(
|
||||
&'a self,
|
||||
dent: &DirEntry,
|
||||
) -> Match<IgnoreMatch<'a>> {
|
||||
let m = self.matched(dent.path(), dent.is_dir());
|
||||
if m.is_none() && self.0.opts.hidden && is_hidden(dent) {
|
||||
return Match::Ignore(IgnoreMatch::hidden());
|
||||
@ -337,7 +347,11 @@ impl Ignore {
|
||||
/// ignored or not.
|
||||
///
|
||||
/// The match contains information about its origin.
|
||||
fn matched<'a, P: AsRef<Path>>(&'a self, path: P, is_dir: bool) -> Match<IgnoreMatch<'a>> {
|
||||
fn matched<'a, P: AsRef<Path>>(
|
||||
&'a self,
|
||||
path: P,
|
||||
is_dir: bool,
|
||||
) -> Match<IgnoreMatch<'a>> {
|
||||
// We need to be careful with our path. If it has a leading ./, then
|
||||
// strip it because it causes nothing but trouble.
|
||||
let mut path = path.as_ref();
|
||||
@ -368,7 +382,8 @@ impl Ignore {
|
||||
}
|
||||
}
|
||||
if !self.0.types.is_empty() {
|
||||
let mat = self.0.types.matched(path, is_dir).map(IgnoreMatch::types);
|
||||
let mat =
|
||||
self.0.types.matched(path, is_dir).map(IgnoreMatch::types);
|
||||
if mat.is_ignore() {
|
||||
return mat;
|
||||
} else if mat.is_whitelist() {
|
||||
@ -380,17 +395,20 @@ impl Ignore {
|
||||
|
||||
/// Performs matching only on the ignore files for this directory and
|
||||
/// all parent directories.
|
||||
fn matched_ignore<'a>(&'a self, path: &Path, is_dir: bool) -> Match<IgnoreMatch<'a>> {
|
||||
let (mut m_custom_ignore, mut m_ignore, mut m_gi, mut m_gi_exclude, mut m_explicit) = (
|
||||
Match::None,
|
||||
Match::None,
|
||||
Match::None,
|
||||
Match::None,
|
||||
Match::None,
|
||||
);
|
||||
fn matched_ignore<'a>(
|
||||
&'a self,
|
||||
path: &Path,
|
||||
is_dir: bool,
|
||||
) -> Match<IgnoreMatch<'a>> {
|
||||
let (
|
||||
mut m_custom_ignore,
|
||||
mut m_ignore,
|
||||
mut m_gi,
|
||||
mut m_gi_exclude,
|
||||
mut m_explicit,
|
||||
) = (Match::None, Match::None, Match::None, Match::None, Match::None);
|
||||
let any_git =
|
||||
!self.0.opts.require_git
|
||||
|| self.parents().any(|ig| ig.0.has_git);
|
||||
!self.0.opts.require_git || self.parents().any(|ig| ig.0.has_git);
|
||||
let mut saw_git = false;
|
||||
for ig in self.parents().take_while(|ig| !ig.0.is_absolute_parent) {
|
||||
if m_custom_ignore.is_none() {
|
||||
@ -422,7 +440,9 @@ impl Ignore {
|
||||
if self.0.opts.parents {
|
||||
if let Some(abs_parent_path) = self.absolute_base() {
|
||||
let path = abs_parent_path.join(path);
|
||||
for ig in self.parents().skip_while(|ig| !ig.0.is_absolute_parent) {
|
||||
for ig in
|
||||
self.parents().skip_while(|ig| !ig.0.is_absolute_parent)
|
||||
{
|
||||
if m_custom_ignore.is_none() {
|
||||
m_custom_ignore =
|
||||
ig.0.custom_ignore_matcher
|
||||
@ -575,7 +595,9 @@ impl IgnoreBuilder {
|
||||
is_absolute_parent: true,
|
||||
absolute_base: None,
|
||||
explicit_ignores: Arc::new(self.explicit_ignores.clone()),
|
||||
custom_ignore_filenames: Arc::new(self.custom_ignore_filenames.clone()),
|
||||
custom_ignore_filenames: Arc::new(
|
||||
self.custom_ignore_filenames.clone(),
|
||||
),
|
||||
custom_ignore_matcher: Gitignore::empty(),
|
||||
ignore_matcher: Gitignore::empty(),
|
||||
git_global_matcher: Arc::new(git_global_matcher),
|
||||
@ -622,8 +644,7 @@ impl IgnoreBuilder {
|
||||
&mut self,
|
||||
file_name: S,
|
||||
) -> &mut IgnoreBuilder {
|
||||
self.custom_ignore_filenames
|
||||
.push(file_name.as_ref().to_os_string());
|
||||
self.custom_ignore_filenames.push(file_name.as_ref().to_os_string());
|
||||
self
|
||||
}
|
||||
|
||||
@ -705,7 +726,10 @@ impl IgnoreBuilder {
|
||||
/// Process ignore files case insensitively
|
||||
///
|
||||
/// This is disabled by default.
|
||||
pub fn ignore_case_insensitive(&mut self, yes: bool) -> &mut IgnoreBuilder {
|
||||
pub fn ignore_case_insensitive(
|
||||
&mut self,
|
||||
yes: bool,
|
||||
) -> &mut IgnoreBuilder {
|
||||
self.opts.ignore_case_insensitive = yes;
|
||||
self
|
||||
}
|
||||
@ -850,10 +874,8 @@ mod tests {
|
||||
|
||||
let (gi, err) = Gitignore::new(td.path().join("not-an-ignore"));
|
||||
assert!(err.is_none());
|
||||
let (ig, err) = IgnoreBuilder::new()
|
||||
.add_ignore(gi)
|
||||
.build()
|
||||
.add_child(td.path());
|
||||
let (ig, err) =
|
||||
IgnoreBuilder::new().add_ignore(gi).build().add_child(td.path());
|
||||
assert!(err.is_none());
|
||||
assert!(ig.matched("foo", false).is_ignore());
|
||||
assert!(ig.matched("bar", false).is_whitelist());
|
||||
@ -1125,9 +1147,8 @@ mod tests {
|
||||
mkdirp(git_dir.join("info"));
|
||||
wfile(git_dir.join("info/exclude"), "ignore_me");
|
||||
mkdirp(git_dir.join("worktrees/linked-worktree"));
|
||||
let commondir_path = || {
|
||||
git_dir.join("worktrees/linked-worktree/commondir")
|
||||
};
|
||||
let commondir_path =
|
||||
|| git_dir.join("worktrees/linked-worktree/commondir");
|
||||
mkdirp(td.path().join("linked-worktree"));
|
||||
let worktree_git_dir_abs = format!(
|
||||
"gitdir: {}",
|
||||
|
@ -332,13 +332,10 @@ impl GitignoreBuilder {
|
||||
pub fn build(&self) -> Result<Gitignore, Error> {
|
||||
let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count();
|
||||
let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count();
|
||||
let set =
|
||||
self.builder.build().map_err(|err| {
|
||||
Error::Glob {
|
||||
glob: None,
|
||||
err: err.to_string(),
|
||||
}
|
||||
})?;
|
||||
let set = self
|
||||
.builder
|
||||
.build()
|
||||
.map_err(|err| Error::Glob { glob: None, err: err.to_string() })?;
|
||||
Ok(Gitignore {
|
||||
set: set,
|
||||
root: self.root.clone(),
|
||||
@ -499,18 +496,15 @@ impl GitignoreBuilder {
|
||||
if glob.actual.ends_with("/**") {
|
||||
glob.actual = format!("{}/*", glob.actual);
|
||||
}
|
||||
let parsed =
|
||||
GlobBuilder::new(&glob.actual)
|
||||
.literal_separator(true)
|
||||
.case_insensitive(self.case_insensitive)
|
||||
.backslash_escape(true)
|
||||
.build()
|
||||
.map_err(|err| {
|
||||
Error::Glob {
|
||||
glob: Some(glob.original.clone()),
|
||||
err: err.kind().to_string(),
|
||||
}
|
||||
})?;
|
||||
let parsed = GlobBuilder::new(&glob.actual)
|
||||
.literal_separator(true)
|
||||
.case_insensitive(self.case_insensitive)
|
||||
.backslash_escape(true)
|
||||
.build()
|
||||
.map_err(|err| Error::Glob {
|
||||
glob: Some(glob.original.clone()),
|
||||
err: err.kind().to_string(),
|
||||
})?;
|
||||
self.builder.add(parsed);
|
||||
self.globs.push(glob);
|
||||
Ok(self)
|
||||
@ -599,9 +593,8 @@ fn parse_excludes_file(data: &[u8]) -> Option<PathBuf> {
|
||||
// probably works in more circumstances. I guess we would ideally have
|
||||
// a full INI parser. Yuck.
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(
|
||||
r"(?im)^\s*excludesfile\s*=\s*(.+)\s*$"
|
||||
).unwrap();
|
||||
static ref RE: Regex =
|
||||
Regex::new(r"(?im)^\s*excludesfile\s*=\s*(.+)\s*$").unwrap();
|
||||
};
|
||||
let caps = match RE.captures(data) {
|
||||
None => return None,
|
||||
@ -630,8 +623,8 @@ fn home_dir() -> Option<PathBuf> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
use super::{Gitignore, GitignoreBuilder};
|
||||
use std::path::Path;
|
||||
|
||||
fn gi_from_str<P: AsRef<Path>>(root: P, s: &str) -> Gitignore {
|
||||
let mut builder = GitignoreBuilder::new(root);
|
||||
@ -726,8 +719,11 @@ mod tests {
|
||||
not_ignored!(ignot12, ROOT, "\n\n\n", "foo");
|
||||
not_ignored!(ignot13, ROOT, "foo/**", "foo", true);
|
||||
not_ignored!(
|
||||
ignot14, "./third_party/protobuf", "m4/ltoptions.m4",
|
||||
"./third_party/protobuf/csharp/src/packages/repositories.config");
|
||||
ignot14,
|
||||
"./third_party/protobuf",
|
||||
"m4/ltoptions.m4",
|
||||
"./third_party/protobuf/csharp/src/packages/repositories.config"
|
||||
);
|
||||
not_ignored!(ignot15, ROOT, "!/bar", "foo/bar");
|
||||
not_ignored!(ignot16, ROOT, "*\n!**/", "foo", true);
|
||||
not_ignored!(ignot17, ROOT, "src/*.rs", "src/grep/src/main.rs");
|
||||
@ -771,9 +767,12 @@ mod tests {
|
||||
#[test]
|
||||
fn case_insensitive() {
|
||||
let gi = GitignoreBuilder::new(ROOT)
|
||||
.case_insensitive(true).unwrap()
|
||||
.add_str(None, "*.html").unwrap()
|
||||
.build().unwrap();
|
||||
.case_insensitive(true)
|
||||
.unwrap()
|
||||
.add_str(None, "*.html")
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
assert!(gi.matched("foo.html", false).is_ignore());
|
||||
assert!(gi.matched("foo.HTML", false).is_ignore());
|
||||
assert!(!gi.matched("foo.htm", false).is_ignore());
|
||||
|
@ -66,10 +66,11 @@ use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub use walk::{
|
||||
DirEntry, Walk, WalkBuilder, WalkParallel, WalkState,
|
||||
ParallelVisitorBuilder, ParallelVisitor,
|
||||
DirEntry, ParallelVisitor, ParallelVisitorBuilder, Walk, WalkBuilder,
|
||||
WalkParallel, WalkState,
|
||||
};
|
||||
|
||||
mod default_types;
|
||||
mod dir;
|
||||
pub mod gitignore;
|
||||
pub mod overrides;
|
||||
@ -137,22 +138,16 @@ impl Clone for Error {
|
||||
fn clone(&self) -> Error {
|
||||
match *self {
|
||||
Error::Partial(ref errs) => Error::Partial(errs.clone()),
|
||||
Error::WithLineNumber { line, ref err } => Error::WithLineNumber {
|
||||
line: line,
|
||||
err: err.clone(),
|
||||
},
|
||||
Error::WithPath { ref path, ref err } => Error::WithPath {
|
||||
path: path.clone(),
|
||||
err: err.clone(),
|
||||
},
|
||||
Error::WithDepth { depth, ref err } => Error::WithDepth {
|
||||
depth: depth,
|
||||
err: err.clone(),
|
||||
},
|
||||
Error::Loop {
|
||||
ref ancestor,
|
||||
ref child,
|
||||
} => Error::Loop {
|
||||
Error::WithLineNumber { line, ref err } => {
|
||||
Error::WithLineNumber { line: line, err: err.clone() }
|
||||
}
|
||||
Error::WithPath { ref path, ref err } => {
|
||||
Error::WithPath { path: path.clone(), err: err.clone() }
|
||||
}
|
||||
Error::WithDepth { depth, ref err } => {
|
||||
Error::WithDepth { depth: depth, err: err.clone() }
|
||||
}
|
||||
Error::Loop { ref ancestor, ref child } => Error::Loop {
|
||||
ancestor: ancestor.clone(),
|
||||
child: child.clone(),
|
||||
},
|
||||
@ -160,11 +155,12 @@ impl Clone for Error {
|
||||
Some(e) => Error::Io(io::Error::from_raw_os_error(e)),
|
||||
None => Error::Io(io::Error::new(err.kind(), err.to_string())),
|
||||
},
|
||||
Error::Glob { ref glob, ref err } => Error::Glob {
|
||||
glob: glob.clone(),
|
||||
err: err.clone(),
|
||||
},
|
||||
Error::UnrecognizedFileType(ref err) => Error::UnrecognizedFileType(err.clone()),
|
||||
Error::Glob { ref glob, ref err } => {
|
||||
Error::Glob { glob: glob.clone(), err: err.clone() }
|
||||
}
|
||||
Error::UnrecognizedFileType(ref err) => {
|
||||
Error::UnrecognizedFileType(err.clone())
|
||||
}
|
||||
Error::InvalidDefinition => Error::InvalidDefinition,
|
||||
}
|
||||
}
|
||||
@ -221,19 +217,14 @@ impl Error {
|
||||
|
||||
/// Turn an error into a tagged error with the given depth.
|
||||
fn with_depth(self, depth: usize) -> Error {
|
||||
Error::WithDepth {
|
||||
depth: depth,
|
||||
err: Box::new(self),
|
||||
}
|
||||
Error::WithDepth { depth: depth, err: Box::new(self) }
|
||||
}
|
||||
|
||||
/// Turn an error into a tagged error with the given file path and line
|
||||
/// number. If path is empty, then it is omitted from the error.
|
||||
fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error {
|
||||
let errline = Error::WithLineNumber {
|
||||
line: lineno,
|
||||
err: Box::new(self),
|
||||
};
|
||||
let errline =
|
||||
Error::WithLineNumber { line: lineno, err: Box::new(self) };
|
||||
if path.as_ref().as_os_str().is_empty() {
|
||||
return errline;
|
||||
}
|
||||
@ -255,10 +246,7 @@ impl Error {
|
||||
let path = err.path().map(|p| p.to_path_buf());
|
||||
let mut ig_err = Error::Io(io::Error::from(err));
|
||||
if let Some(path) = path {
|
||||
ig_err = Error::WithPath {
|
||||
path: path,
|
||||
err: Box::new(ig_err),
|
||||
};
|
||||
ig_err = Error::WithPath { path: path, err: Box::new(ig_err) };
|
||||
}
|
||||
ig_err
|
||||
}
|
||||
@ -285,16 +273,18 @@ impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Partial(ref errs) => {
|
||||
let msgs: Vec<String> = errs.iter().map(|err| err.to_string()).collect();
|
||||
let msgs: Vec<String> =
|
||||
errs.iter().map(|err| err.to_string()).collect();
|
||||
write!(f, "{}", msgs.join("\n"))
|
||||
}
|
||||
Error::WithLineNumber { line, ref err } => write!(f, "line {}: {}", line, err),
|
||||
Error::WithPath { ref path, ref err } => write!(f, "{}: {}", path.display(), err),
|
||||
Error::WithLineNumber { line, ref err } => {
|
||||
write!(f, "line {}: {}", line, err)
|
||||
}
|
||||
Error::WithPath { ref path, ref err } => {
|
||||
write!(f, "{}: {}", path.display(), err)
|
||||
}
|
||||
Error::WithDepth { ref err, .. } => err.fmt(f),
|
||||
Error::Loop {
|
||||
ref ancestor,
|
||||
ref child,
|
||||
} => write!(
|
||||
Error::Loop { ref ancestor, ref child } => write!(
|
||||
f,
|
||||
"File system loop found: \
|
||||
{} points to an ancestor {}",
|
||||
@ -302,15 +292,13 @@ impl fmt::Display for Error {
|
||||
ancestor.display()
|
||||
),
|
||||
Error::Io(ref err) => err.fmt(f),
|
||||
Error::Glob {
|
||||
glob: None,
|
||||
ref err,
|
||||
} => write!(f, "{}", err),
|
||||
Error::Glob {
|
||||
glob: Some(ref glob),
|
||||
ref err,
|
||||
} => write!(f, "error parsing glob '{}': {}", glob, err),
|
||||
Error::UnrecognizedFileType(ref ty) => write!(f, "unrecognized file type: {}", ty),
|
||||
Error::Glob { glob: None, ref err } => write!(f, "{}", err),
|
||||
Error::Glob { glob: Some(ref glob), ref err } => {
|
||||
write!(f, "error parsing glob '{}': {}", glob, err)
|
||||
}
|
||||
Error::UnrecognizedFileType(ref ty) => {
|
||||
write!(f, "unrecognized file type: {}", ty)
|
||||
}
|
||||
Error::InvalidDefinition => write!(
|
||||
f,
|
||||
"invalid definition (format is type:glob, e.g., \
|
||||
@ -456,7 +444,8 @@ mod tests {
|
||||
use std::result;
|
||||
|
||||
/// A convenient result type alias.
|
||||
pub type Result<T> = result::Result<T, Box<dyn error::Error + Send + Sync>>;
|
||||
pub type Result<T> =
|
||||
result::Result<T, Box<dyn error::Error + Send + Sync>>;
|
||||
|
||||
macro_rules! err {
|
||||
($($tt:tt)*) => {
|
||||
@ -494,8 +483,9 @@ mod tests {
|
||||
if path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
fs::create_dir_all(&path)
|
||||
.map_err(|e| err!("failed to create {}: {}", path.display(), e))?;
|
||||
fs::create_dir_all(&path).map_err(|e| {
|
||||
err!("failed to create {}: {}", path.display(), e)
|
||||
})?;
|
||||
return Ok(TempDir(path));
|
||||
}
|
||||
Err(err!("failed to create temp dir after {} tries", TRIES))
|
||||
|
@ -115,9 +115,7 @@ impl OverrideBuilder {
|
||||
///
|
||||
/// Matching is done relative to the directory path provided.
|
||||
pub fn new<P: AsRef<Path>>(path: P) -> OverrideBuilder {
|
||||
OverrideBuilder {
|
||||
builder: GitignoreBuilder::new(path),
|
||||
}
|
||||
OverrideBuilder { builder: GitignoreBuilder::new(path) }
|
||||
}
|
||||
|
||||
/// Builds a new override matcher from the globs added so far.
|
||||
@ -240,9 +238,12 @@ mod tests {
|
||||
#[test]
|
||||
fn case_insensitive() {
|
||||
let ov = OverrideBuilder::new(ROOT)
|
||||
.case_insensitive(true).unwrap()
|
||||
.add("*.html").unwrap()
|
||||
.build().unwrap();
|
||||
.case_insensitive(true)
|
||||
.unwrap()
|
||||
.add("*.html")
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
assert!(ov.matched("foo.html", false).is_whitelist());
|
||||
assert!(ov.matched("foo.HTML", false).is_whitelist());
|
||||
assert!(ov.matched("foo.htm", false).is_ignore());
|
||||
@ -251,9 +252,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_case_sensitive() {
|
||||
let ov = OverrideBuilder::new(ROOT)
|
||||
.add("*.html").unwrap()
|
||||
.build().unwrap();
|
||||
let ov =
|
||||
OverrideBuilder::new(ROOT).add("*.html").unwrap().build().unwrap();
|
||||
assert!(ov.matched("foo.html", false).is_whitelist());
|
||||
assert!(ov.matched("foo.HTML", false).is_ignore());
|
||||
assert!(ov.matched("foo.htm", false).is_ignore());
|
||||
|
@ -91,8 +91,8 @@ pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
|
||||
/// the empty string.
|
||||
#[cfg(unix)]
|
||||
pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use memchr::memchr;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
let path = path.as_ref().as_os_str().as_bytes();
|
||||
memchr(b'/', path).is_none()
|
||||
@ -113,8 +113,8 @@ pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
|
||||
pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
|
||||
path: &'a P,
|
||||
) -> Option<&'a OsStr> {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use memchr::memrchr;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
let path = path.as_ref().as_os_str().as_bytes();
|
||||
if path.is_empty() {
|
||||
|
@ -93,243 +93,10 @@ use globset::{GlobBuilder, GlobSet, GlobSetBuilder};
|
||||
use regex::Regex;
|
||||
use thread_local::ThreadLocal;
|
||||
|
||||
use default_types::DEFAULT_TYPES;
|
||||
use pathutil::file_name;
|
||||
use {Error, Match};
|
||||
|
||||
const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
|
||||
("agda", &["*.agda", "*.lagda"]),
|
||||
("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
|
||||
("aidl", &["*.aidl"]),
|
||||
("amake", &["*.mk", "*.bp"]),
|
||||
("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
|
||||
("asm", &["*.asm", "*.s", "*.S"]),
|
||||
("asp", &["*.aspx", "*.aspx.cs", "*.aspx.cs", "*.ascx", "*.ascx.cs", "*.ascx.vb"]),
|
||||
("avro", &["*.avdl", "*.avpr", "*.avsc"]),
|
||||
("awk", &["*.awk"]),
|
||||
("bazel", &["*.bzl", "WORKSPACE", "BUILD", "BUILD.bazel"]),
|
||||
("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
|
||||
("brotli", &["*.br"]),
|
||||
("buildstream", &["*.bst"]),
|
||||
("bzip2", &["*.bz2", "*.tbz2"]),
|
||||
("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
|
||||
("cabal", &["*.cabal"]),
|
||||
("cbor", &["*.cbor"]),
|
||||
("ceylon", &["*.ceylon"]),
|
||||
("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
|
||||
("cmake", &["*.cmake", "CMakeLists.txt"]),
|
||||
("coffeescript", &["*.coffee"]),
|
||||
("creole", &["*.creole"]),
|
||||
("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
|
||||
("cpp", &[
|
||||
"*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh", "*.inl",
|
||||
"*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
|
||||
]),
|
||||
("crystal", &["Projectfile", "*.cr"]),
|
||||
("cs", &["*.cs"]),
|
||||
("csharp", &["*.cs"]),
|
||||
("cshtml", &["*.cshtml"]),
|
||||
("css", &["*.css", "*.scss"]),
|
||||
("csv", &["*.csv"]),
|
||||
("cython", &["*.pyx", "*.pxi", "*.pxd"]),
|
||||
("dart", &["*.dart"]),
|
||||
("d", &["*.d"]),
|
||||
("dhall", &["*.dhall"]),
|
||||
("diff", &["*.patch", "*.diff"]),
|
||||
("docker", &["*Dockerfile*"]),
|
||||
("edn", &["*.edn"]),
|
||||
("elisp", &["*.el"]),
|
||||
("elixir", &["*.ex", "*.eex", "*.exs"]),
|
||||
("elm", &["*.elm"]),
|
||||
("erb", &["*.erb"]),
|
||||
("erlang", &["*.erl", "*.hrl"]),
|
||||
("fidl", &["*.fidl"]),
|
||||
("fish", &["*.fish"]),
|
||||
("fortran", &[
|
||||
"*.f", "*.F", "*.f77", "*.F77", "*.pfo",
|
||||
"*.f90", "*.F90", "*.f95", "*.F95",
|
||||
]),
|
||||
("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
|
||||
("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
|
||||
("gn", &["*.gn", "*.gni"]),
|
||||
("go", &["*.go"]),
|
||||
("gzip", &["*.gz", "*.tgz"]),
|
||||
("groovy", &["*.groovy", "*.gradle"]),
|
||||
("gradle", &["*.gradle"]),
|
||||
("h", &["*.h", "*.hpp"]),
|
||||
("hbs", &["*.hbs"]),
|
||||
("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
|
||||
("haml", &["*.haml"]),
|
||||
("hs", &["*.hs", "*.lhs"]),
|
||||
("html", &["*.htm", "*.html", "*.ejs"]),
|
||||
("idris", &["*.idr", "*.lidr"]),
|
||||
("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
|
||||
("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
|
||||
("js", &[
|
||||
"*.js", "*.jsx", "*.vue",
|
||||
]),
|
||||
("json", &["*.json", "composer.lock"]),
|
||||
("jsonl", &["*.jsonl"]),
|
||||
("julia", &["*.jl"]),
|
||||
("jupyter", &["*.ipynb", "*.jpynb"]),
|
||||
("jl", &["*.jl"]),
|
||||
("kotlin", &["*.kt", "*.kts"]),
|
||||
("less", &["*.less"]),
|
||||
("license", &[
|
||||
// General
|
||||
"COPYING", "COPYING[.-]*",
|
||||
"COPYRIGHT", "COPYRIGHT[.-]*",
|
||||
"EULA", "EULA[.-]*",
|
||||
"licen[cs]e", "licen[cs]e.*",
|
||||
"LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
|
||||
"NOTICE", "NOTICE[.-]*",
|
||||
"PATENTS", "PATENTS[.-]*",
|
||||
"UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
|
||||
// GPL (gpl.txt, etc.)
|
||||
"agpl[.-]*",
|
||||
"gpl[.-]*",
|
||||
"lgpl[.-]*",
|
||||
// Other license-specific (APACHE-2.0.txt, etc.)
|
||||
"AGPL-*[0-9]*",
|
||||
"APACHE-*[0-9]*",
|
||||
"BSD-*[0-9]*",
|
||||
"CC-BY-*",
|
||||
"GFDL-*[0-9]*",
|
||||
"GNU-*[0-9]*",
|
||||
"GPL-*[0-9]*",
|
||||
"LGPL-*[0-9]*",
|
||||
"MIT-*[0-9]*",
|
||||
"MPL-*[0-9]*",
|
||||
"OFL-*[0-9]*",
|
||||
]),
|
||||
("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
|
||||
("lock", &["*.lock", "package-lock.json"]),
|
||||
("log", &["*.log"]),
|
||||
("lua", &["*.lua"]),
|
||||
("lzma", &["*.lzma"]),
|
||||
("lz4", &["*.lz4"]),
|
||||
("m4", &["*.ac", "*.m4"]),
|
||||
("make", &[
|
||||
"[Gg][Nn][Uu]makefile", "[Mm]akefile",
|
||||
"[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
|
||||
"[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
|
||||
"*.mk", "*.mak"
|
||||
]),
|
||||
("mako", &["*.mako", "*.mao"]),
|
||||
("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
||||
("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
|
||||
("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
|
||||
("matlab", &["*.m"]),
|
||||
("mk", &["mkfile"]),
|
||||
("ml", &["*.ml"]),
|
||||
("msbuild", &[
|
||||
"*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets"
|
||||
]),
|
||||
("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
|
||||
("nix", &["*.nix"]),
|
||||
("objc", &["*.h", "*.m"]),
|
||||
("objcpp", &["*.h", "*.mm"]),
|
||||
("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
|
||||
("org", &["*.org", "*.org_archive"]),
|
||||
("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
|
||||
("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
|
||||
("pdf", &["*.pdf"]),
|
||||
("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
|
||||
("pod", &["*.pod"]),
|
||||
("postscript", &["*.eps", "*.ps"]),
|
||||
("protobuf", &["*.proto"]),
|
||||
("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
|
||||
("puppet", &["*.erb", "*.pp", "*.rb"]),
|
||||
("purs", &["*.purs"]),
|
||||
("py", &["*.py"]),
|
||||
("qmake", &["*.pro", "*.pri", "*.prf"]),
|
||||
("qml", &["*.qml"]),
|
||||
("readme", &["README*", "*README"]),
|
||||
("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
|
||||
("rdoc", &["*.rdoc"]),
|
||||
("robot", &["*.robot"]),
|
||||
("rst", &["*.rst"]),
|
||||
("ruby", &["Gemfile", "*.gemspec", ".irbrc", "Rakefile", "*.rb"]),
|
||||
("rust", &["*.rs"]),
|
||||
("sass", &["*.sass", "*.scss"]),
|
||||
("scala", &["*.scala", "*.sbt"]),
|
||||
("sh", &[
|
||||
// Portable/misc. init files
|
||||
".login", ".logout", ".profile", "profile",
|
||||
// bash-specific init files
|
||||
".bash_login", "bash_login",
|
||||
".bash_logout", "bash_logout",
|
||||
".bash_profile", "bash_profile",
|
||||
".bashrc", "bashrc", "*.bashrc",
|
||||
// csh-specific init files
|
||||
".cshrc", "*.cshrc",
|
||||
// ksh-specific init files
|
||||
".kshrc", "*.kshrc",
|
||||
// tcsh-specific init files
|
||||
".tcshrc",
|
||||
// zsh-specific init files
|
||||
".zshenv", "zshenv",
|
||||
".zlogin", "zlogin",
|
||||
".zlogout", "zlogout",
|
||||
".zprofile", "zprofile",
|
||||
".zshrc", "zshrc",
|
||||
// Extensions
|
||||
"*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
|
||||
]),
|
||||
("slim", &["*.skim", "*.slim", "*.slime"]),
|
||||
("smarty", &["*.tpl"]),
|
||||
("sml", &["*.sml", "*.sig"]),
|
||||
("soy", &["*.soy"]),
|
||||
("spark", &["*.spark"]),
|
||||
("spec", &["*.spec"]),
|
||||
("sql", &["*.sql", "*.psql"]),
|
||||
("stylus", &["*.styl"]),
|
||||
("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
|
||||
("svg", &["*.svg"]),
|
||||
("swift", &["*.swift"]),
|
||||
("swig", &["*.def", "*.i"]),
|
||||
("systemd", &[
|
||||
"*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
|
||||
"*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
|
||||
"*.timer",
|
||||
]),
|
||||
("taskpaper", &["*.taskpaper"]),
|
||||
("tcl", &["*.tcl"]),
|
||||
("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
|
||||
("textile", &["*.textile"]),
|
||||
("thrift", &["*.thrift"]),
|
||||
("tf", &["*.tf"]),
|
||||
("ts", &["*.ts", "*.tsx"]),
|
||||
("txt", &["*.txt"]),
|
||||
("toml", &["*.toml", "Cargo.lock"]),
|
||||
("twig", &["*.twig"]),
|
||||
("typoscript", &["*.typoscript", "*.ts"]),
|
||||
("vala", &["*.vala"]),
|
||||
("vb", &["*.vb"]),
|
||||
("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
|
||||
("vhdl", &["*.vhd", "*.vhdl"]),
|
||||
("vim", &["*.vim"]),
|
||||
("vimscript", &["*.vim"]),
|
||||
("wiki", &["*.mediawiki", "*.wiki"]),
|
||||
("webidl", &["*.idl", "*.webidl", "*.widl"]),
|
||||
("xml", &[
|
||||
"*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
|
||||
"*.rng", "*.sch", "*.xhtml",
|
||||
]),
|
||||
("xz", &["*.xz", "*.txz"]),
|
||||
("yacc", &["*.y"]),
|
||||
("yaml", &["*.yaml", "*.yml"]),
|
||||
("zig", &["*.zig"]),
|
||||
("zsh", &[
|
||||
".zshenv", "zshenv",
|
||||
".zlogin", "zlogin",
|
||||
".zlogout", "zlogout",
|
||||
".zprofile", "zprofile",
|
||||
".zshrc", "zshrc",
|
||||
"*.zsh",
|
||||
]),
|
||||
("zstd", &["*.zst", "*.zstd"]),
|
||||
];
|
||||
|
||||
/// Glob represents a single glob in a set of file type definitions.
|
||||
///
|
||||
/// There may be more than one glob for a particular file type.
|
||||
@ -359,7 +126,7 @@ enum GlobInner<'a> {
|
||||
which: usize,
|
||||
/// Whether the selection was negated or not.
|
||||
negated: bool,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Glob<'a> {
|
||||
@ -373,9 +140,7 @@ impl<'a> Glob<'a> {
|
||||
pub fn file_type_def(&self) -> Option<&FileTypeDef> {
|
||||
match self {
|
||||
Glob(GlobInner::UnmatchedIgnore) => None,
|
||||
Glob(GlobInner::Matched { def, .. }) => {
|
||||
Some(def)
|
||||
},
|
||||
Glob(GlobInner::Matched { def, .. }) => Some(def),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -561,10 +326,7 @@ impl TypesBuilder {
|
||||
/// of default type definitions can be added with `add_defaults`, and
|
||||
/// additional type definitions can be added with `select` and `negate`.
|
||||
pub fn new() -> TypesBuilder {
|
||||
TypesBuilder {
|
||||
types: HashMap::new(),
|
||||
selections: vec![],
|
||||
}
|
||||
TypesBuilder { types: HashMap::new(), selections: vec![] }
|
||||
}
|
||||
|
||||
/// Build the current set of file type definitions *and* selections into
|
||||
@ -589,19 +351,18 @@ impl TypesBuilder {
|
||||
GlobBuilder::new(glob)
|
||||
.literal_separator(true)
|
||||
.build()
|
||||
.map_err(|err| {
|
||||
Error::Glob {
|
||||
glob: Some(glob.to_string()),
|
||||
err: err.kind().to_string(),
|
||||
}
|
||||
})?);
|
||||
.map_err(|err| Error::Glob {
|
||||
glob: Some(glob.to_string()),
|
||||
err: err.kind().to_string(),
|
||||
})?,
|
||||
);
|
||||
glob_to_selection.push((isel, iglob));
|
||||
}
|
||||
selections.push(selection.clone().map(move |_| def));
|
||||
}
|
||||
let set = build_set.build().map_err(|err| {
|
||||
Error::Glob { glob: None, err: err.to_string() }
|
||||
})?;
|
||||
let set = build_set
|
||||
.build()
|
||||
.map_err(|err| Error::Glob { glob: None, err: err.to_string() })?;
|
||||
Ok(Types {
|
||||
defs: defs,
|
||||
selections: selections,
|
||||
@ -673,9 +434,14 @@ impl TypesBuilder {
|
||||
return Err(Error::InvalidDefinition);
|
||||
}
|
||||
let (key, glob) = (name.to_string(), glob.to_string());
|
||||
self.types.entry(key).or_insert_with(|| {
|
||||
FileTypeDef { name: name.to_string(), globs: vec![] }
|
||||
}).globs.push(glob);
|
||||
self.types
|
||||
.entry(key)
|
||||
.or_insert_with(|| FileTypeDef {
|
||||
name: name.to_string(),
|
||||
globs: vec![],
|
||||
})
|
||||
.globs
|
||||
.push(glob);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -702,7 +468,10 @@ impl TypesBuilder {
|
||||
3 => {
|
||||
let name = parts[0];
|
||||
let types_string = parts[2];
|
||||
if name.is_empty() || parts[1] != "include" || types_string.is_empty() {
|
||||
if name.is_empty()
|
||||
|| parts[1] != "include"
|
||||
|| types_string.is_empty()
|
||||
{
|
||||
return Err(Error::InvalidDefinition);
|
||||
}
|
||||
let types = types_string.split(',');
|
||||
@ -712,14 +481,15 @@ impl TypesBuilder {
|
||||
return Err(Error::InvalidDefinition);
|
||||
}
|
||||
for type_name in types {
|
||||
let globs = self.types.get(type_name).unwrap().globs.clone();
|
||||
let globs =
|
||||
self.types.get(type_name).unwrap().globs.clone();
|
||||
for glob in globs {
|
||||
self.add(name, &glob)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Error::InvalidDefinition)
|
||||
_ => Err(Error::InvalidDefinition),
|
||||
}
|
||||
}
|
||||
|
||||
@ -776,7 +546,7 @@ mod tests {
|
||||
"rust:*.rs",
|
||||
"js:*.js",
|
||||
"foo:*.{rs,foo}",
|
||||
"combo:include:html,rust"
|
||||
"combo:include:html,rust",
|
||||
]
|
||||
}
|
||||
|
||||
@ -810,7 +580,7 @@ mod tests {
|
||||
"combo:include:html,python",
|
||||
// Bad format
|
||||
"combo:foobar:html,rust",
|
||||
""
|
||||
"",
|
||||
];
|
||||
for def in bad_defs {
|
||||
assert!(btypes.add_def(def).is_err());
|
||||
|
@ -102,24 +102,15 @@ impl DirEntry {
|
||||
}
|
||||
|
||||
fn new_stdin() -> DirEntry {
|
||||
DirEntry {
|
||||
dent: DirEntryInner::Stdin,
|
||||
err: None,
|
||||
}
|
||||
DirEntry { dent: DirEntryInner::Stdin, err: None }
|
||||
}
|
||||
|
||||
fn new_walkdir(dent: walkdir::DirEntry, err: Option<Error>) -> DirEntry {
|
||||
DirEntry {
|
||||
dent: DirEntryInner::Walkdir(dent),
|
||||
err: err,
|
||||
}
|
||||
DirEntry { dent: DirEntryInner::Walkdir(dent), err: err }
|
||||
}
|
||||
|
||||
fn new_raw(dent: DirEntryRaw, err: Option<Error>) -> DirEntry {
|
||||
DirEntry {
|
||||
dent: DirEntryInner::Raw(dent),
|
||||
err: err,
|
||||
}
|
||||
DirEntry { dent: DirEntryInner::Raw(dent), err: err }
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,9 +176,9 @@ impl DirEntryInner {
|
||||
));
|
||||
Err(err.with_path("<stdin>"))
|
||||
}
|
||||
Walkdir(ref x) => x
|
||||
.metadata()
|
||||
.map_err(|err| Error::Io(io::Error::from(err)).with_path(x.path())),
|
||||
Walkdir(ref x) => x.metadata().map_err(|err| {
|
||||
Error::Io(io::Error::from(err)).with_path(x.path())
|
||||
}),
|
||||
Raw(ref x) => x.metadata(),
|
||||
}
|
||||
}
|
||||
@ -314,9 +305,7 @@ impl DirEntryRaw {
|
||||
}
|
||||
|
||||
fn file_name(&self) -> &OsStr {
|
||||
self.path
|
||||
.file_name()
|
||||
.unwrap_or_else(|| self.path.as_os_str())
|
||||
self.path.file_name().unwrap_or_else(|| self.path.as_os_str())
|
||||
}
|
||||
|
||||
fn depth(&self) -> usize {
|
||||
@ -328,13 +317,13 @@ impl DirEntryRaw {
|
||||
self.ino
|
||||
}
|
||||
|
||||
fn from_entry(depth: usize, ent: &fs::DirEntry) -> Result<DirEntryRaw, Error> {
|
||||
fn from_entry(
|
||||
depth: usize,
|
||||
ent: &fs::DirEntry,
|
||||
) -> Result<DirEntryRaw, Error> {
|
||||
let ty = ent.file_type().map_err(|err| {
|
||||
let err = Error::Io(io::Error::from(err)).with_path(ent.path());
|
||||
Error::WithDepth {
|
||||
depth: depth,
|
||||
err: Box::new(err),
|
||||
}
|
||||
Error::WithDepth { depth: depth, err: Box::new(err) }
|
||||
})?;
|
||||
DirEntryRaw::from_entry_os(depth, ent, ty)
|
||||
}
|
||||
@ -347,10 +336,7 @@ impl DirEntryRaw {
|
||||
) -> Result<DirEntryRaw, Error> {
|
||||
let md = ent.metadata().map_err(|err| {
|
||||
let err = Error::Io(io::Error::from(err)).with_path(ent.path());
|
||||
Error::WithDepth {
|
||||
depth: depth,
|
||||
err: Box::new(err),
|
||||
}
|
||||
Error::WithDepth { depth: depth, err: Box::new(err) }
|
||||
})?;
|
||||
Ok(DirEntryRaw {
|
||||
path: ent.path(),
|
||||
@ -392,8 +378,13 @@ impl DirEntryRaw {
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn from_path(depth: usize, pb: PathBuf, link: bool) -> Result<DirEntryRaw, Error> {
|
||||
let md = fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
||||
fn from_path(
|
||||
depth: usize,
|
||||
pb: PathBuf,
|
||||
link: bool,
|
||||
) -> Result<DirEntryRaw, Error> {
|
||||
let md =
|
||||
fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
||||
Ok(DirEntryRaw {
|
||||
path: pb,
|
||||
ty: md.file_type(),
|
||||
@ -404,10 +395,15 @@ impl DirEntryRaw {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn from_path(depth: usize, pb: PathBuf, link: bool) -> Result<DirEntryRaw, Error> {
|
||||
fn from_path(
|
||||
depth: usize,
|
||||
pb: PathBuf,
|
||||
link: bool,
|
||||
) -> Result<DirEntryRaw, Error> {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
||||
let md = fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
||||
let md =
|
||||
fs::metadata(&pb).map_err(|err| Error::Io(err).with_path(&pb))?;
|
||||
Ok(DirEntryRaw {
|
||||
path: pb,
|
||||
ty: md.file_type(),
|
||||
@ -419,7 +415,11 @@ impl DirEntryRaw {
|
||||
|
||||
// Placeholder implementation to allow compiling on non-standard platforms (e.g. wasm32).
|
||||
#[cfg(not(any(windows, unix)))]
|
||||
fn from_path(depth: usize, pb: PathBuf, link: bool) -> Result<DirEntryRaw, Error> {
|
||||
fn from_path(
|
||||
depth: usize,
|
||||
pb: PathBuf,
|
||||
link: bool,
|
||||
) -> Result<DirEntryRaw, Error> {
|
||||
Err(Error::Io(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"unsupported platform",
|
||||
@ -490,7 +490,9 @@ pub struct WalkBuilder {
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Sorter {
|
||||
ByName(Arc<dyn Fn(&OsStr, &OsStr) -> cmp::Ordering + Send + Sync + 'static>),
|
||||
ByName(
|
||||
Arc<dyn Fn(&OsStr, &OsStr) -> cmp::Ordering + Send + Sync + 'static>,
|
||||
),
|
||||
ByPath(Arc<dyn Fn(&Path, &Path) -> cmp::Ordering + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
@ -550,10 +552,14 @@ impl WalkBuilder {
|
||||
if let Some(ref sorter) = sorter {
|
||||
match sorter.clone() {
|
||||
Sorter::ByName(cmp) => {
|
||||
wd = wd.sort_by(move |a, b| cmp(a.file_name(), b.file_name()));
|
||||
wd = wd.sort_by(move |a, b| {
|
||||
cmp(a.file_name(), b.file_name())
|
||||
});
|
||||
}
|
||||
Sorter::ByPath(cmp) => {
|
||||
wd = wd.sort_by(move |a, b| cmp(a.path(), b.path()));
|
||||
wd = wd.sort_by(move |a, b| {
|
||||
cmp(a.path(), b.path())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1012,11 +1018,7 @@ enum WalkEvent {
|
||||
|
||||
impl From<WalkDir> for WalkEventIter {
|
||||
fn from(it: WalkDir) -> WalkEventIter {
|
||||
WalkEventIter {
|
||||
depth: 0,
|
||||
it: it.into_iter(),
|
||||
next: None,
|
||||
}
|
||||
WalkEventIter { depth: 0, it: it.into_iter(), next: None }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1085,8 +1087,8 @@ pub trait ParallelVisitorBuilder<'s> {
|
||||
fn build(&mut self) -> Box<dyn ParallelVisitor + 's>;
|
||||
}
|
||||
|
||||
impl<'a, 's, P: ParallelVisitorBuilder<'s>>
|
||||
ParallelVisitorBuilder<'s> for &'a mut P
|
||||
impl<'a, 's, P: ParallelVisitorBuilder<'s>> ParallelVisitorBuilder<'s>
|
||||
for &'a mut P
|
||||
{
|
||||
fn build(&mut self) -> Box<dyn ParallelVisitor + 's> {
|
||||
(**self).build()
|
||||
@ -1109,16 +1111,17 @@ struct FnBuilder<F> {
|
||||
builder: F,
|
||||
}
|
||||
|
||||
impl<'s, F: FnMut() -> FnVisitor<'s>> ParallelVisitorBuilder<'s> for FnBuilder<F> {
|
||||
impl<'s, F: FnMut() -> FnVisitor<'s>> ParallelVisitorBuilder<'s>
|
||||
for FnBuilder<F>
|
||||
{
|
||||
fn build(&mut self) -> Box<dyn ParallelVisitor + 's> {
|
||||
let visitor = (self.builder)();
|
||||
Box::new(FnVisitorImp { visitor })
|
||||
}
|
||||
}
|
||||
|
||||
type FnVisitor<'s> = Box<
|
||||
dyn FnMut(Result<DirEntry, Error>) -> WalkState + Send + 's
|
||||
>;
|
||||
type FnVisitor<'s> =
|
||||
Box<dyn FnMut(Result<DirEntry, Error>) -> WalkState + Send + 's>;
|
||||
|
||||
struct FnVisitorImp<'s> {
|
||||
visitor: FnVisitor<'s>,
|
||||
@ -1263,7 +1266,8 @@ impl WalkParallel {
|
||||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
}).unwrap(); // Pass along panics from threads
|
||||
})
|
||||
.unwrap(); // Pass along panics from threads
|
||||
}
|
||||
|
||||
fn threads(&self) -> usize {
|
||||
@ -1488,7 +1492,9 @@ impl<'s> Worker<'s> {
|
||||
let fs_dent = match result {
|
||||
Ok(fs_dent) => fs_dent,
|
||||
Err(err) => {
|
||||
return self.visitor.visit(Err(Error::from(err).with_depth(depth)));
|
||||
return self
|
||||
.visitor
|
||||
.visit(Err(Error::from(err).with_depth(depth)));
|
||||
}
|
||||
};
|
||||
let mut dent = match DirEntryRaw::from_entry(depth, &fs_dent) {
|
||||
@ -1522,15 +1528,16 @@ impl<'s> Worker<'s> {
|
||||
}
|
||||
}
|
||||
let should_skip_path = should_skip_entry(ig, &dent);
|
||||
let should_skip_filesize = if self.max_filesize.is_some() && !dent.is_dir() {
|
||||
skip_filesize(
|
||||
self.max_filesize.unwrap(),
|
||||
dent.path(),
|
||||
&dent.metadata().ok(),
|
||||
)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let should_skip_filesize =
|
||||
if self.max_filesize.is_some() && !dent.is_dir() {
|
||||
skip_filesize(
|
||||
self.max_filesize.unwrap(),
|
||||
dent.path(),
|
||||
&dent.metadata().ok(),
|
||||
)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !should_skip_path && !should_skip_filesize {
|
||||
self.tx
|
||||
@ -1579,11 +1586,12 @@ impl<'s> Worker<'s> {
|
||||
return None;
|
||||
}
|
||||
// Wait for next `Work` or `Quit` message.
|
||||
value = Ok(self.rx.recv().expect(
|
||||
"channel disconnected while worker is alive",
|
||||
));
|
||||
value = Ok(self
|
||||
.rx
|
||||
.recv()
|
||||
.expect("channel disconnected while worker is alive"));
|
||||
self.resume();
|
||||
},
|
||||
}
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
unreachable!("channel disconnected while worker is alive");
|
||||
}
|
||||
@ -1619,18 +1627,11 @@ fn check_symlink_loop(
|
||||
child_depth: usize,
|
||||
) -> Result<(), Error> {
|
||||
let hchild = Handle::from_path(child_path).map_err(|err| {
|
||||
Error::from(err)
|
||||
.with_path(child_path)
|
||||
.with_depth(child_depth)
|
||||
Error::from(err).with_path(child_path).with_depth(child_depth)
|
||||
})?;
|
||||
for ig in ig_parent
|
||||
.parents()
|
||||
.take_while(|ig| !ig.is_absolute_parent())
|
||||
{
|
||||
for ig in ig_parent.parents().take_while(|ig| !ig.is_absolute_parent()) {
|
||||
let h = Handle::from_path(ig.path()).map_err(|err| {
|
||||
Error::from(err)
|
||||
.with_path(child_path)
|
||||
.with_depth(child_depth)
|
||||
Error::from(err).with_path(child_path).with_depth(child_depth)
|
||||
})?;
|
||||
if hchild == h {
|
||||
return Err(Error::Loop {
|
||||
@ -1645,7 +1646,11 @@ fn check_symlink_loop(
|
||||
|
||||
// Before calling this function, make sure that you ensure that is really
|
||||
// necessary as the arguments imply a file stat.
|
||||
fn skip_filesize(max_filesize: u64, path: &Path, ent: &Option<Metadata>) -> bool {
|
||||
fn skip_filesize(
|
||||
max_filesize: u64,
|
||||
path: &Path,
|
||||
ent: &Option<Metadata>,
|
||||
) -> bool {
|
||||
let filesize = match *ent {
|
||||
Some(ref md) => Some(md.len()),
|
||||
None => None,
|
||||
@ -1743,7 +1748,8 @@ fn walkdir_is_dir(dent: &walkdir::DirEntry) -> bool {
|
||||
/// Returns true if and only if the given path is on the same device as the
|
||||
/// given root device.
|
||||
fn is_same_file_system(root_device: u64, path: &Path) -> Result<bool, Error> {
|
||||
let dent_device = device_num(path).map_err(|err| Error::Io(err).with_path(path))?;
|
||||
let dent_device =
|
||||
device_num(path).map_err(|err| Error::Io(err).with_path(path))?;
|
||||
Ok(root_device == dent_device)
|
||||
}
|
||||
|
||||
@ -1825,7 +1831,10 @@ mod tests {
|
||||
paths
|
||||
}
|
||||
|
||||
fn walk_collect_parallel(prefix: &Path, builder: &WalkBuilder) -> Vec<String> {
|
||||
fn walk_collect_parallel(
|
||||
prefix: &Path,
|
||||
builder: &WalkBuilder,
|
||||
) -> Vec<String> {
|
||||
let mut paths = vec![];
|
||||
for dent in walk_collect_entries_parallel(builder) {
|
||||
let path = dent.path().strip_prefix(prefix).unwrap();
|
||||
@ -2079,7 +2088,9 @@ mod tests {
|
||||
assert_eq!(1, dents.len());
|
||||
assert!(!dents[0].path_is_symlink());
|
||||
|
||||
let dents = walk_collect_entries_parallel(&WalkBuilder::new(td.path().join("foo")));
|
||||
let dents = walk_collect_entries_parallel(&WalkBuilder::new(
|
||||
td.path().join("foo"),
|
||||
));
|
||||
assert_eq!(1, dents.len());
|
||||
assert!(!dents[0].path_is_symlink());
|
||||
}
|
||||
|
@ -55,7 +55,6 @@ fn test_files_in_root() {
|
||||
assert!(m("ROOT/file_root_33").is_none());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_files_in_deep() {
|
||||
let gitignore = get_gitignore();
|
||||
@ -88,7 +87,6 @@ fn test_files_in_deep() {
|
||||
assert!(m("ROOT/parent_dir/file_deep_33").is_none());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_dirs_in_root() {
|
||||
let gitignore = get_gitignore();
|
||||
@ -193,7 +191,6 @@ fn test_dirs_in_root() {
|
||||
assert!(m("ROOT/dir_root_33/child_dir/file", false).is_ignore());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_dirs_in_deep() {
|
||||
let gitignore = get_gitignore();
|
||||
@ -205,17 +202,13 @@ fn test_dirs_in_deep() {
|
||||
assert!(m("ROOT/parent_dir/dir_deep_00", true).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_00/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_00/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir/file", false).is_ignore());
|
||||
|
||||
// 01
|
||||
assert!(m("ROOT/parent_dir/dir_deep_01", true).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_01/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore());
|
||||
|
||||
// 02
|
||||
assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none());
|
||||
@ -257,67 +250,51 @@ fn test_dirs_in_deep() {
|
||||
assert!(m("ROOT/parent_dir/dir_deep_20", true).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_20/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_20/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir/file", false).is_ignore());
|
||||
|
||||
// 21
|
||||
assert!(m("ROOT/parent_dir/dir_deep_21", true).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_21/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_21/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir/file", false).is_ignore());
|
||||
|
||||
// 22
|
||||
// dir itself doesn't match
|
||||
assert!(m("ROOT/parent_dir/dir_deep_22", true).is_none());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_22/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_22/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir/file", false).is_ignore());
|
||||
|
||||
// 23
|
||||
// dir itself doesn't match
|
||||
assert!(m("ROOT/parent_dir/dir_deep_23", true).is_none());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_23/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_23/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir/file", false).is_ignore());
|
||||
|
||||
// 30
|
||||
assert!(m("ROOT/parent_dir/dir_deep_30", true).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_30/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_30/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir/file", false).is_ignore());
|
||||
|
||||
// 31
|
||||
assert!(m("ROOT/parent_dir/dir_deep_31", true).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_31/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_31/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir/file", false).is_ignore());
|
||||
|
||||
// 32
|
||||
// dir itself doesn't match
|
||||
assert!(m("ROOT/parent_dir/dir_deep_32", true).is_none());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_32/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_32/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir/file", false).is_ignore());
|
||||
|
||||
// 33
|
||||
// dir itself doesn't match
|
||||
assert!(m("ROOT/parent_dir/dir_deep_33", true).is_none());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_33/file", false).is_ignore());
|
||||
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir", true).is_ignore());
|
||||
assert!(
|
||||
m("ROOT/parent_dir/dir_deep_33/child_dir/file", false).is_ignore()
|
||||
);
|
||||
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir/file", false).is_ignore());
|
||||
}
|
||||
|
@ -1 +1,2 @@
|
||||
disable_all_formatting = true
|
||||
max_width = 79
|
||||
use_small_heuristics = "max"
|
||||
|
1092
src/app.rs
1092
src/app.rs
File diff suppressed because it is too large
Load Diff
234
src/args.rs
234
src/args.rs
@ -17,11 +17,8 @@ use grep::pcre2::{
|
||||
RegexMatcherBuilder as PCRE2RegexMatcherBuilder,
|
||||
};
|
||||
use grep::printer::{
|
||||
ColorSpecs, Stats,
|
||||
JSON, JSONBuilder,
|
||||
Standard, StandardBuilder,
|
||||
Summary, SummaryBuilder, SummaryKind,
|
||||
default_color_specs,
|
||||
default_color_specs, ColorSpecs, JSONBuilder, Standard, StandardBuilder,
|
||||
Stats, Summary, SummaryBuilder, SummaryKind, JSON,
|
||||
};
|
||||
use grep::regex::{
|
||||
RegexMatcher as RustRegexMatcher,
|
||||
@ -36,15 +33,12 @@ use ignore::{Walk, WalkBuilder, WalkParallel};
|
||||
use log;
|
||||
use num_cpus;
|
||||
use regex;
|
||||
use termcolor::{
|
||||
WriteColor,
|
||||
BufferWriter, ColorChoice,
|
||||
};
|
||||
use termcolor::{BufferWriter, ColorChoice, WriteColor};
|
||||
|
||||
use crate::app;
|
||||
use crate::config;
|
||||
use crate::logger::Logger;
|
||||
use crate::messages::{set_messages, set_ignore_messages};
|
||||
use crate::messages::{set_ignore_messages, set_messages};
|
||||
use crate::path_printer::{PathPrinter, PathPrinterBuilder};
|
||||
use crate::search::{
|
||||
PatternMatcher, Printer, SearchWorker, SearchWorkerBuilder,
|
||||
@ -84,11 +78,9 @@ impl Command {
|
||||
|
||||
match *self {
|
||||
Search | SearchParallel => true,
|
||||
| SearchNever
|
||||
| Files
|
||||
| FilesParallel
|
||||
| Types
|
||||
| PCRE2Version => false,
|
||||
SearchNever | Files | FilesParallel | Types | PCRE2Version => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -210,15 +202,12 @@ impl Args {
|
||||
.printer_standard(self.paths(), wtr, separator_search)
|
||||
.map(Printer::Standard)
|
||||
}
|
||||
OutputKind::Summary => {
|
||||
self.matches()
|
||||
.printer_summary(self.paths(), wtr)
|
||||
.map(Printer::Summary)
|
||||
}
|
||||
OutputKind::Summary => self
|
||||
.matches()
|
||||
.printer_summary(self.paths(), wtr)
|
||||
.map(Printer::Summary),
|
||||
OutputKind::JSON => {
|
||||
self.matches()
|
||||
.printer_json(wtr)
|
||||
.map(Printer::JSON)
|
||||
self.matches().printer_json(wtr).map(Printer::JSON)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,29 +441,23 @@ impl SortBy {
|
||||
}
|
||||
SortByKind::LastModified => {
|
||||
builder.sort_by_file_path(move |a, b| {
|
||||
sort_by_metadata_time(
|
||||
a, b,
|
||||
self.reverse,
|
||||
|md| md.modified(),
|
||||
)
|
||||
sort_by_metadata_time(a, b, self.reverse, |md| {
|
||||
md.modified()
|
||||
})
|
||||
});
|
||||
}
|
||||
SortByKind::LastAccessed => {
|
||||
builder.sort_by_file_path(move |a, b| {
|
||||
sort_by_metadata_time(
|
||||
a, b,
|
||||
self.reverse,
|
||||
|md| md.accessed(),
|
||||
)
|
||||
sort_by_metadata_time(a, b, self.reverse, |md| {
|
||||
md.accessed()
|
||||
})
|
||||
});
|
||||
}
|
||||
SortByKind::Created => {
|
||||
builder.sort_by_file_path(move |a, b| {
|
||||
sort_by_metadata_time(
|
||||
a, b,
|
||||
self.reverse,
|
||||
|md| md.created(),
|
||||
)
|
||||
sort_by_metadata_time(a, b, self.reverse, |md| {
|
||||
md.created()
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -520,7 +503,7 @@ impl EncodingMode {
|
||||
fn has_explicit_encoding(&self) -> bool {
|
||||
match self {
|
||||
EncodingMode::Some(_) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -568,13 +551,12 @@ impl ArgMatches {
|
||||
let patterns = self.patterns()?;
|
||||
let matcher = self.matcher(&patterns)?;
|
||||
let mut paths = self.paths();
|
||||
let using_default_path =
|
||||
if paths.is_empty() {
|
||||
paths.push(self.path_default());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let using_default_path = if paths.is_empty() {
|
||||
paths.push(self.path_default());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
Ok(Args(Arc::new(ArgsImp {
|
||||
matches: self,
|
||||
patterns: patterns,
|
||||
@ -605,7 +587,8 @@ impl ArgMatches {
|
||||
Err(err) => err,
|
||||
};
|
||||
log::debug!(
|
||||
"error building Rust regex in hybrid mode:\n{}", rust_err,
|
||||
"error building Rust regex in hybrid mode:\n{}",
|
||||
rust_err,
|
||||
);
|
||||
let pcre_err = match self.matcher_pcre2(patterns) {
|
||||
Ok(matcher) => return Ok(PatternMatcher::PCRE2(matcher)),
|
||||
@ -616,7 +599,10 @@ impl ArgMatches {
|
||||
engine or with PCRE2.\n\n\
|
||||
default regex engine error:\n{}\n{}\n{}\n\n\
|
||||
PCRE2 regex engine error:\n{}",
|
||||
"~".repeat(79), rust_err, "~".repeat(79), pcre_err,
|
||||
"~".repeat(79),
|
||||
rust_err,
|
||||
"~".repeat(79),
|
||||
pcre_err,
|
||||
)))
|
||||
} else {
|
||||
let matcher = match self.matcher_rust(patterns) {
|
||||
@ -660,14 +646,10 @@ impl ArgMatches {
|
||||
if self.is_present("multiline") {
|
||||
builder.dot_matches_new_line(self.is_present("multiline-dotall"));
|
||||
if self.is_present("crlf") {
|
||||
builder
|
||||
.crlf(true)
|
||||
.line_terminator(None);
|
||||
builder.crlf(true).line_terminator(None);
|
||||
}
|
||||
} else {
|
||||
builder
|
||||
.line_terminator(Some(b'\n'))
|
||||
.dot_matches_new_line(false);
|
||||
builder.line_terminator(Some(b'\n')).dot_matches_new_line(false);
|
||||
if self.is_present("crlf") {
|
||||
builder.crlf(true);
|
||||
}
|
||||
@ -686,12 +668,11 @@ impl ArgMatches {
|
||||
if let Some(limit) = self.dfa_size_limit()? {
|
||||
builder.dfa_size_limit(limit);
|
||||
}
|
||||
let res =
|
||||
if self.is_present("fixed-strings") {
|
||||
builder.build_literals(patterns)
|
||||
} else {
|
||||
builder.build(&patterns.join("|"))
|
||||
};
|
||||
let res = if self.is_present("fixed-strings") {
|
||||
builder.build_literals(patterns)
|
||||
} else {
|
||||
builder.build(&patterns.join("|"))
|
||||
};
|
||||
match res {
|
||||
Ok(m) => Ok(m),
|
||||
Err(err) => Err(From::from(suggest_multiline(err.to_string()))),
|
||||
@ -718,7 +699,7 @@ impl ArgMatches {
|
||||
// The PCRE2 docs say that 32KB is the default, and that 1MB
|
||||
// should be big enough for anything. But let's crank it to
|
||||
// 10MB.
|
||||
.max_jit_stack_size(Some(10 * (1<<20)));
|
||||
.max_jit_stack_size(Some(10 * (1 << 20)));
|
||||
}
|
||||
if self.unicode() {
|
||||
builder.utf(true).ucp(true);
|
||||
@ -822,14 +803,13 @@ impl ArgMatches {
|
||||
/// Build a searcher from the command line parameters.
|
||||
fn searcher(&self, paths: &[PathBuf]) -> Result<Searcher> {
|
||||
let (ctx_before, ctx_after) = self.contexts()?;
|
||||
let line_term =
|
||||
if self.is_present("crlf") {
|
||||
LineTerminator::crlf()
|
||||
} else if self.is_present("null-data") {
|
||||
LineTerminator::byte(b'\x00')
|
||||
} else {
|
||||
LineTerminator::byte(b'\n')
|
||||
};
|
||||
let line_term = if self.is_present("crlf") {
|
||||
LineTerminator::crlf()
|
||||
} else if self.is_present("null-data") {
|
||||
LineTerminator::byte(b'\x00')
|
||||
} else {
|
||||
LineTerminator::byte(b'\n')
|
||||
};
|
||||
let mut builder = SearcherBuilder::new();
|
||||
builder
|
||||
.line_terminator(line_term)
|
||||
@ -902,12 +882,9 @@ impl ArgMatches {
|
||||
/// 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.is_present("null-data");
|
||||
let none = self.is_present("text") || self.is_present("null-data");
|
||||
let convert =
|
||||
self.is_present("binary")
|
||||
|| self.unrestricted_count() >= 3;
|
||||
self.is_present("binary") || self.unrestricted_count() >= 3;
|
||||
if none {
|
||||
BinaryDetection::none()
|
||||
} else if convert {
|
||||
@ -925,9 +902,7 @@ impl ArgMatches {
|
||||
/// 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");
|
||||
let none = self.is_present("text") || self.is_present("null-data");
|
||||
if none {
|
||||
BinaryDetection::none()
|
||||
} else {
|
||||
@ -955,8 +930,8 @@ impl ArgMatches {
|
||||
/// case is disabled.
|
||||
fn case_smart(&self) -> bool {
|
||||
self.is_present("smart-case")
|
||||
&& !self.is_present("ignore-case")
|
||||
&& !self.is_present("case-sensitive")
|
||||
&& !self.is_present("ignore-case")
|
||||
&& !self.is_present("case-sensitive")
|
||||
}
|
||||
|
||||
/// Returns the user's color choice based on command line parameters and
|
||||
@ -1012,11 +987,7 @@ impl ArgMatches {
|
||||
let after = self.usize_of("after-context")?.unwrap_or(0);
|
||||
let before = self.usize_of("before-context")?.unwrap_or(0);
|
||||
let both = self.usize_of("context")?.unwrap_or(0);
|
||||
Ok(if both > 0 {
|
||||
(both, both)
|
||||
} else {
|
||||
(before, after)
|
||||
})
|
||||
Ok(if both > 0 { (both, both) } else { (before, after) })
|
||||
}
|
||||
|
||||
/// Returns the unescaped context separator in UTF-8 bytes.
|
||||
@ -1111,8 +1082,8 @@ impl ArgMatches {
|
||||
false
|
||||
} else {
|
||||
cli::is_tty_stdout()
|
||||
|| self.is_present("heading")
|
||||
|| self.is_present("pretty")
|
||||
|| self.is_present("heading")
|
||||
|| self.is_present("pretty")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1168,10 +1139,10 @@ impl ArgMatches {
|
||||
// tty for human consumption, except for one interesting case: when
|
||||
// we're only searching stdin. This makes pipelines work as expected.
|
||||
(cli::is_tty_stdout() && !self.is_only_stdin(paths))
|
||||
|| self.is_present("line-number")
|
||||
|| self.is_present("column")
|
||||
|| self.is_present("pretty")
|
||||
|| self.is_present("vimgrep")
|
||||
|| self.is_present("line-number")
|
||||
|| self.is_present("column")
|
||||
|| self.is_present("pretty")
|
||||
|| self.is_present("vimgrep")
|
||||
}
|
||||
|
||||
/// The maximum number of columns allowed on each line.
|
||||
@ -1264,8 +1235,7 @@ impl ArgMatches {
|
||||
}
|
||||
|
||||
let (count, count_matches) = self.counts();
|
||||
let summary =
|
||||
count
|
||||
let summary = count
|
||||
|| count_matches
|
||||
|| self.is_present("files-with-matches")
|
||||
|| self.is_present("files-without-match");
|
||||
@ -1325,10 +1295,10 @@ impl ArgMatches {
|
||||
/// be used when ripgrep is not otherwise given at least one file path
|
||||
/// as a positional argument.
|
||||
fn path_default(&self) -> PathBuf {
|
||||
let file_is_stdin = self.values_of_os("file")
|
||||
let file_is_stdin = self
|
||||
.values_of_os("file")
|
||||
.map_or(false, |mut files| files.any(|f| f == "-"));
|
||||
let search_cwd =
|
||||
!cli::is_readable_stdin()
|
||||
let search_cwd = !cli::is_readable_stdin()
|
||||
|| (self.is_present("file") && file_is_stdin)
|
||||
|| self.is_present("files")
|
||||
|| self.is_present("type-list")
|
||||
@ -1357,8 +1327,8 @@ impl ArgMatches {
|
||||
the given separator is {} bytes: {}\n\
|
||||
In some shells on Windows '/' is automatically \
|
||||
expanded. Use '//' instead.",
|
||||
sep.len(),
|
||||
cli::escape(&sep),
|
||||
sep.len(),
|
||||
cli::escape(&sep),
|
||||
)))
|
||||
} else {
|
||||
Ok(Some(sep[0]))
|
||||
@ -1408,14 +1378,16 @@ impl ArgMatches {
|
||||
if let Some(paths) = self.values_of_os("file") {
|
||||
for path in paths {
|
||||
if path == "-" {
|
||||
pats.extend(cli::patterns_from_stdin()?
|
||||
.into_iter()
|
||||
.map(|p| self.pattern_from_string(p))
|
||||
pats.extend(
|
||||
cli::patterns_from_stdin()?
|
||||
.into_iter()
|
||||
.map(|p| self.pattern_from_string(p)),
|
||||
);
|
||||
} else {
|
||||
pats.extend(cli::patterns_from_path(path)?
|
||||
.into_iter()
|
||||
.map(|p| self.pattern_from_string(p))
|
||||
pats.extend(
|
||||
cli::patterns_from_path(path)?
|
||||
.into_iter()
|
||||
.map(|p| self.pattern_from_string(p)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1528,7 +1500,7 @@ impl ArgMatches {
|
||||
None => match self.value_of_lossy("sortr") {
|
||||
None => return Ok(SortBy::none()),
|
||||
Some(choice) => SortBy::desc(SortByKind::new(&choice)),
|
||||
}
|
||||
},
|
||||
Some(choice) => SortBy::asc(SortByKind::new(&choice)),
|
||||
};
|
||||
Ok(sortby)
|
||||
@ -1571,11 +1543,7 @@ impl ArgMatches {
|
||||
return Ok(1);
|
||||
}
|
||||
let threads = self.usize_of("threads")?.unwrap_or(0);
|
||||
Ok(if threads == 0 {
|
||||
cmp::min(12, num_cpus::get())
|
||||
} else {
|
||||
threads
|
||||
})
|
||||
Ok(if threads == 0 { cmp::min(12, num_cpus::get()) } else { threads })
|
||||
}
|
||||
|
||||
/// Builds a file type matcher from the command line flags.
|
||||
@ -1623,9 +1591,11 @@ impl ArgMatches {
|
||||
} else {
|
||||
let path_stdin = Path::new("-");
|
||||
self.is_present("with-filename")
|
||||
|| self.is_present("vimgrep")
|
||||
|| paths.len() > 1
|
||||
|| paths.get(0).map_or(false, |p| p != path_stdin && p.is_dir())
|
||||
|| self.is_present("vimgrep")
|
||||
|| paths.len() > 1
|
||||
|| paths
|
||||
.get(0)
|
||||
.map_or(false, |p| p != path_stdin && p.is_dir())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1648,11 +1618,7 @@ impl ArgMatches {
|
||||
None => return Ok(None),
|
||||
Some(n) => n,
|
||||
};
|
||||
Ok(if n == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(n)
|
||||
})
|
||||
Ok(if n == 0 { None } else { Some(n) })
|
||||
}
|
||||
|
||||
/// Safely reads an arg value with the given name, and if it's present,
|
||||
@ -1718,19 +1684,25 @@ fn suggest_pcre2(msg: String) -> String {
|
||||
if !msg.contains("backreferences") && !msg.contains("look-around") {
|
||||
msg
|
||||
} else {
|
||||
format!("{}
|
||||
format!(
|
||||
"{}
|
||||
|
||||
Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences
|
||||
and look-around.", msg)
|
||||
and look-around.",
|
||||
msg
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_multiline(msg: String) -> String {
|
||||
if msg.contains("the literal") && msg.contains("not allowed") {
|
||||
format!("{}
|
||||
format!(
|
||||
"{}
|
||||
|
||||
Consider enabling multiline mode with the --multiline flag (or -U for short).
|
||||
When multiline mode is enabled, new line characters can be matched.", msg)
|
||||
When multiline mode is enabled, new line characters can be matched.",
|
||||
msg
|
||||
)
|
||||
} else {
|
||||
msg
|
||||
}
|
||||
@ -1738,10 +1710,7 @@ When multiline mode is enabled, new line characters can be matched.", msg)
|
||||
|
||||
/// Convert the result of parsing a human readable file size to a `usize`,
|
||||
/// failing if the type does not fit.
|
||||
fn u64_to_usize(
|
||||
arg_name: &str,
|
||||
value: Option<u64>,
|
||||
) -> Result<Option<usize>> {
|
||||
fn u64_to_usize(arg_name: &str, value: Option<u64>) -> Result<Option<usize>> {
|
||||
use std::usize;
|
||||
|
||||
let value = match value {
|
||||
@ -1766,7 +1735,8 @@ fn sort_by_metadata_time<G>(
|
||||
reverse: bool,
|
||||
get_time: G,
|
||||
) -> cmp::Ordering
|
||||
where G: Fn(&fs::Metadata) -> io::Result<SystemTime>
|
||||
where
|
||||
G: Fn(&fs::Metadata) -> io::Result<SystemTime>,
|
||||
{
|
||||
let t1 = match p1.metadata().and_then(|md| get_time(&md)) {
|
||||
Ok(t) => t,
|
||||
@ -1789,11 +1759,10 @@ where G: Fn(&fs::Metadata) -> io::Result<SystemTime>
|
||||
/// corresponds to a `--help` or `--version` request. In which case, the
|
||||
/// corresponding output is printed and the current process is exited
|
||||
/// successfully.
|
||||
fn clap_matches<I, T>(
|
||||
args: I,
|
||||
) -> Result<clap::ArgMatches<'static>>
|
||||
where I: IntoIterator<Item=T>,
|
||||
T: Into<OsString> + Clone
|
||||
fn clap_matches<I, T>(args: I) -> Result<clap::ArgMatches<'static>>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
{
|
||||
let err = match app::app().get_matches_from_safe(args) {
|
||||
Ok(matches) => return Ok(matches),
|
||||
@ -1831,5 +1800,6 @@ fn current_dir() -> Result<PathBuf> {
|
||||
"failed to get current working directory: {} \
|
||||
--- did your CWD get deleted?",
|
||||
err,
|
||||
).into())
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use bstr::{io::BufReadExt, ByteSlice};
|
||||
@ -102,12 +102,13 @@ fn parse_reader<R: io::Read>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::OsString;
|
||||
use super::parse_reader;
|
||||
use std::ffi::OsString;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let (args, errs) = parse_reader(&b"\
|
||||
let (args, errs) = parse_reader(
|
||||
&b"\
|
||||
# Test
|
||||
--context=0
|
||||
--smart-case
|
||||
@ -116,13 +117,13 @@ mod tests {
|
||||
|
||||
# --bar
|
||||
--foo
|
||||
"[..]).unwrap();
|
||||
"[..],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(errs.is_empty());
|
||||
let args: Vec<String> =
|
||||
args.into_iter().map(|s| s.into_string().unwrap()).collect();
|
||||
assert_eq!(args, vec![
|
||||
"--context=0", "--smart-case", "-u", "--foo",
|
||||
]);
|
||||
assert_eq!(args, vec!["--context=0", "--smart-case", "-u", "--foo",]);
|
||||
}
|
||||
|
||||
// We test that we can handle invalid UTF-8 on Unix-like systems.
|
||||
@ -131,32 +132,38 @@ mod tests {
|
||||
fn error() {
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
|
||||
let (args, errs) = parse_reader(&b"\
|
||||
let (args, errs) = parse_reader(
|
||||
&b"\
|
||||
quux
|
||||
foo\xFFbar
|
||||
baz
|
||||
"[..]).unwrap();
|
||||
"[..],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(errs.is_empty());
|
||||
assert_eq!(args, vec![
|
||||
OsString::from("quux"),
|
||||
OsString::from_vec(b"foo\xFFbar".to_vec()),
|
||||
OsString::from("baz"),
|
||||
]);
|
||||
assert_eq!(
|
||||
args,
|
||||
vec![
|
||||
OsString::from("quux"),
|
||||
OsString::from_vec(b"foo\xFFbar".to_vec()),
|
||||
OsString::from("baz"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// ... but test that invalid UTF-8 fails on Windows.
|
||||
#[test]
|
||||
#[cfg(not(unix))]
|
||||
fn error() {
|
||||
let (args, errs) = parse_reader(&b"\
|
||||
let (args, errs) = parse_reader(
|
||||
&b"\
|
||||
quux
|
||||
foo\xFFbar
|
||||
baz
|
||||
"[..]).unwrap();
|
||||
"[..],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(errs.len(), 1);
|
||||
assert_eq!(args, vec![
|
||||
OsString::from("quux"),
|
||||
OsString::from("baz"),
|
||||
]);
|
||||
assert_eq!(args, vec![OsString::from("quux"), OsString::from("baz"),]);
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,7 @@ impl PathPrinterBuilder {
|
||||
/// 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,
|
||||
}
|
||||
PathPrinter { config: self.config.clone(), wtr: wtr }
|
||||
}
|
||||
|
||||
/// Set the color specification for this printer.
|
||||
|
@ -7,9 +7,9 @@ use std::time::Duration;
|
||||
use grep::cli;
|
||||
use grep::matcher::Matcher;
|
||||
#[cfg(feature = "pcre2")]
|
||||
use grep::pcre2::{RegexMatcher as PCRE2RegexMatcher};
|
||||
use grep::printer::{JSON, Standard, Summary, Stats};
|
||||
use grep::regex::{RegexMatcher as RustRegexMatcher};
|
||||
use grep::pcre2::RegexMatcher as PCRE2RegexMatcher;
|
||||
use grep::printer::{Standard, Stats, Summary, JSON};
|
||||
use grep::regex::RegexMatcher as RustRegexMatcher;
|
||||
use grep::searcher::{BinaryDetection, Searcher};
|
||||
use ignore::overrides::Override;
|
||||
use serde_json as json;
|
||||
@ -86,8 +86,12 @@ impl SearchWorkerBuilder {
|
||||
let command_builder = self.command_builder.clone();
|
||||
let decomp_builder = self.decomp_builder.clone();
|
||||
SearchWorker {
|
||||
config, command_builder, decomp_builder,
|
||||
matcher, searcher, printer,
|
||||
config,
|
||||
command_builder,
|
||||
decomp_builder,
|
||||
matcher,
|
||||
searcher,
|
||||
printer,
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,9 +231,7 @@ impl<W: WriteColor> Printer<W> {
|
||||
stats: &Stats,
|
||||
) -> io::Result<()> {
|
||||
match *self {
|
||||
Printer::JSON(_) => {
|
||||
self.print_stats_json(total_duration, stats)
|
||||
}
|
||||
Printer::JSON(_) => self.print_stats_json(total_duration, stats),
|
||||
Printer::Standard(_) | Printer::Summary(_) => {
|
||||
self.print_stats_human(total_duration, stats)
|
||||
}
|
||||
@ -273,17 +275,20 @@ impl<W: WriteColor> Printer<W> {
|
||||
// the grep-printer crate. We simply "extend" it with the 'summary'
|
||||
// message type.
|
||||
let fractional = fractional_seconds(total_duration);
|
||||
json::to_writer(self.get_mut(), &json!({
|
||||
"type": "summary",
|
||||
"data": {
|
||||
"stats": stats,
|
||||
"elapsed_total": {
|
||||
"secs": total_duration.as_secs(),
|
||||
"nanos": total_duration.subsec_nanos(),
|
||||
"human": format!("{:0.6}s", fractional),
|
||||
},
|
||||
}
|
||||
}))?;
|
||||
json::to_writer(
|
||||
self.get_mut(),
|
||||
&json!({
|
||||
"type": "summary",
|
||||
"data": {
|
||||
"stats": stats,
|
||||
"elapsed_total": {
|
||||
"secs": total_duration.as_secs(),
|
||||
"nanos": total_duration.subsec_nanos(),
|
||||
"human": format!("{:0.6}s", fractional),
|
||||
},
|
||||
}
|
||||
}),
|
||||
)?;
|
||||
write!(self.get_mut(), "\n")
|
||||
}
|
||||
|
||||
@ -315,12 +320,11 @@ pub struct SearchWorker<W> {
|
||||
impl<W: WriteColor> SearchWorker<W> {
|
||||
/// Execute a search over the given subject.
|
||||
pub fn search(&mut self, subject: &Subject) -> io::Result<SearchResult> {
|
||||
let bin =
|
||||
if subject.is_explicit() {
|
||||
self.config.binary_explicit.clone()
|
||||
} else {
|
||||
self.config.binary_implicit.clone()
|
||||
};
|
||||
let bin = if subject.is_explicit() {
|
||||
self.config.binary_explicit.clone()
|
||||
} else {
|
||||
self.config.binary_implicit.clone()
|
||||
};
|
||||
self.searcher.set_binary_detection(bin);
|
||||
|
||||
let path = subject.path();
|
||||
@ -389,19 +393,15 @@ impl<W: WriteColor> SearchWorker<W> {
|
||||
let mut cmd = Command::new(bin);
|
||||
cmd.arg(path).stdin(Stdio::from(File::open(path)?));
|
||||
|
||||
let rdr = self
|
||||
.command_builder
|
||||
.build(&mut cmd)
|
||||
.map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"preprocessor command could not start: '{:?}': {}",
|
||||
cmd,
|
||||
err,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
let rdr = self.command_builder.build(&mut cmd).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"preprocessor command could not start: '{:?}': {}",
|
||||
cmd, err,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
self.search_reader(path, rdr).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@ -413,10 +413,7 @@ impl<W: WriteColor> SearchWorker<W> {
|
||||
/// Attempt to decompress the data at the given file path and search the
|
||||
/// result. If the given file path isn't recognized as a compressed file,
|
||||
/// then search it without doing any decompression.
|
||||
fn search_decompress(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
) -> io::Result<SearchResult> {
|
||||
fn search_decompress(&mut self, path: &Path) -> io::Result<SearchResult> {
|
||||
let rdr = self.decomp_builder.build(path)?;
|
||||
self.search_reader(path, rdr)
|
||||
}
|
||||
|
@ -11,9 +11,7 @@ struct Config {
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
strip_dot_prefix: false,
|
||||
}
|
||||
Config { strip_dot_prefix: false }
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,9 +76,9 @@ impl SubjectBuilder {
|
||||
log::debug!(
|
||||
"ignoring {}: failed to pass subject filter: \
|
||||
file type: {:?}, metadata: {:?}",
|
||||
subj.dent.path().display(),
|
||||
subj.dent.file_type(),
|
||||
subj.dent.metadata()
|
||||
subj.dent.path().display(),
|
||||
subj.dent.file_type(),
|
||||
subj.dent.metadata()
|
||||
);
|
||||
}
|
||||
None
|
||||
|
111
tests/binary.rs
111
tests/binary.rs
@ -36,9 +36,7 @@ const HAY: &'static [u8] = include_bytes!("./data/sherlock-nul.txt");
|
||||
// that matches our file.
|
||||
rgtest!(after_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "Project Gutenberg EBook", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "Project Gutenberg EBook", "-g", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
hay:1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||
@ -51,9 +49,7 @@ WARNING: stopped searching binary file hay after match (found \"\\u{0}\" byte ar
|
||||
// explicitly. This results in identical behavior, but a different message.
|
||||
rgtest!(after_match1_explicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "Project Gutenberg EBook", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "Project Gutenberg EBook", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||
@ -64,9 +60,7 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||
|
||||
// Like after_match1_explicit, except we feed our content on stdin.
|
||||
rgtest!(after_match1_stdin, |_: Dir, mut cmd: TestCommand| {
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "Project Gutenberg EBook",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "Project Gutenberg EBook"]);
|
||||
|
||||
let expected = "\
|
||||
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||
@ -81,7 +75,12 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||
rgtest!(after_match1_implicit_binary, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "--binary", "Project Gutenberg EBook", "-g", "hay",
|
||||
"--no-mmap",
|
||||
"-n",
|
||||
"--binary",
|
||||
"Project Gutenberg EBook",
|
||||
"-g",
|
||||
"hay",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -96,7 +95,12 @@ Binary file hay matches (found \"\\u{0}\" byte around offset 9741)
|
||||
rgtest!(after_match1_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "--text", "Project Gutenberg EBook", "-g", "hay",
|
||||
"--no-mmap",
|
||||
"-n",
|
||||
"--text",
|
||||
"Project Gutenberg EBook",
|
||||
"-g",
|
||||
"hay",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -109,9 +113,7 @@ hay:1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||
// detection should be performed.
|
||||
rgtest!(after_match1_explicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "--text", "Project Gutenberg EBook", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "--text", "Project Gutenberg EBook", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
1:The Project Gutenberg EBook of A Study In Scarlet, by Arthur Conan Doyle
|
||||
@ -134,9 +136,7 @@ rgtest!(after_match1_explicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||
// --quiet flag is set. See the next test.)
|
||||
rgtest!(after_match1_implicit_path, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-l", "Project Gutenberg EBook", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-l", "Project Gutenberg EBook", "-g", "hay"]);
|
||||
eqnice!("hay\n", cmd.stdout());
|
||||
});
|
||||
|
||||
@ -145,9 +145,7 @@ rgtest!(after_match1_implicit_path, |dir: Dir, mut cmd: TestCommand| {
|
||||
// manifest as an exit code with no output.)
|
||||
rgtest!(after_match1_implicit_quiet, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-q", "Project Gutenberg EBook", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-q", "Project Gutenberg EBook", "-g", "hay"]);
|
||||
eqnice!("", cmd.stdout());
|
||||
});
|
||||
|
||||
@ -157,32 +155,34 @@ rgtest!(after_match1_implicit_quiet, |dir: Dir, mut cmd: TestCommand| {
|
||||
// detects the binary data and suppresses output.
|
||||
rgtest!(after_match1_implicit_count, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-c", "Project Gutenberg EBook", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-c", "Project Gutenberg EBook", "-g", "hay"]);
|
||||
cmd.assert_err();
|
||||
});
|
||||
|
||||
// Like after_match1_implicit_count, except the --binary flag is provided,
|
||||
// which makes ripgrep disable binary data filtering even for implicit files.
|
||||
rgtest!(after_match1_implicit_count_binary, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-c", "--binary",
|
||||
"Project Gutenberg EBook",
|
||||
"-g", "hay",
|
||||
]);
|
||||
eqnice!("hay:1\n", cmd.stdout());
|
||||
});
|
||||
rgtest!(
|
||||
after_match1_implicit_count_binary,
|
||||
|dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap",
|
||||
"-c",
|
||||
"--binary",
|
||||
"Project Gutenberg EBook",
|
||||
"-g",
|
||||
"hay",
|
||||
]);
|
||||
eqnice!("hay:1\n", cmd.stdout());
|
||||
}
|
||||
);
|
||||
|
||||
// Like after_match1_implicit_count, except the file path is provided
|
||||
// explicitly, so binary filtering is disabled and a count is correctly
|
||||
// reported.
|
||||
rgtest!(after_match1_explicit_count, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-c", "Project Gutenberg EBook", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-c", "Project Gutenberg EBook", "hay"]);
|
||||
eqnice!("1\n", cmd.stdout());
|
||||
});
|
||||
|
||||
@ -191,9 +191,11 @@ rgtest!(after_match1_explicit_count, |dir: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(after_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n",
|
||||
"--no-mmap",
|
||||
"-n",
|
||||
"Project Gutenberg EBook|a medical student",
|
||||
"-g", "hay",
|
||||
"-g",
|
||||
"hay",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -208,9 +210,12 @@ WARNING: stopped searching binary file hay after match (found \"\\u{0}\" byte ar
|
||||
rgtest!(after_match2_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "--text",
|
||||
"--no-mmap",
|
||||
"-n",
|
||||
"--text",
|
||||
"Project Gutenberg EBook|a medical student",
|
||||
"-g", "hay",
|
||||
"-g",
|
||||
"hay",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -224,9 +229,7 @@ hay:236:\"And yet you say he is not a medical student?\"
|
||||
// after a NUL byte.
|
||||
rgtest!(before_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "Heaven", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "Heaven", "-g", "hay"]);
|
||||
cmd.assert_err();
|
||||
});
|
||||
|
||||
@ -234,9 +237,7 @@ rgtest!(before_match1_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
// occurs after a NUL byte when a file is explicitly searched.
|
||||
rgtest!(before_match1_explicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "Heaven", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "Heaven", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||
@ -249,9 +250,7 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||
// the file were given explicitly.
|
||||
rgtest!(before_match1_implicit_binary, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "--binary", "Heaven", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "--binary", "Heaven", "-g", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
Binary file hay matches (found \"\\u{0}\" byte around offset 9741)
|
||||
@ -263,9 +262,7 @@ Binary file hay matches (found \"\\u{0}\" byte around offset 9741)
|
||||
// detection should be performed.
|
||||
rgtest!(before_match1_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "--text", "Heaven", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "--text", "Heaven", "-g", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
hay:238:\"No. Heaven knows what the objects of his studies are. But here we
|
||||
@ -277,9 +274,7 @@ hay:238:\"No. Heaven knows what the objects of his studies are. But here we
|
||||
// before a NUL byte, but within the same buffer as the NUL byte.
|
||||
rgtest!(before_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "a medical student", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "a medical student", "-g", "hay"]);
|
||||
cmd.assert_err();
|
||||
});
|
||||
|
||||
@ -290,9 +285,7 @@ rgtest!(before_match2_implicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
// the behavior of GNU grep.)
|
||||
rgtest!(before_match2_explicit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "a medical student", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "a medical student", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||
@ -304,9 +297,7 @@ Binary file matches (found \"\\u{0}\" byte around offset 9741)
|
||||
// detection should be performed.
|
||||
rgtest!(before_match2_implicit_text, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes("hay", HAY);
|
||||
cmd.args(&[
|
||||
"--no-mmap", "-n", "--text", "a medical student", "-g", "hay",
|
||||
]);
|
||||
cmd.args(&["--no-mmap", "-n", "--text", "a medical student", "-g", "hay"]);
|
||||
|
||||
let expected = "\
|
||||
hay:236:\"And yet you say he is not a medical student?\"
|
||||
|
138
tests/feature.rs
138
tests/feature.rs
@ -1,5 +1,5 @@
|
||||
use crate::hay::{SHERLOCK, SHERLOCK_CRLF};
|
||||
use crate::util::{Dir, TestCommand, sort_lines};
|
||||
use crate::util::{sort_lines, Dir, TestCommand};
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/1
|
||||
rgtest!(f1_sjis, |dir: Dir, mut cmd: TestCommand| {
|
||||
@ -181,8 +181,10 @@ rgtest!(f45_precedence_internal, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("wat.log", "test");
|
||||
|
||||
cmd.args(&[
|
||||
"--ignore-file", ".not-an-ignore1",
|
||||
"--ignore-file", ".not-an-ignore2",
|
||||
"--ignore-file",
|
||||
".not-an-ignore1",
|
||||
"--ignore-file",
|
||||
".not-an-ignore2",
|
||||
"test",
|
||||
]);
|
||||
eqnice!("imp.log:test\n", cmd.stdout());
|
||||
@ -388,28 +390,34 @@ rgtest!(f362_exceeds_regex_size_limit, |dir: Dir, mut cmd: TestCommand| {
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/362
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
rgtest!(f362_u64_to_narrow_usize_overflow, |dir: Dir, mut cmd: TestCommand| {
|
||||
// --dfa-size-limit doesn't apply to PCRE2.
|
||||
if dir.is_pcre2() {
|
||||
return;
|
||||
}
|
||||
dir.create_size("foo", 1000000);
|
||||
rgtest!(
|
||||
f362_u64_to_narrow_usize_overflow,
|
||||
|dir: Dir, mut cmd: TestCommand| {
|
||||
// --dfa-size-limit doesn't apply to PCRE2.
|
||||
if dir.is_pcre2() {
|
||||
return;
|
||||
}
|
||||
dir.create_size("foo", 1000000);
|
||||
|
||||
// 2^35 * 2^20 is ok for u64, but not for usize
|
||||
cmd.arg("--dfa-size-limit").arg("34359738368M").arg("--files");
|
||||
cmd.assert_err();
|
||||
});
|
||||
// 2^35 * 2^20 is ok for u64, but not for usize
|
||||
cmd.arg("--dfa-size-limit").arg("34359738368M").arg("--files");
|
||||
cmd.assert_err();
|
||||
}
|
||||
);
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/411
|
||||
rgtest!(f411_single_threaded_search_stats, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
rgtest!(
|
||||
f411_single_threaded_search_stats,
|
||||
|dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
|
||||
let lines = cmd.arg("--stats").arg("Sherlock").stdout();
|
||||
assert!(lines.contains("2 matched lines"));
|
||||
assert!(lines.contains("1 files contained matches"));
|
||||
assert!(lines.contains("1 files searched"));
|
||||
assert!(lines.contains("seconds"));
|
||||
});
|
||||
let lines = cmd.arg("--stats").arg("Sherlock").stdout();
|
||||
assert!(lines.contains("2 matched lines"));
|
||||
assert!(lines.contains("1 files contained matches"));
|
||||
assert!(lines.contains("1 files searched"));
|
||||
assert!(lines.contains("seconds"));
|
||||
}
|
||||
);
|
||||
|
||||
rgtest!(f411_parallel_search_stats, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock_1", SHERLOCK);
|
||||
@ -568,7 +576,7 @@ rgtest!(f948_exit_code_error, |dir: Dir, mut cmd: TestCommand| {
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/917
|
||||
rgtest!(f917_trim, |dir: Dir, mut cmd: TestCommand| {
|
||||
const SHERLOCK: &'static str = "\
|
||||
const SHERLOCK: &'static str = "\
|
||||
zzz
|
||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
Holmeses, success in the province of detective work must always
|
||||
@ -578,9 +586,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
and exhibited clearly, with a label attached.
|
||||
";
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-n", "-B1", "-A2", "--trim", "Holmeses", "sherlock",
|
||||
]);
|
||||
cmd.args(&["-n", "-B1", "-A2", "--trim", "Holmeses", "sherlock"]);
|
||||
|
||||
let expected = "\
|
||||
2-For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
@ -596,7 +602,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
// This is like f917_trim, except this tests that trimming occurs even when the
|
||||
// whitespace is part of a match.
|
||||
rgtest!(f917_trim_match, |dir: Dir, mut cmd: TestCommand| {
|
||||
const SHERLOCK: &'static str = "\
|
||||
const SHERLOCK: &'static str = "\
|
||||
zzz
|
||||
For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
Holmeses, success in the province of detective work must always
|
||||
@ -606,9 +612,7 @@ but Doctor Watson has to have it taken out for him and dusted,
|
||||
and exhibited clearly, with a label attached.
|
||||
";
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-n", "-B1", "-A2", "--trim", r"\s+Holmeses", "sherlock",
|
||||
]);
|
||||
cmd.args(&["-n", "-B1", "-A2", "--trim", r"\s+Holmeses", "sherlock"]);
|
||||
|
||||
let expected = "\
|
||||
2-For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
@ -636,7 +640,8 @@ rgtest!(f993_null_data, |dir: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(f1078_max_columns_preview1, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-M46", "--max-columns-preview",
|
||||
"-M46",
|
||||
"--max-columns-preview",
|
||||
"exhibited|dusted|has to have it",
|
||||
]);
|
||||
|
||||
@ -650,7 +655,8 @@ sherlock:and exhibited clearly, with a label attached.
|
||||
rgtest!(f1078_max_columns_preview2, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-M43", "--max-columns-preview",
|
||||
"-M43",
|
||||
"--max-columns-preview",
|
||||
// Doing a replacement forces ripgrep to show the number of remaining
|
||||
// matches. Normally, this happens by default when printing a tty with
|
||||
// colors.
|
||||
@ -702,10 +708,7 @@ sherlock:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
// Tests if without encoding 'none' flag null bytes are consumed by automatic
|
||||
// encoding detection.
|
||||
rgtest!(f1207_auto_encoding, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_bytes(
|
||||
"foo",
|
||||
b"\xFF\xFE\x00\x62"
|
||||
);
|
||||
dir.create_bytes("foo", b"\xFF\xFE\x00\x62");
|
||||
cmd.arg("-a").arg("\\x00").arg("foo");
|
||||
cmd.assert_exit_code(1);
|
||||
});
|
||||
@ -720,10 +723,7 @@ rgtest!(f1207_ignore_encoding, |dir: Dir, mut cmd: TestCommand| {
|
||||
return;
|
||||
}
|
||||
|
||||
dir.create_bytes(
|
||||
"foo",
|
||||
b"\xFF\xFE\x00\x62"
|
||||
);
|
||||
dir.create_bytes("foo", b"\xFF\xFE\x00\x62");
|
||||
cmd.arg("--encoding").arg("none").arg("-a").arg("\\x00").arg("foo");
|
||||
eqnice!("\u{FFFD}\u{FFFD}\x00b\n", cmd.stdout());
|
||||
});
|
||||
@ -734,25 +734,22 @@ rgtest!(f1414_no_require_git, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("foo", "");
|
||||
dir.create("bar", "");
|
||||
|
||||
let stdout = cmd.args(&[
|
||||
"--sort", "path",
|
||||
"--files",
|
||||
]).stdout();
|
||||
let stdout = cmd.args(&["--sort", "path", "--files"]).stdout();
|
||||
eqnice!("bar\nfoo\n", stdout);
|
||||
|
||||
let stdout = cmd.args(&[
|
||||
"--sort", "path",
|
||||
"--files",
|
||||
"--no-require-git",
|
||||
]).stdout();
|
||||
let stdout =
|
||||
cmd.args(&["--sort", "path", "--files", "--no-require-git"]).stdout();
|
||||
eqnice!("bar\n", stdout);
|
||||
|
||||
let stdout = cmd.args(&[
|
||||
"--sort", "path",
|
||||
"--files",
|
||||
"--no-require-git",
|
||||
"--require-git",
|
||||
]).stdout();
|
||||
let stdout = cmd
|
||||
.args(&[
|
||||
"--sort",
|
||||
"path",
|
||||
"--files",
|
||||
"--no-require-git",
|
||||
"--require-git",
|
||||
])
|
||||
.stdout();
|
||||
eqnice!("bar\nfoo\n", stdout);
|
||||
});
|
||||
|
||||
@ -770,12 +767,7 @@ rgtest!(f1420_no_ignore_dot, |dir: Dir, mut cmd: TestCommand| {
|
||||
|
||||
rgtest!(no_context_sep, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||
cmd.args(&[
|
||||
"-A1",
|
||||
"--no-context-separator",
|
||||
"foo",
|
||||
"test",
|
||||
]);
|
||||
cmd.args(&["-A1", "--no-context-separator", "foo", "test"]);
|
||||
eqnice!("foo\nctx\nfoo\nctx\n", cmd.stdout());
|
||||
});
|
||||
|
||||
@ -783,7 +775,8 @@ rgtest!(no_context_sep_overrides, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||
cmd.args(&[
|
||||
"-A1",
|
||||
"--context-separator", "AAA",
|
||||
"--context-separator",
|
||||
"AAA",
|
||||
"--no-context-separator",
|
||||
"foo",
|
||||
"test",
|
||||
@ -796,7 +789,8 @@ rgtest!(no_context_sep_overridden, |dir: Dir, mut cmd: TestCommand| {
|
||||
cmd.args(&[
|
||||
"-A1",
|
||||
"--no-context-separator",
|
||||
"--context-separator", "AAA",
|
||||
"--context-separator",
|
||||
"AAA",
|
||||
"foo",
|
||||
"test",
|
||||
]);
|
||||
@ -805,33 +799,19 @@ rgtest!(no_context_sep_overridden, |dir: Dir, mut cmd: TestCommand| {
|
||||
|
||||
rgtest!(context_sep, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||
cmd.args(&[
|
||||
"-A1",
|
||||
"--context-separator", "AAA",
|
||||
"foo",
|
||||
"test",
|
||||
]);
|
||||
cmd.args(&["-A1", "--context-separator", "AAA", "foo", "test"]);
|
||||
eqnice!("foo\nctx\nAAA\nfoo\nctx\n", cmd.stdout());
|
||||
});
|
||||
|
||||
rgtest!(context_sep_default, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||
cmd.args(&[
|
||||
"-A1",
|
||||
"foo",
|
||||
"test",
|
||||
]);
|
||||
cmd.args(&["-A1", "foo", "test"]);
|
||||
eqnice!("foo\nctx\n--\nfoo\nctx\n", cmd.stdout());
|
||||
});
|
||||
|
||||
rgtest!(context_sep_empty, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("test", "foo\nctx\nbar\nctx\nfoo\nctx");
|
||||
cmd.args(&[
|
||||
"-A1",
|
||||
"--context-separator", "",
|
||||
"foo",
|
||||
"test",
|
||||
]);
|
||||
cmd.args(&["-A1", "--context-separator", "", "foo", "test"]);
|
||||
eqnice!("foo\nctx\n\nfoo\nctx\n", cmd.stdout());
|
||||
});
|
||||
|
||||
|
@ -108,8 +108,12 @@ enum Data {
|
||||
}
|
||||
|
||||
impl Data {
|
||||
fn text(s: &str) -> Data { Data::Text { text: s.to_string() } }
|
||||
fn bytes(s: &str) -> Data { Data::Bytes { bytes: s.to_string() } }
|
||||
fn text(s: &str) -> Data {
|
||||
Data::Text { text: s.to_string() }
|
||||
}
|
||||
fn bytes(s: &str) -> Data {
|
||||
Data::Bytes { bytes: s.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||
@ -172,31 +176,17 @@ rgtest!(basic, |dir: Dir, mut cmd: TestCommand| {
|
||||
),
|
||||
line_number: Some(3),
|
||||
absolute_offset: 129,
|
||||
submatches: vec![
|
||||
SubMatch {
|
||||
m: Data::text("Sherlock Holmes"),
|
||||
start: 48,
|
||||
end: 63,
|
||||
},
|
||||
],
|
||||
submatches: vec![SubMatch {
|
||||
m: Data::text("Sherlock Holmes"),
|
||||
start: 48,
|
||||
end: 63,
|
||||
},],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
msgs[3].unwrap_end().path,
|
||||
Some(Data::text("sherlock"))
|
||||
);
|
||||
assert_eq!(
|
||||
msgs[3].unwrap_end().binary_offset,
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
msgs[4].unwrap_summary().stats.searches_with_match,
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
msgs[4].unwrap_summary().stats.bytes_printed,
|
||||
494
|
||||
);
|
||||
assert_eq!(msgs[3].unwrap_end().path, Some(Data::text("sherlock")));
|
||||
assert_eq!(msgs[3].unwrap_end().binary_offset, None);
|
||||
assert_eq!(msgs[4].unwrap_summary().stats.searches_with_match, 1);
|
||||
assert_eq!(msgs[4].unwrap_summary().stats.bytes_printed, 494);
|
||||
});
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -239,13 +229,11 @@ rgtest!(notutf8, |dir: Dir, mut cmd: TestCommand| {
|
||||
lines: Data::bytes("cXV1eP9iYXo="),
|
||||
line_number: Some(1),
|
||||
absolute_offset: 0,
|
||||
submatches: vec![
|
||||
SubMatch {
|
||||
m: Data::bytes("/w=="),
|
||||
start: 4,
|
||||
end: 5,
|
||||
},
|
||||
],
|
||||
submatches: vec![SubMatch {
|
||||
m: Data::bytes("/w=="),
|
||||
start: 4,
|
||||
end: 5,
|
||||
},],
|
||||
}
|
||||
);
|
||||
});
|
||||
@ -282,13 +270,11 @@ rgtest!(notutf8_file, |dir: Dir, mut cmd: TestCommand| {
|
||||
lines: Data::bytes("cXV1eP9iYXo="),
|
||||
line_number: Some(1),
|
||||
absolute_offset: 0,
|
||||
submatches: vec![
|
||||
SubMatch {
|
||||
m: Data::bytes("/w=="),
|
||||
start: 4,
|
||||
end: 5,
|
||||
},
|
||||
],
|
||||
submatches: vec![SubMatch {
|
||||
m: Data::bytes("/w=="),
|
||||
start: 4,
|
||||
end: 5,
|
||||
},],
|
||||
}
|
||||
);
|
||||
});
|
||||
@ -306,11 +292,7 @@ rgtest!(crlf, |dir: Dir, mut cmd: TestCommand| {
|
||||
|
||||
assert_eq!(
|
||||
msgs[1].unwrap_match().submatches[0].clone(),
|
||||
SubMatch {
|
||||
m: Data::text("Sherlock"),
|
||||
start: 56,
|
||||
end: 64,
|
||||
},
|
||||
SubMatch { m: Data::text("Sherlock"), start: 56, end: 64 },
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -11,7 +11,7 @@ macro_rules! rgtest {
|
||||
$fun(dir, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::hay::SHERLOCK;
|
||||
use crate::util::{Dir, TestCommand, cmd_exists, sort_lines};
|
||||
use crate::util::{cmd_exists, sort_lines, Dir, TestCommand};
|
||||
|
||||
// This file contains "miscellaneous" tests that were either written before
|
||||
// features were tracked more explicitly, or were simply written without
|
||||
@ -65,8 +65,10 @@ rgtest!(with_heading, |dir: Dir, mut cmd: TestCommand| {
|
||||
cmd.args(&[
|
||||
// This forces the issue since --with-filename is disabled by default
|
||||
// when searching one file.
|
||||
"--with-filename", "--heading",
|
||||
"Sherlock", "sherlock",
|
||||
"--with-filename",
|
||||
"--heading",
|
||||
"Sherlock",
|
||||
"sherlock",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -184,9 +186,7 @@ be, to a very large extent, the result of luck. FooBar Holmes
|
||||
|
||||
rgtest!(replace_groups, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-r", "$2, $1", "([A-Z][a-z]+) ([A-Z][a-z]+)", "sherlock",
|
||||
]);
|
||||
cmd.args(&["-r", "$2, $1", "([A-Z][a-z]+) ([A-Z][a-z]+)", "sherlock"]);
|
||||
|
||||
let expected = "\
|
||||
For the Watsons, Doctor of this world, as opposed to the Sherlock
|
||||
@ -199,7 +199,8 @@ but Watson, Doctor has to have it taken out for him and dusted,
|
||||
rgtest!(replace_named_groups, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-r", "$last, $first",
|
||||
"-r",
|
||||
"$last, $first",
|
||||
"(?P<first>[A-Z][a-z]+) (?P<last>[A-Z][a-z]+)",
|
||||
"sherlock",
|
||||
]);
|
||||
@ -279,9 +280,7 @@ rgtest!(file_type_add, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("file.py", "Sherlock");
|
||||
dir.create("file.rs", "Sherlock");
|
||||
dir.create("file.wat", "Sherlock");
|
||||
cmd.args(&[
|
||||
"--type-add", "wat:*.wat", "-t", "wat", "Sherlock",
|
||||
]);
|
||||
cmd.args(&["--type-add", "wat:*.wat", "-t", "wat", "Sherlock"]);
|
||||
|
||||
eqnice!("file.wat:Sherlock\n", cmd.stdout());
|
||||
});
|
||||
@ -292,9 +291,12 @@ rgtest!(file_type_add_compose, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("file.rs", "Sherlock");
|
||||
dir.create("file.wat", "Sherlock");
|
||||
cmd.args(&[
|
||||
"--type-add", "wat:*.wat",
|
||||
"--type-add", "combo:include:wat,py",
|
||||
"-t", "combo",
|
||||
"--type-add",
|
||||
"wat:*.wat",
|
||||
"--type-add",
|
||||
"combo:include:wat,py",
|
||||
"-t",
|
||||
"combo",
|
||||
"Sherlock",
|
||||
]);
|
||||
|
||||
@ -394,11 +396,7 @@ rgtest!(count_matches_via_only, |dir: Dir, mut cmd: TestCommand| {
|
||||
|
||||
rgtest!(include_zero, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"--count",
|
||||
"--include-zero",
|
||||
"nada",
|
||||
]);
|
||||
cmd.args(&["--count", "--include-zero", "nada"]);
|
||||
cmd.assert_err();
|
||||
|
||||
let output = cmd.cmd().output().unwrap();
|
||||
@ -410,12 +408,7 @@ rgtest!(include_zero, |dir: Dir, mut cmd: TestCommand| {
|
||||
|
||||
rgtest!(include_zero_override, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"--count",
|
||||
"--include-zero",
|
||||
"--no-include-zero",
|
||||
"nada",
|
||||
]);
|
||||
cmd.args(&["--count", "--include-zero", "--no-include-zero", "nada"]);
|
||||
cmd.assert_err();
|
||||
|
||||
let output = cmd.cmd().output().unwrap();
|
||||
|
@ -20,9 +20,7 @@ rgtest!(overlap2, |dir: Dir, mut cmd: TestCommand| {
|
||||
// Tests that even in a multiline search, a '.' does not match a newline.
|
||||
rgtest!(dot_no_newline, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-n", "-U", "of this world.+detective work", "sherlock",
|
||||
]);
|
||||
cmd.args(&["-n", "-U", "of this world.+detective work", "sherlock"]);
|
||||
cmd.assert_err();
|
||||
});
|
||||
|
||||
@ -30,8 +28,11 @@ rgtest!(dot_no_newline, |dir: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(dot_all, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-n", "-U", "--multiline-dotall",
|
||||
"of this world.+detective work", "sherlock",
|
||||
"-n",
|
||||
"-U",
|
||||
"--multiline-dotall",
|
||||
"of this world.+detective work",
|
||||
"sherlock",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -45,8 +46,11 @@ rgtest!(dot_all, |dir: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(only_matching, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-n", "-U", "--only-matching",
|
||||
r"Watson|Sherlock\p{Any}+?Holmes", "sherlock",
|
||||
"-n",
|
||||
"-U",
|
||||
"--only-matching",
|
||||
r"Watson|Sherlock\p{Any}+?Holmes",
|
||||
"sherlock",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -63,8 +67,11 @@ rgtest!(only_matching, |dir: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(vimgrep, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-n", "-U", "--vimgrep",
|
||||
r"Watson|Sherlock\p{Any}+?Holmes", "sherlock",
|
||||
"-n",
|
||||
"-U",
|
||||
"--vimgrep",
|
||||
r"Watson|Sherlock\p{Any}+?Holmes",
|
||||
"sherlock",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
@ -81,9 +88,7 @@ sherlock:5:12:but Doctor Watson has to have it taken out for him and dusted,
|
||||
// important test because multiline search must read the entire contents of
|
||||
// what it is searching into memory before executing the search.
|
||||
rgtest!(stdin, |_: Dir, mut cmd: TestCommand| {
|
||||
cmd.args(&[
|
||||
"-n", "-U", r"of this world\p{Any}+?detective work",
|
||||
]);
|
||||
cmd.args(&["-n", "-U", r"of this world\p{Any}+?detective work"]);
|
||||
let expected = "\
|
||||
1:For the Doctor Watsons of this world, as opposed to the Sherlock
|
||||
2:Holmeses, success in the province of detective work must always
|
||||
@ -95,8 +100,11 @@ rgtest!(stdin, |_: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(context, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", SHERLOCK);
|
||||
cmd.args(&[
|
||||
"-n", "-U", "-C1",
|
||||
r"detective work\p{Any}+?result of luck", "sherlock",
|
||||
"-n",
|
||||
"-U",
|
||||
"-C1",
|
||||
r"detective work\p{Any}+?result of luck",
|
||||
"sherlock",
|
||||
]);
|
||||
|
||||
let expected = "\
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::hay::SHERLOCK;
|
||||
use crate::util::{Dir, TestCommand, sort_lines};
|
||||
use crate::util::{sort_lines, Dir, TestCommand};
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/16
|
||||
rgtest!(r16, |dir: Dir, mut cmd: TestCommand| {
|
||||
@ -346,7 +346,10 @@ rgtest!(r391, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create(".git/description", "");
|
||||
|
||||
cmd.args(&[
|
||||
"--no-ignore", "--hidden", "--follow", "--files",
|
||||
"--no-ignore",
|
||||
"--hidden",
|
||||
"--follow",
|
||||
"--files",
|
||||
"--glob",
|
||||
"!{.git,node_modules,plugged}/**",
|
||||
"--glob",
|
||||
@ -371,14 +374,18 @@ rgtest!(r405, |dir: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(r428_color_context_path, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("sherlock", "foo\nbar");
|
||||
cmd.args(&[
|
||||
"-A1", "-H", "--no-heading", "-N",
|
||||
"--colors=match:none", "--color=always",
|
||||
"-A1",
|
||||
"-H",
|
||||
"--no-heading",
|
||||
"-N",
|
||||
"--colors=match:none",
|
||||
"--color=always",
|
||||
"foo",
|
||||
]);
|
||||
|
||||
let expected = format!(
|
||||
"{colored_path}:foo\n{colored_path}-bar\n",
|
||||
colored_path=
|
||||
colored_path =
|
||||
"\x1b\x5b\x30\x6d\x1b\x5b\x33\x35\x6dsherlock\x1b\x5b\x30\x6d"
|
||||
);
|
||||
eqnice!(expected, cmd.stdout());
|
||||
@ -414,9 +421,7 @@ rgtest!(r451_only_matching_as_in_issue, |dir: Dir, mut cmd: TestCommand| {
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/451
|
||||
rgtest!(r451_only_matching, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("digits.txt", "1 2 3\n123\n");
|
||||
cmd.args(&[
|
||||
"--only-matching", "--column", r"[0-9]", "digits.txt",
|
||||
]);
|
||||
cmd.args(&["--only-matching", "--column", r"[0-9]", "digits.txt"]);
|
||||
|
||||
let expected = "\
|
||||
1:1:1
|
||||
@ -517,11 +522,16 @@ rgtest!(r568_leading_hyphen_option_args, |dir: Dir, mut cmd: TestCommand| {
|
||||
rgtest!(r599, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("input.txt", "\n\ntest\n");
|
||||
cmd.args(&[
|
||||
"--color", "ansi",
|
||||
"--colors", "path:none",
|
||||
"--colors", "line:none",
|
||||
"--colors", "match:fg:red",
|
||||
"--colors", "match:style:nobold",
|
||||
"--color",
|
||||
"ansi",
|
||||
"--colors",
|
||||
"path:none",
|
||||
"--colors",
|
||||
"line:none",
|
||||
"--colors",
|
||||
"match:fg:red",
|
||||
"--colors",
|
||||
"match:style:nobold",
|
||||
"--line-number",
|
||||
r"^$",
|
||||
"input.txt",
|
||||
@ -707,16 +717,19 @@ rgtest!(r1203_reverse_suffix_literal, |dir: Dir, _: TestCommand| {
|
||||
});
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/1223
|
||||
rgtest!(r1223_no_dir_check_for_default_path, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_dir("-");
|
||||
dir.create("a.json", "{}");
|
||||
dir.create("a.txt", "some text");
|
||||
rgtest!(
|
||||
r1223_no_dir_check_for_default_path,
|
||||
|dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_dir("-");
|
||||
dir.create("a.json", "{}");
|
||||
dir.create("a.txt", "some text");
|
||||
|
||||
eqnice!(
|
||||
"a.json\na.txt\n",
|
||||
sort_lines(&cmd.arg("a").pipe(b"a.json\na.txt"))
|
||||
);
|
||||
});
|
||||
eqnice!(
|
||||
"a.json\na.txt\n",
|
||||
sort_lines(&cmd.arg("a").pipe(b"a.json\na.txt"))
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/1259
|
||||
rgtest!(r1259_drop_last_byte_nonl, |dir: Dir, mut cmd: TestCommand| {
|
||||
@ -734,7 +747,8 @@ rgtest!(r1319, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("input", "CCAGCTACTCGGGAGGCTGAGGCTGGAGGATCGCTTGAGTCCAGGAGTTC");
|
||||
eqnice!(
|
||||
"input:CCAGCTACTCGGGAGGCTGAGGCTGGAGGATCGCTTGAGTCCAGGAGTTC\n",
|
||||
cmd.arg("TTGAGTCCAGGAG[ATCG]{2}C").stdout());
|
||||
cmd.arg("TTGAGTCCAGGAG[ATCG]{2}C").stdout()
|
||||
);
|
||||
});
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/issues/1334
|
||||
@ -753,27 +767,27 @@ rgtest!(r1389_bad_symlinks_no_biscuit, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create("mydir/file.txt", "test");
|
||||
dir.link_dir("mydir", "mylink");
|
||||
|
||||
let stdout = cmd.args(&[
|
||||
"test",
|
||||
"--no-ignore",
|
||||
"--sort", "path",
|
||||
"mylink",
|
||||
]).stdout();
|
||||
let stdout = cmd
|
||||
.args(&["test", "--no-ignore", "--sort", "path", "mylink"])
|
||||
.stdout();
|
||||
eqnice!("mylink/file.txt:test\n", stdout);
|
||||
});
|
||||
|
||||
// See: https://github.com/BurntSushi/ripgrep/pull/1446
|
||||
rgtest!(r1446_respect_excludes_in_worktree, |dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_dir("repo/.git/info");
|
||||
dir.create("repo/.git/info/exclude", "ignored");
|
||||
dir.create_dir("repo/.git/worktrees/repotree");
|
||||
dir.create("repo/.git/worktrees/repotree/commondir", "../..");
|
||||
rgtest!(
|
||||
r1446_respect_excludes_in_worktree,
|
||||
|dir: Dir, mut cmd: TestCommand| {
|
||||
dir.create_dir("repo/.git/info");
|
||||
dir.create("repo/.git/info/exclude", "ignored");
|
||||
dir.create_dir("repo/.git/worktrees/repotree");
|
||||
dir.create("repo/.git/worktrees/repotree/commondir", "../..");
|
||||
|
||||
dir.create_dir("repotree");
|
||||
dir.create("repotree/.git", "gitdir: repo/.git/worktrees/repotree");
|
||||
dir.create("repotree/ignored", "");
|
||||
dir.create("repotree/not-ignored", "");
|
||||
dir.create_dir("repotree");
|
||||
dir.create("repotree/.git", "gitdir: repo/.git/worktrees/repotree");
|
||||
dir.create("repotree/ignored", "");
|
||||
dir.create("repotree/not-ignored", "");
|
||||
|
||||
cmd.arg("--sort").arg("path").arg("--files").arg("repotree");
|
||||
eqnice!("repotree/not-ignored\n", cmd.stdout());
|
||||
});
|
||||
cmd.arg("--sort").arg("path").arg("--files").arg("repotree");
|
||||
eqnice!("repotree/not-ignored\n", cmd.stdout());
|
||||
}
|
||||
);
|
||||
|
@ -72,19 +72,13 @@ impl Dir {
|
||||
.parent()
|
||||
.expect("executable's directory")
|
||||
.to_path_buf();
|
||||
let dir = env::temp_dir()
|
||||
.join(TEST_DIR)
|
||||
.join(name)
|
||||
.join(&format!("{}", id));
|
||||
let dir =
|
||||
env::temp_dir().join(TEST_DIR).join(name).join(&format!("{}", id));
|
||||
if dir.exists() {
|
||||
nice_err(&dir, fs::remove_dir_all(&dir));
|
||||
}
|
||||
nice_err(&dir, repeat(|| fs::create_dir_all(&dir)));
|
||||
Dir {
|
||||
root: root,
|
||||
dir: dir,
|
||||
pcre2: false,
|
||||
}
|
||||
Dir { root: root, dir: dir, pcre2: false }
|
||||
}
|
||||
|
||||
/// Use PCRE2 for this test.
|
||||
@ -262,12 +256,10 @@ impl TestCommand {
|
||||
}
|
||||
|
||||
/// Add any number of arguments to the command.
|
||||
pub fn args<I, A>(
|
||||
&mut self,
|
||||
args: I,
|
||||
) -> &mut TestCommand
|
||||
where I: IntoIterator<Item=A>,
|
||||
A: AsRef<OsStr>
|
||||
pub fn args<I, A>(&mut self, args: I) -> &mut TestCommand
|
||||
where
|
||||
I: IntoIterator<Item = A>,
|
||||
A: AsRef<OsStr>,
|
||||
{
|
||||
self.cmd.args(args);
|
||||
self
|
||||
@ -292,8 +284,7 @@ impl TestCommand {
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"could not convert from string: {:?}\n\n{}",
|
||||
err,
|
||||
stdout
|
||||
err, stdout
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -311,9 +302,7 @@ impl TestCommand {
|
||||
// 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 || {
|
||||
stdin.write_all(&input)
|
||||
});
|
||||
let worker = thread::spawn(move || stdin.write_all(&input));
|
||||
|
||||
let output = self.expect_success(child.wait_with_output().unwrap());
|
||||
worker.join().unwrap().unwrap();
|
||||
@ -324,8 +313,7 @@ impl TestCommand {
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"could not convert from string: {:?}\n\n{}",
|
||||
err,
|
||||
stdout
|
||||
err, stdout
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -368,9 +356,7 @@ impl TestCommand {
|
||||
\n\nexpected: {}\
|
||||
\n\nfound: {}\
|
||||
\n\n=====\n",
|
||||
self.cmd,
|
||||
expected_code,
|
||||
code
|
||||
self.cmd, expected_code, code
|
||||
);
|
||||
}
|
||||
|
||||
@ -396,14 +382,14 @@ impl TestCommand {
|
||||
|
||||
fn expect_success(&self, o: process::Output) -> process::Output {
|
||||
if !o.status.success() {
|
||||
let suggest =
|
||||
if o.stderr.is_empty() {
|
||||
"\n\nDid your search end up with no results?".to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let suggest = if o.stderr.is_empty() {
|
||||
"\n\nDid your search end up with no results?".to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
panic!("\n\n==========\n\
|
||||
panic!(
|
||||
"\n\n==========\n\
|
||||
command failed but expected success!\
|
||||
{}\
|
||||
\n\ncommand: {:?}\
|
||||
@ -412,18 +398,19 @@ impl TestCommand {
|
||||
\n\nstdout: {}\
|
||||
\n\nstderr: {}\
|
||||
\n\n==========\n",
|
||||
suggest, self.cmd, self.dir.dir.display(), o.status,
|
||||
String::from_utf8_lossy(&o.stdout),
|
||||
String::from_utf8_lossy(&o.stderr));
|
||||
suggest,
|
||||
self.cmd,
|
||||
self.dir.dir.display(),
|
||||
o.status,
|
||||
String::from_utf8_lossy(&o.stdout),
|
||||
String::from_utf8_lossy(&o.stderr)
|
||||
);
|
||||
}
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
fn nice_err<T, E: error::Error>(
|
||||
path: &Path,
|
||||
res: Result<T, E>,
|
||||
) -> T {
|
||||
fn nice_err<T, E: error::Error>(path: &Path, res: Result<T, E>) -> T {
|
||||
match res {
|
||||
Ok(t) => t,
|
||||
Err(err) => panic!("{}: {:?}", path.display(), err),
|
||||
|
Loading…
Reference in New Issue
Block a user