mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-26 05:47:32 +00:00
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.
This commit is contained in:
parent
a6712994c5
commit
95596297a9
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -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)",
|
||||
@ -85,6 +86,11 @@ dependencies = [
|
||||
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "iso8601"
|
||||
version = "0.1.1"
|
||||
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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() {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ impl Options {
|
||||
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");
|
||||
|
@ -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 {
|
||||
|
1
xtests/ignores_ogg
Normal file
1
xtests/ignores_ogg
Normal file
@ -0,0 +1 @@
|
||||
[36m/home/vagrant/testcases/file-types/[38;5;92mmusic.mp3[0m
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user