From 99b2fc214e3783bc079c34826af6d63f4430cb4b Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Mon, 17 Jun 2024 19:47:57 -0400 Subject: [PATCH] feat: add --no-|show-symlinks flags for filtering output --- README.md | 2 ++ completions/fish/eza.fish | 2 ++ completions/nush/eza.nu | 2 ++ completions/zsh/_eza | 2 ++ man/eza.1.md | 5 +++++ src/fs/filter.rs | 38 +++++++++++++++++++++++++++++++------- src/options/filter.rs | 4 ++++ src/options/flags.rs | 4 +++- 8 files changed, 51 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 97645000..e28d7643 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,8 @@ eza’s options are almost, but not quite, entirely unlike `ls`’s. Quick overv - **--group-directories-first**: list directories before other files - **-D**, **--only-dirs**: list only directories - **-f**, **--only-files**: list only files +- **--no-symlinks**: don't show symbolic links +- **--show-symlinks**: explicitly show links (with `--only-dirs`, `--only-files`, to show symlinks that match the filter) - **--git-ignore**: ignore files mentioned in `.gitignore` - **-I**, **--ignore-glob=(globs)**: glob patterns (pipe-separated) of files to ignore diff --git a/completions/fish/eza.fish b/completions/fish/eza.fish index 97a579c6..91334b58 100644 --- a/completions/fish/eza.fish +++ b/completions/fish/eza.fish @@ -81,6 +81,8 @@ complete -c eza -s s -l sort -d "Which field to sort by" -x -a " complete -c eza -s I -l ignore-glob -d "Ignore files that match these glob patterns" -r complete -c eza -s D -l only-dirs -d "List only directories" complete -c eza -s f -l only-files -d "List only files" +complete -c eza -l show-symlinks -d "Explicitly show symbolic links (For use with --only-dirs | --only-files)" +complete -c eza -l no-symlinks -d "Do not show symbolic links" # Long view options complete -c eza -s b -l binary -d "List file sizes with binary prefixes" diff --git a/completions/nush/eza.nu b/completions/nush/eza.nu index 1859c0a1..f3d42d33 100644 --- a/completions/nush/eza.nu +++ b/completions/nush/eza.nu @@ -30,6 +30,8 @@ export extern "eza" [ --sort(-s) # Which field to sort by --only-dirs(-D) # List only directories --only-files(-f) # List only files + --show-symlinks # Explicitly show symbolic links (for use with --only-dirs | --only-files) + --no-symlinks # Do not show symbolic links --binary(-b) # List file sizes with binary prefixes --bytes(-B) # List file sizes in bytes, without any prefixes --group(-g) # List each file's group diff --git a/completions/zsh/_eza b/completions/zsh/_eza index ed1f0c29..f672e0a5 100644 --- a/completions/zsh/_eza +++ b/completions/zsh/_eza @@ -33,6 +33,8 @@ __eza() { {-A,--almost-all}"[Equivalent to --all; included for compatibility with \'ls -A\']" \ {-d,--list-dirs}"[List directories like regular files]" \ {-D,--only-dirs}"[List only directories]" \ + --no-symlinks"[Do not show symbolic links]" + --show-symlinks"[Explictly show symbolic links: for use with '--only-dirs'| '--only-files']" {-f,--only-files}"[List only files]" \ {-L,--level}"+[Limit the depth of recursion]" \ {-w,--width}"+[Limits column output of grid, 0 implies auto-width]" \ diff --git a/man/eza.1.md b/man/eza.1.md index a6d43fd8..56873d9c 100644 --- a/man/eza.1.md +++ b/man/eza.1.md @@ -166,6 +166,11 @@ Sort fields starting with a capital letter will sort uppercase before lowercase: `-f`, `--only-files` : List only files, not directories. +`--show-symlinks` +: Explicitly show symbolic links (when used with `--only-files` | `--only-dirs`) + +`--no-symlinks` +: Do not show symbolic links LONG VIEW OPTIONS ================= diff --git a/src/fs/filter.rs b/src/fs/filter.rs index 162cf0ac..2fe8a4e1 100644 --- a/src/fs/filter.rs +++ b/src/fs/filter.rs @@ -21,6 +21,12 @@ pub enum FileFilterFlags { /// Whether to only show files. OnlyFiles, + + /// Whether to ignore symlinks + NoSymlinks, + + /// Whether to explicitly show symlinks + ShowSymlinks, } /// The **file filter** processes a list of files before displaying them to @@ -68,28 +74,47 @@ pub struct FileFilter { /// Whether to ignore Git-ignored patterns. pub git_ignore: GitIgnore, + + /// Whether to ignore symlinks + pub no_symlinks: bool, + + /// Whether to explicitly show symlinks + pub show_symlinks: bool, } impl FileFilter { /// Remove every file in the given vector that does *not* pass the /// filter predicate for files found inside a directory. pub fn filter_child_files(&self, files: &mut Vec>) { - use FileFilterFlags::{OnlyDirs, OnlyFiles}; + use FileFilterFlags::{NoSymlinks, OnlyDirs, OnlyFiles, ShowSymlinks}; files.retain(|f| !self.ignore_patterns.is_ignored(&f.name)); match ( self.flags.contains(&OnlyDirs), self.flags.contains(&OnlyFiles), + self.flags.contains(&NoSymlinks), + self.flags.contains(&ShowSymlinks), ) { - (true, false) => { - // On pass -'-only-dirs' flag only + (true, false, false, false) => { + // On pass '--only-dirs' flag only files.retain(File::is_directory); } - (false, true) => { - // On pass -'-only-files' flag only + (true, false, true, false) => { + files.retain(File::is_directory); + } + (true, false, false, true) => { + files.retain(|f| f.is_directory() || f.points_to_directory()); + } + (false, true, false, false) => { files.retain(File::is_file); } + (false, true, false, true) => { + files.retain(|f| f.is_file() || f.is_link() && !f.points_to_directory()); + } + (false, false, true, false) => { + files.retain(|f| !f.is_link()); + } _ => {} } } @@ -99,7 +124,7 @@ impl FileFilter { /// /// 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 + /// 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! @@ -249,7 +274,6 @@ impl SortField { Self::ChangedDate => a.changed_time().cmp(&b.changed_time()), Self::CreatedDate => a.created_time().cmp(&b.created_time()), Self::ModifiedAge => b.modified_time().cmp(&a.modified_time()), // flip b and a - Self::FileType => match a.type_char().cmp(&b.type_char()) { // todo: this recomputes Ordering::Equal => natord::compare(&a.name, &b.name), order => order, diff --git a/src/options/filter.rs b/src/options/filter.rs index 745fda68..fce5a926 100644 --- a/src/options/filter.rs +++ b/src/options/filter.rs @@ -18,6 +18,8 @@ impl FileFilter { (matches.has(&flags::REVERSE)?, FFF::Reverse), (matches.has(&flags::ONLY_DIRS)?, FFF::OnlyDirs), (matches.has(&flags::ONLY_FILES)?, FFF::OnlyFiles), + (matches.has(&flags::NO_SYMLINKS)?, FFF::NoSymlinks), + (matches.has(&flags::SHOW_SYMLINKS)?, FFF::ShowSymlinks), ] { if *has { filter_flags.push(flag.clone()); @@ -27,6 +29,8 @@ impl FileFilter { #[rustfmt::skip] return Ok(Self { list_dirs_first: matches.has(&flags::DIRS_FIRST)?, + no_symlinks: filter_flags.contains(&FFF::NoSymlinks), + show_symlinks: filter_flags.contains(&FFF::ShowSymlinks), flags: filter_flags, sort_field: SortField::deduce(matches)?, dot_filter: DotFilter::deduce(matches)?, diff --git a/src/options/flags.rs b/src/options/flags.rs index 3d6d8359..8cd538cf 100644 --- a/src/options/flags.rs +++ b/src/options/flags.rs @@ -41,6 +41,8 @@ pub static GIT_IGNORE: Arg = Arg { short: None, long: "git-ignore", t pub static DIRS_FIRST: Arg = Arg { short: None, long: "group-directories-first", takes_value: TakesValue::Forbidden }; pub static ONLY_DIRS: Arg = Arg { short: Some(b'D'), long: "only-dirs", takes_value: TakesValue::Forbidden }; pub static ONLY_FILES: Arg = Arg { short: Some(b'f'), long: "only-files", takes_value: TakesValue::Forbidden }; +pub static NO_SYMLINKS: Arg = Arg { short: None, long: "no-symlinks", takes_value: TakesValue::Forbidden }; +pub static SHOW_SYMLINKS: Arg = Arg { short: None, long: "show-symlinks", takes_value: TakesValue::Forbidden }; const SORTS: Values = &[ "name", "Name", "size", "extension", "Extension", "modified", "changed", "accessed", "created", "inode", "type", "none" ]; @@ -97,7 +99,7 @@ pub static ALL_ARGS: Args = Args(&[ &BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED, &BLOCKSIZE, &TOTAL_SIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK, &MOUNTS, - &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &SMART_GROUP, + &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &SMART_GROUP, &NO_SYMLINKS, &SHOW_SYMLINKS, &GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT, &EXTENDED, &OCTAL, &SECURITY_CONTEXT, &STDIN, &FILE_FLAGS