summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen S <ogham@bsago.me>2016-10-30 14:43:33 +0000
committerBen S <ogham@bsago.me>2016-10-30 14:43:33 +0000
commit95596297a910455171647dce00781563e4a19d8a (patch)
tree0dfb4d65a48fcf615560c807d08469d52b59e367
parenta6712994c5d6e496f1b1c4a29070daaa82deefd0 (diff)
Basic glob ignoring
See #97 and recently #130 too. This allows the user to pass in options such as "--ignore '*.pyc'" to not list any files ending in '.pyc' in the output. It uses the Rust glob crate and currently does a simple split on pipe, without any escaping, so it’s not really *complete*, but is at least something.
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/exa.rs5
-rw-r--r--src/options/filter.rs54
-rw-r--r--src/options/misfire.rs11
-rw-r--r--src/options/mod.rs9
-rw-r--r--src/output/details.rs2
-rw-r--r--xtests/ignores_ogg1
-rwxr-xr-xxtests/run.sh4
9 files changed, 83 insertions, 11 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 82dbfa7..e2e75a6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7,6 +7,7 @@ dependencies = [
"datetime 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"locale 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -86,6 +87,11 @@ dependencies = [
]
[[package]]
+name = "glob"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "iso8601"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -390,6 +396,7 @@ dependencies = [
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
"checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
"checksum git2 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "123c3149e1d558792dae893ac01495919ca46b8734a7cf246ee34bf475860c9b"
+"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum iso8601 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11dc464f8c6f17595d191447c9c6559298b2d023d6f846a4a23ac7ea3c46c477"
"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
diff --git a/Cargo.toml b/Cargo.toml
index ec548fc..251104c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,7 @@ ansi_term = "0.7.1"
bitflags = "0.1"
datetime = "0.4.3"
getopts = "0.2.14"
+glob = "0.2"
lazy_static = "0.1.*"
libc = "0.2.9"
locale = "0.2.1"
diff --git a/src/exa.rs b/src/exa.rs
index e02ec1a..50515ec 100644
--- a/src/exa.rs
+++ b/src/exa.rs
@@ -4,6 +4,7 @@
extern crate ansi_term;
extern crate datetime;
extern crate getopts;
+extern crate glob;
extern crate libc;
extern crate locale;
extern crate natord;
@@ -94,7 +95,9 @@ impl<'w, W: Write + 'w> Exa<'w, W> {
let no_files = files.is_empty();
let is_only_dir = dirs.len() == 1 && no_files;
+ self.options.filter.filter_argument_files(&mut files);
try!(self.print_files(None, files));
+
self.print_dirs(dirs, no_files, is_only_dir)
}
@@ -122,7 +125,7 @@ impl<'w, W: Write + 'w> Exa<'w, W> {
}
};
- self.options.filter.filter_files(&mut children);
+ self.options.filter.filter_child_files(&mut children);
self.options.filter.sort_files(&mut children);
if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
diff --git a/src/options/filter.rs b/src/options/filter.rs
index 1ff8647..ce7d4ba 100644
--- a/src/options/filter.rs
+++ b/src/options/filter.rs
@@ -2,6 +2,7 @@ use std::cmp::Ordering;
use std::os::unix::fs::MetadataExt;
use getopts;
+use glob;
use natord;
use fs::File;
@@ -60,6 +61,10 @@ pub struct FileFilter {
/// evaluation that goes through my home directory is slowed down by
/// this accumulated sludge.
show_invisibles: bool,
+
+ /// Glob patterns to ignore. Any file name that matches *any* of these
+ /// patterns won't be displayed in the list.
+ ignore_patterns: IgnorePatterns,
}
impl FileFilter {
@@ -67,22 +72,36 @@ impl FileFilter {
/// Determines the set of file filter options to use, based on the user’s
/// command-line arguments.
pub fn deduce(matches: &getopts::Matches) -> Result<FileFilter, Misfire> {
- let sort_field = try!(SortField::deduce(&matches));
-
Ok(FileFilter {
list_dirs_first: matches.opt_present("group-directories-first"),
reverse: matches.opt_present("reverse"),
+ sort_field: try!(SortField::deduce(matches)),
show_invisibles: matches.opt_present("all"),
- sort_field: sort_field,
+ ignore_patterns: try!(IgnorePatterns::deduce(matches)),
})
}
/// Remove every file in the given vector that does *not* pass the
- /// filter predicate.
- pub fn filter_files(&self, files: &mut Vec<File>) {
+ /// filter predicate for files found inside a directory.
+ pub fn filter_child_files(&self, files: &mut Vec<File>) {
if !self.show_invisibles {
files.retain(|f| !f.is_dotfile());
}
+
+ files.retain(|f| !self.ignore_patterns.is_ignored(f));
+ }
+
+ /// Remove every file in the given vector that does *not* pass the
+ /// filter predicate for file names specified on the command-line.
+ ///
+ /// The rules are different for these types of files than the other
+ /// type because the ignore rules can be used with globbing. For
+ /// example, running "exa -I='*.tmp' .vimrc" shouldn't filter out the
+ /// dotfile, because it's been directly specified. But running
+ /// "exa -I='*.ogg' music/*" should filter out the ogg files obtained
+ /// from the glob, even though the globbing is done by the shell!
+ pub fn filter_argument_files(&self, files: &mut Vec<File>) {
+ files.retain(|f| !self.ignore_patterns.is_ignored(f));
}
/// Sort the files in the given vector based on the sort field option.
@@ -229,3 +248,28 @@ impl SortField {
}
}
}
+
+
+#[derive(PartialEq, Default, Debug, Clone)]
+struct IgnorePatterns {
+ patterns: Vec<glob::Pattern>,
+}
+
+impl IgnorePatterns {
+ /// Determines the set of file filter options to use, based on the user’s
+ /// command-line arguments.
+ pub fn deduce(matches: &getopts::Matches) -> Result<IgnorePatterns, Misfire> {
+ let patterns = match matches.opt_str("ignore-glob") {
+ None => Ok(Vec::new()),
+ Some(is) => is.split('|').map(|a| glob::Pattern::new(a)).collect(),
+ };
+
+ Ok(IgnorePatterns {
+ patterns: try!(patterns),
+ })
+ }
+
+ fn is_ignored(&self, file: &File) -> bool {
+ self.patterns.iter().any(|p| p.matches(&file.name))
+ }
+}
diff --git a/src/options/misfire.rs b/src/options/misfire.rs
index 0d7ab73..1ee437d 100644
--- a/src/options/misfire.rs
+++ b/src/options/misfire.rs
@@ -2,6 +2,7 @@ use std::fmt;
use std::num::ParseIntError;
use getopts;
+use glob;
/// A list of legal choices for an argument-taking option
@@ -45,6 +46,9 @@ pub enum Misfire {
/// A numeric option was given that failed to be parsed as a number.
FailedParse(ParseIntError),
+
+ /// A glob ignore was given that failed to be parsed as a pattern.
+ FailedGlobPattern(String),
}
impl Misfire {
@@ -66,6 +70,12 @@ impl Misfire {
}
}
+impl From<glob::PatternError> for Misfire {
+ fn from(error: glob::PatternError) -> Misfire {
+ Misfire::FailedGlobPattern(error.to_string())
+ }
+}
+
impl fmt::Display for Misfire {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Misfire::*;
@@ -80,6 +90,7 @@ impl fmt::Display for Misfire {
Useless(a, true, b) => write!(f, "Option --{} is useless given option --{}.", a, b),
Useless2(a, b1, b2) => write!(f, "Option --{} is useless without options --{} or --{}.", a, b1, b2),
FailedParse(ref e) => write!(f, "Failed to parse number: {}", e),
+ FailedGlobPattern(ref e) => write!(f, "Failed to parse glob pattern: {}", e),
}
}
}
diff --git a/src/options/mod.rs b/src/options/mod.rs
index bcac05f..7dd329f 100644
--- a/src/options/mod.rs
+++ b/src/options/mod.rs
@@ -60,10 +60,11 @@ impl Options {
// Filtering and sorting options
opts.optflag("", "group-directories-first", "list directories before other files");
- opts.optflag("a", "all", "show dot-files");
- opts.optflag("d", "list-dirs", "list directories as regular files");
- opts.optflag("r", "reverse", "reverse order of files");
- opts.optopt ("s", "sort", "field to sort by", "WORD");
+ opts.optflag("a", "all", "show dot-files");
+ opts.optflag("d", "list-dirs", "list directories as regular files");
+ opts.optflag("r", "reverse", "reverse order of files");
+ opts.optopt ("s", "sort", "field to sort by", "WORD");
+ opts.optopt ("I", "ignore-glob", "patterns (|-separated) of names to ignore", "GLOBS");
// Long view options
opts.optflag("b", "binary", "use binary prefixes in file sizes");
diff --git a/src/output/details.rs b/src/output/details.rs
index f71296b..dba804c 100644
--- a/src/output/details.rs
+++ b/src/output/details.rs
@@ -333,7 +333,7 @@ impl Details {
}
}
- self.filter.filter_files(&mut files);
+ self.filter.filter_child_files(&mut files);
if !files.is_empty() {
for xattr in egg.xattrs {
diff --git a/xtests/ignores_ogg b/xtests/ignores_ogg
new file mode 100644
index 0000000..5b350a7
--- /dev/null
+++ b/xtests/ignores_ogg
@@ -0,0 +1 @@
+/home/vagrant/testcases/file-types/music.mp3
diff --git a/xtests/run.sh b/xtests/run.sh
index ea30312..998852d 100755
--- a/xtests/run.sh
+++ b/xtests/run.sh
@@ -46,6 +46,10 @@ $exa $testcases/permissions -lghR 2>&1 | diff -q - $results/permissions || exit
# File types
$exa $testcases/file-types -1 2>&1 | diff -q - $results/file-types || exit 1
+# Ignores
+$exa $testcases/file-types/music.* -I "*.ogg" -1 2>&1 | diff -q - $results/ignores_ogg || exit 1
+$exa $testcases/file-types/music.* -I "*.ogg|*.mp3" -1 2>&1 | diff -q - $results/empty || exit 1
+
# Links
$exa $testcases/links -1 2>&1 | diff -q - $results/links_1 || exit 1
$exa $testcases/links -T 2>&1 | diff -q - $results/links_T || exit 1