mirror of
https://github.com/sharkdp/fd
synced 2024-10-04 14:59:15 +00:00
Add support for exclude-patterns
* Add `--exclude`/`-E` option. * Support for multiple exclude patterns Example: ``` bash > fd --exclude 'tests/**/*.rs' mod src/exec/mod.rs src/fshelper/mod.rs src/lscolors/mod.rs ``` Closes #89
This commit is contained in:
parent
19afb15a98
commit
5ad69fb2fb
35
src/app.rs
35
src/app.rs
|
@ -81,6 +81,22 @@ pub fn build_app() -> App<'static, 'static> {
|
|||
.takes_value(true)
|
||||
.value_name("ext"),
|
||||
)
|
||||
.arg(
|
||||
arg("exec")
|
||||
.long("exec")
|
||||
.short("x")
|
||||
.takes_value(true)
|
||||
.value_name("cmd"),
|
||||
)
|
||||
.arg(
|
||||
arg("exclude")
|
||||
.long("exclude")
|
||||
.short("E")
|
||||
.takes_value(true)
|
||||
.value_name("pattern")
|
||||
.number_of_values(1)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
arg("color")
|
||||
.long("color")
|
||||
|
@ -103,13 +119,6 @@ pub fn build_app() -> App<'static, 'static> {
|
|||
.takes_value(true)
|
||||
.hidden(true),
|
||||
)
|
||||
.arg(
|
||||
arg("exec")
|
||||
.long("exec")
|
||||
.short("x")
|
||||
.takes_value(true)
|
||||
.value_name("cmd"),
|
||||
)
|
||||
.arg(arg("pattern"))
|
||||
.arg(arg("path"))
|
||||
}
|
||||
|
@ -158,6 +167,9 @@ fn usage() -> HashMap<&'static str, Help> {
|
|||
'f' or 'file': regular files\n \
|
||||
'd' or 'directory': directories\n \
|
||||
'l' or 'symlink': symbolic links");
|
||||
doc!(h, "extension"
|
||||
, "Filter by file extension"
|
||||
, "(Additionally) filter search results by their file extension.");
|
||||
doc!(h, "exec"
|
||||
, "Execute the given command for each search result"
|
||||
, "Execute the given command for each search result.\n\
|
||||
|
@ -167,10 +179,11 @@ fn usage() -> HashMap<&'static str, Help> {
|
|||
'{.}': removes the extension from the input\n \
|
||||
'{/}': places the basename of the input\n \
|
||||
'{//}': places the parent of the input\n \
|
||||
'{/.}': places the basename of the input, without the extension\n");
|
||||
doc!(h, "extension"
|
||||
, "Filter by file extension"
|
||||
, "(Additionally) filter search results by their file extension.");
|
||||
'{/.}': places the basename of the input, without the extension");
|
||||
doc!(h, "exclude"
|
||||
, "Exclude entries that match the given glob pattern."
|
||||
, "Exclude files/directories that match the given glob pattern. This overrides any \
|
||||
other ignore logic. Multiple exclude patterns can be specified.");
|
||||
doc!(h, "color"
|
||||
, "When to use colors: never, *auto*, always"
|
||||
, "Declare when to use color for the pattern match output:\n \
|
||||
|
|
|
@ -77,6 +77,9 @@ pub struct FdOptions {
|
|||
|
||||
/// If a value is supplied, each item found will be used to generate and execute commands.
|
||||
pub command: Option<TokenizedCommand>,
|
||||
|
||||
/// A list of glob patterns that should be excluded from the search.
|
||||
pub exclude_patterns: Vec<String>,
|
||||
}
|
||||
|
||||
/// Print error message to stderr and exit with status `1`.
|
||||
|
|
|
@ -141,6 +141,10 @@ fn main() {
|
|||
e.trim_left_matches('.').to_lowercase()
|
||||
}),
|
||||
command,
|
||||
exclude_patterns: matches
|
||||
.values_of("exclude")
|
||||
.map(|v| v.map(|p| String::from("!") + p).collect())
|
||||
.unwrap_or(vec![]),
|
||||
};
|
||||
|
||||
match RegexBuilder::new(pattern)
|
||||
|
|
14
src/walk.rs
14
src/walk.rs
|
@ -18,6 +18,7 @@ use std::thread;
|
|||
use std::time;
|
||||
|
||||
use ignore::{self, WalkBuilder};
|
||||
use ignore::overrides::OverrideBuilder;
|
||||
use regex::Regex;
|
||||
|
||||
/// The receiver thread can either be buffering results or directly streaming to the console.
|
||||
|
@ -48,6 +49,18 @@ pub fn scan(root: &Path, pattern: Arc<Regex>, config: Arc<FdOptions>) {
|
|||
let (tx, rx) = channel();
|
||||
let threads = config.threads;
|
||||
|
||||
let mut override_builder = OverrideBuilder::new(root);
|
||||
|
||||
for pattern in config.exclude_patterns.iter() {
|
||||
let res = override_builder.add(pattern);
|
||||
if res.is_err() {
|
||||
error(&format!("Error: malformed exclude pattern '{}'", pattern));
|
||||
}
|
||||
}
|
||||
let overrides = override_builder.build().unwrap_or_else(|_| {
|
||||
error("Mismatch in exclude patterns");
|
||||
});
|
||||
|
||||
let walker = WalkBuilder::new(root)
|
||||
.hidden(config.ignore_hidden)
|
||||
.ignore(config.read_ignore)
|
||||
|
@ -55,6 +68,7 @@ pub fn scan(root: &Path, pattern: Arc<Regex>, config: Arc<FdOptions>) {
|
|||
.parents(config.read_ignore)
|
||||
.git_global(config.read_ignore)
|
||||
.git_exclude(config.read_ignore)
|
||||
.overrides(overrides)
|
||||
.follow_links(config.follow_links)
|
||||
.max_depth(config.max_depth)
|
||||
.threads(threads)
|
||||
|
|
|
@ -538,3 +538,50 @@ fn test_symlink() {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Exclude patterns (--exclude)
|
||||
#[test]
|
||||
fn test_excludes() {
|
||||
let te = TestEnv::new();
|
||||
|
||||
te.assert_output(
|
||||
&["--exclude", "*.foo"],
|
||||
"one
|
||||
one/two
|
||||
one/two/C.Foo2
|
||||
one/two/three
|
||||
one/two/three/directory_foo
|
||||
symlink",
|
||||
);
|
||||
|
||||
te.assert_output(
|
||||
&["--exclude", "*.foo", "--exclude", "*.Foo2"],
|
||||
"one
|
||||
one/two
|
||||
one/two/three
|
||||
one/two/three/directory_foo
|
||||
symlink",
|
||||
);
|
||||
|
||||
te.assert_output(
|
||||
&["--exclude", "*.foo", "--exclude", "*.Foo2", "foo"],
|
||||
"one/two/three/directory_foo",
|
||||
);
|
||||
|
||||
te.assert_output(
|
||||
&["--exclude", "one/two", "foo"],
|
||||
"a.foo
|
||||
one/b.foo",
|
||||
);
|
||||
|
||||
te.assert_output(
|
||||
&["--exclude", "one/**/*.foo"],
|
||||
"a.foo
|
||||
one
|
||||
one/two
|
||||
one/two/C.Foo2
|
||||
one/two/three
|
||||
one/two/three/directory_foo
|
||||
symlink",
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue