Browse Source

Merge branch 'main' into ignore-dot-git-when-using-git-ignore-flag

Christina Sørensen 2 years ago
parent
commit
71661e5751

+ 61 - 4
Cargo.lock

@@ -84,6 +84,7 @@ version = "0.11.0"
 dependencies = [
  "ansiterm",
  "datetime",
+ "gethostname",
  "git2",
  "glob",
  "lazy_static",
@@ -94,11 +95,13 @@ dependencies = [
  "num_cpus",
  "number_prefix",
  "phf",
+ "proc-mounts",
  "scoped_threadpool",
  "term_grid",
  "terminal_size",
  "timeago",
  "unicode-width",
+ "urlencoding",
  "uzers",
  "zoneinfo_compiled",
 ]
@@ -113,6 +116,16 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "gethostname"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+dependencies = [
+ "libc",
+ "windows-targets",
+]
+
 [[package]]
 name = "git2"
 version = "0.18.0"
@@ -129,9 +142,9 @@ dependencies = [
 
 [[package]]
 name = "glob"
-version = "0.3.1"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
 [[package]]
 name = "hermit-abi"
@@ -288,6 +301,15 @@ dependencies = [
  "unicode-width",
 ]
 
+[[package]]
+name = "partition-identity"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa925f9becb532d758b0014b472c576869910929cf4c3f8054b386f19ab9e21"
+dependencies = [
+ "thiserror",
+]
+
 [[package]]
 name = "percent-encoding"
 version = "2.1.0"
@@ -351,6 +373,15 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "proc-mounts"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d652f8435d0ab70bf4f3590a6a851d59604831a458086541b95238cc51ffcf2"
+dependencies = [
+ "partition-identity",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.33"
@@ -409,9 +440,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
 
 [[package]]
 name = "syn"
-version = "2.0.29"
+version = "2.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -437,6 +468,26 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "timeago"
 version = "0.4.1"
@@ -500,6 +551,12 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "urlencoding"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
+
 [[package]]
 name = "uzers"
 version = "0.11.2"

+ 5 - 0
Cargo.toml

@@ -37,6 +37,7 @@ name = "eza"
 
 [dependencies]
 ansiterm = "0.12.2"
+gethostname = "0.4.3"
 glob = "0.3"
 lazy_static = "1.3"
 libc = "0.2"
@@ -51,6 +52,7 @@ term_grid = "0.1"
 terminal_size = "0.2.6"
 timeago = { version = "0.4.1", default-features = false }
 unicode-width = "0.1"
+urlencoding = "2.1.3"
 zoneinfo_compiled = "0.5.1"
 
 [dependencies.datetime]
@@ -67,6 +69,9 @@ default-features = false
 # See: https://github.com/eza-community/eza/pull/192
 features = ["vendored-libgit2"]
 
+[target.'cfg(target_os = "linux")'.dependencies]
+proc-mounts = "0.3"
+
 [target.'cfg(unix)'.dependencies]
 uzers = "0.11.2"
 

+ 2 - 0
README.md

@@ -34,6 +34,7 @@ By deliberately making some decisions differently, eza attempts to be a more fea
 
  -   Fixes [“The Grid Bug”](https://github.com/eza-community/eza/issues/66#issuecomment-1656758327) introduced in exa 2021.
  -   Hyperlink support.
+ -   Mount point details.
  -   Selinux context output.
  -   Git repo status output.
  -   Human readable relative dates.
@@ -189,6 +190,7 @@ These options are available when running with `--long` (`-l`):
 - **-H**, **--links**: list each file’s number of hard links
 - **-i**, **--inode**: list each file’s inode number
 - **-m**, **--modified**: use the modified timestamp field
+- **-M**, **--mounts**: Show mount details (Linux only).
 - **-S**, **--blocksize**: show size of allocated file system blocks
 - **-t**, **--time=(field)**: which timestamp field to use
 - **-u**, **--accessed**: use the accessed timestamp field

+ 1 - 0
completions/fish/eza.fish

@@ -89,6 +89,7 @@ complete -c eza -s o -l octal-permissions -d "List each file's permission in oct
 complete -c eza -l no-filesize -d "Suppress the filesize field"
 complete -c eza -l no-user -d "Suppress the user field"
 complete -c eza -l no-time -d "Suppress the time field"
+complete -c eza -s M -l mounts -d "Show mount details"
 
 # Optional extras
 complete -c eza -l git -d "List each file's Git status, if tracked"

+ 1 - 0
completions/zsh/_eza

@@ -60,6 +60,7 @@ __eza() {
         --git-repos-no-status"[List each git-repos branch name (much faster)]" \
         {-@,--extended}"[List each file's extended attributes and sizes]" \
         {-Z,--context}"[List each file's security context]" \
+        {-M,--mounts}"[Show mount details (long mode only)]" \
         '*:filename:_files'
 }
 

+ 3 - 0
man/eza.1.md

@@ -149,6 +149,9 @@ These options are available when running with `--long` (`-l`):
 `-m`, `--modified`
 : Use the modified timestamp field.
 
+`-M`, `--mounts`
+: Show mount details (Linux only)
+
 `-n`, `--numeric`
 : List numeric user and group IDs.
 

+ 3 - 0
man/eza_colors.5.md

@@ -220,6 +220,9 @@ LIST OF CODES
 `bO`
 : the overlay style for broken symlink paths
 
+`mp`
+: a mount point
+
 Values in `EXA_COLORS` override those given in `LS_COLORS`, so you don’t need to re-write an existing `LS_COLORS` variable with proprietary extensions.
 
 

+ 38 - 5
src/fs/file.rs

@@ -12,11 +12,14 @@ use std::time::{Duration, UNIX_EPOCH};
 
 use log::*;
 
+use crate::ALL_MOUNTS;
 use crate::fs::dir::Dir;
 use crate::fs::feature::xattr;
 use crate::fs::feature::xattr::{FileAttributes, Attribute};
 use crate::fs::fields as f;
 
+use super::mounts::MountedFs;
+
 
 /// A **File** is a wrapper around one of Rust’s `PathBuf` values, along with
 /// associated data about the file.
@@ -79,7 +82,9 @@ pub struct File<'dir> {
     pub deref_links: bool,
     /// The extended attributes of this file.
     pub extended_attributes: Vec<Attribute>,
-}
+
+    /// The absolute value of this path, used to look up mount points.
+    pub absolute_path: PathBuf,}
 
 impl<'dir> File<'dir> {
     pub fn from_args<PD, FN>(path: PathBuf, parent_dir: PD, filename: FN, deref_links: bool) -> io::Result<File<'dir>>
@@ -94,8 +99,9 @@ impl<'dir> File<'dir> {
         let metadata   = std::fs::symlink_metadata(&path)?;
         let is_all_all = false;
         let extended_attributes = File::gather_extended_attributes(&path);
+        let absolute_path = std::fs::canonicalize(&path)?;
 
-        Ok(File { name, ext, path, metadata, parent_dir, is_all_all, deref_links, extended_attributes })
+        Ok(File { name, ext, path, metadata, parent_dir, is_all_all, deref_links, extended_attributes, absolute_path })
     }
 
     pub fn new_aa_current(parent_dir: &'dir Dir) -> io::Result<File<'dir>> {
@@ -107,8 +113,9 @@ impl<'dir> File<'dir> {
         let is_all_all = true;
         let parent_dir = Some(parent_dir);
         let extended_attributes = File::gather_extended_attributes(&path);
+        let absolute_path = std::fs::canonicalize(&path)?;
 
-        Ok(File { path, parent_dir, metadata, ext, name: ".".into(), is_all_all, deref_links: false, extended_attributes })
+        Ok(File { path, parent_dir, metadata, ext, name: ".".into(), is_all_all, deref_links: false, extended_attributes, absolute_path })
     }
 
     pub fn new_aa_parent(path: PathBuf, parent_dir: &'dir Dir) -> io::Result<File<'dir>> {
@@ -119,8 +126,9 @@ impl<'dir> File<'dir> {
         let is_all_all = true;
         let parent_dir = Some(parent_dir);
         let extended_attributes = File::gather_extended_attributes(&path);
+        let absolute_path = std::fs::canonicalize(&path)?;
 
-        Ok(File { path, parent_dir, metadata, ext, name: "..".into(), is_all_all, deref_links: false, extended_attributes })
+        Ok(File { path, parent_dir, metadata, ext, name: "..".into(), is_all_all, deref_links: false, extended_attributes, absolute_path })
     }
 
     /// A file’s name is derived from its string. This needs to handle directories
@@ -243,6 +251,21 @@ impl<'dir> File<'dir> {
         self.metadata.file_type().is_socket()
     }
 
+    /// Whether this file is a mount point
+    pub fn is_mount_point(&self) -> bool {
+        if cfg!(target_os = "linux") && self.is_directory() {
+            return ALL_MOUNTS.contains_key(&self.absolute_path);
+        }
+        false
+    }
+
+    /// The filesystem device and type for a mount point
+    pub fn mount_point_info(&self) -> Option<&MountedFs> {
+        if cfg!(target_os = "linux") {
+            return ALL_MOUNTS.get(&self.absolute_path);
+        }
+        None
+    }
 
     /// Re-prefixes the path pointed to by this file, if it’s a symlink, to
     /// make it an absolute path that can be accessed from whichever
@@ -293,7 +316,17 @@ impl<'dir> File<'dir> {
                 let ext  = File::ext(&path);
                 let name = File::filename(&path);
                 let extended_attributes = File::gather_extended_attributes(&absolute_path);
-                let file = File { parent_dir: None, path, ext, metadata, name, is_all_all: false, deref_links: self.deref_links, extended_attributes };
+                let file = File {
+                    parent_dir: None,
+                    path,
+                    ext,
+                    metadata,
+                    name,
+                    is_all_all: false,
+                    deref_links: self.deref_links,
+                    extended_attributes,
+                    absolute_path
+                };
                 FileTarget::Ok(Box::new(file))
             }
             Err(e) => {

+ 1 - 0
src/fs/mod.rs

@@ -8,3 +8,4 @@ pub mod dir_action;
 pub mod feature;
 pub mod fields;
 pub mod filter;
+pub mod mounts;

+ 6 - 0
src/fs/mounts.rs

@@ -0,0 +1,6 @@
+/// Details of a mounted filesystem.
+pub struct MountedFs {
+    pub dest: String,
+    pub fstype: String,
+    pub source: String,
+}

+ 58 - 25
src/info/filetype.rs

@@ -39,27 +39,23 @@ const FILENAME_TYPES: Map<&'static str, FileType> = phf_map! {
     "build.gradle"       => FileType::Immediate,
     "build.sbt"          => FileType::Immediate,
     "build.xml"          => FileType::Immediate,
-    "Cargo.lock"         => FileType::Immediate,
     "Cargo.toml"         => FileType::Immediate,
     "CMakeLists.txt"     => FileType::Immediate,
     "composer.json"      => FileType::Immediate,
-    "configure.ac"       => FileType::Immediate,
-    "Configure.ac"       => FileType::Immediate,
+    "configure"          => FileType::Immediate,
     "Containerfile"      => FileType::Immediate,
     "Dockerfile"         => FileType::Immediate,
     "Earthfile"          => FileType::Immediate,
-    "flake.lock"         => FileType::Immediate,
     "flake.nix"          => FileType::Immediate,
     "Gemfile"            => FileType::Immediate,
     "GNUmakefile"        => FileType::Immediate,
     "Gruntfile.coffee"   => FileType::Immediate,
     "Gruntfile.js"       => FileType::Immediate,
+    "jsconfig.json"      => FileType::Immediate,
     "Justfile"           => FileType::Immediate,
     "justfile"           => FileType::Immediate,
     "Makefile"           => FileType::Immediate,
     "makefile"           => FileType::Immediate,
-    "Makefile.in"        => FileType::Immediate,
-    "makefile.in"        => FileType::Immediate,
     "meson.build"        => FileType::Immediate,
     "mix.exs"            => FileType::Immediate,
     "package.json"       => FileType::Immediate,
@@ -68,6 +64,7 @@ const FILENAME_TYPES: Map<&'static str, FileType> = phf_map! {
     "Podfile"            => FileType::Immediate,
     "pom.xml"            => FileType::Immediate,
     "Procfile"           => FileType::Immediate,
+    "pyproject.toml"     => FileType::Immediate,
     "Rakefile"           => FileType::Immediate,
     "RoboFile.php"       => FileType::Immediate,
     "SConstruct"         => FileType::Immediate,
@@ -76,6 +73,13 @@ const FILENAME_TYPES: Map<&'static str, FileType> = phf_map! {
     "webpack.config.cjs" => FileType::Immediate,
     "webpack.config.js"  => FileType::Immediate,
     "WORKSPACE"          => FileType::Immediate,
+    /* Cryptology files */
+    "id_dsa"             => FileType::Crypto,
+    "id_ecdsa"           => FileType::Crypto,
+    "id_ecdsa_sk"        => FileType::Crypto,
+    "id_ed25519"         => FileType::Crypto,
+    "id_ed25519_sk"      => FileType::Crypto,
+    "id_rsa"             => FileType::Crypto,
 };
 
 /// Mapping from lowercase file extension to file type.  If an image, video, music, or lossless
@@ -93,6 +97,7 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "dvi"        => FileType::Image,
     "eps"        => FileType::Image,
     "gif"        => FileType::Image,
+    "heic"       => FileType::Image,
     "heif"       => FileType::Image,
     "ico"        => FileType::Image,
     "j2c"        => FileType::Image,
@@ -115,6 +120,7 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "pnm"        => FileType::Image,
     "ppm"        => FileType::Image,
     "ps"         => FileType::Image,
+    "psd"        => FileType::Image,
     "pxm"        => FileType::Image,
     "raw"        => FileType::Image,
     "stl"        => FileType::Image,
@@ -122,11 +128,12 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "tif"        => FileType::Image,
     "tiff"       => FileType::Image,
     "webp"       => FileType::Image,
+    "xcf"        => FileType::Image,
     "xpm"        => FileType::Image,
     /* Video files */
     "avi"        => FileType::Video,
     "flv"        => FileType::Video,
-    "heic"       => FileType::Video,
+    "heics"      => FileType::Video,
     "m2ts"       => FileType::Video,
     "m2v"        => FileType::Video,
     "m4v"        => FileType::Video,
@@ -137,6 +144,7 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "mpg"        => FileType::Video,
     "ogm"        => FileType::Video,
     "ogv"        => FileType::Video,
+    "video"      => FileType::Video,
     "vob"        => FileType::Video,
     "webm"       => FileType::Video,
     "wmv"        => FileType::Video,
@@ -155,24 +163,34 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "flac"       => FileType::Lossless,
     "wav"        => FileType::Lossless,
     /* Cryptology files */
-    "asc"        => FileType::Crypto,
-    "enc"        => FileType::Crypto,
-    "gpg"        => FileType::Crypto,
-    "p12"        => FileType::Crypto,
-    "pfx"        => FileType::Crypto,
-    "pgp"        => FileType::Crypto,
-    "sig"        => FileType::Crypto,
-    "signature"  => FileType::Crypto,
+    "asc"        => FileType::Crypto, // GnuPG ASCII armored file
+    "gpg"        => FileType::Crypto, // GnuPG encrypted file
+    "kbx"        => FileType::Crypto, // GnuPG keybox
+    "md5"        => FileType::Crypto, // MD5 checksum
+    "p12"        => FileType::Crypto, // PKCS#12 certificate (Netscape)
+    "pem"        => FileType::Crypto, // Privacy enhanced mail certificate
+    "pfx"        => FileType::Crypto, // PKCS#12 certificate (Microsoft)
+    "pgp"        => FileType::Crypto, // PGP security key
+    "pub"        => FileType::Crypto, // Public key
+    "sha1"       => FileType::Crypto, // SHA-1 hash
+    "sha224"     => FileType::Crypto, // SHA-224 hash
+    "sha256"     => FileType::Crypto, // SHA-256 hash
+    "sha384"     => FileType::Crypto, // SHA-384 hash
+    "sha512"     => FileType::Crypto, // SHA-512 hash
+    "sig"        => FileType::Crypto, // GnuPG signed file
+    "signature"  => FileType::Crypto, // e-Filing Digital Signature File (India)
     /* Document files */
     "djvu"       => FileType::Document,
     "doc"        => FileType::Document,
     "docx"       => FileType::Document,
     "eml"        => FileType::Document,
     "fotd"       => FileType::Document,
+    "gdoc"       => FileType::Document,
     "key"        => FileType::Document,
     "keynote"    => FileType::Document,
     "numbers"    => FileType::Document,
     "odp"        => FileType::Document,
+    "ods"        => FileType::Document,
     "odt"        => FileType::Document,
     "pages"      => FileType::Document,
     "pdf"        => FileType::Document,
@@ -180,13 +198,15 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "pptx"       => FileType::Document,
     "rtf"        => FileType::Document,
     "xls"        => FileType::Document,
+    "xlsm"       => FileType::Document,
     "xlsx"       => FileType::Document,
     /* Compressed/archive files */
     "7z"         => FileType::Compressed,
-    "a"          => FileType::Compressed,
     "ar"         => FileType::Compressed,
+    "br"         => FileType::Compressed,
     "bz"         => FileType::Compressed,
     "bz2"        => FileType::Compressed,
+    "bz3"        => FileType::Compressed,
     "cpio"       => FileType::Compressed,
     "deb"        => FileType::Compressed,
     "dmg"        => FileType::Compressed,
@@ -197,7 +217,9 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "lzh"        => FileType::Compressed,
     "lzma"       => FileType::Compressed,
     "lzo"        => FileType::Compressed,
-    "par"        => FileType::Compressed,
+    "phar"       => FileType::Compressed,
+    "qcow"       => FileType::Compressed,
+    "qcow2"      => FileType::Compressed,
     "rar"        => FileType::Compressed,
     "rpm"        => FileType::Compressed,
     "tar"        => FileType::Compressed,
@@ -209,8 +231,10 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "tlz"        => FileType::Compressed,
     "txz"        => FileType::Compressed,
     "tz"         => FileType::Compressed,
-    "tzo"        => FileType::Compressed,
     "xz"         => FileType::Compressed,
+    "vdi"        => FileType::Compressed,
+    "vhd"        => FileType::Compressed,
+    "vmdk"       => FileType::Compressed,
     "z"          => FileType::Compressed,
     "zip"        => FileType::Compressed,
     "zst"        => FileType::Compressed,
@@ -218,18 +242,27 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "bak"        => FileType::Temp,
     "bk"         => FileType::Temp,
     "bkp"        => FileType::Temp,
+    "download"   => FileType::Temp,
     "swn"        => FileType::Temp,
     "swo"        => FileType::Temp,
     "swp"        => FileType::Temp,
     "tmp"        => FileType::Temp,
     /* Compiler output files */
-    "class"      => FileType::Compiled,
-    "elc"        => FileType::Compiled,
-    "hi"         => FileType::Compiled,
-    "ko"         => FileType::Compiled,
-    "o"          => FileType::Compiled,
-    "pyc"        => FileType::Compiled,
-    "zwc"        => FileType::Compiled,
+    "a"          => FileType::Compiled, // Unix static library
+    "bundle"     => FileType::Compiled, // Mac OS X application bundle
+    "class"      => FileType::Compiled, // Java class file
+    "dll"        => FileType::Compiled, // Windows dynamic link library
+    "dylib"      => FileType::Compiled, // Mach-O dynamic library
+    "elc"        => FileType::Compiled, // Emacs compiled lisp
+    "ko"         => FileType::Compiled, // Linux kernel module
+    "lib"        => FileType::Compiled, // Windows static library
+    "o"          => FileType::Compiled, // Compiled object file
+    "obj"        => FileType::Compiled, // Compiled object file
+    "pyc"        => FileType::Compiled, // Python compiled code
+    "pyd"        => FileType::Compiled, // Python dynamic module
+    "pyo"        => FileType::Compiled, // Python optimized code
+    "so"         => FileType::Compiled, // Unix shared library
+    "zwc"        => FileType::Compiled, // zsh compiled file
 };
 
 impl FileType {

+ 3 - 4
src/info/sources.rs

@@ -12,7 +12,7 @@ impl<'a> File<'a> {
     /// their source file `foo.coffee` exists in the same directory.
     /// For example, `foo.js` is perfectly valid without `foo.coffee`, so we
     /// don’t want to always blindly highlight `*.js` as compiled.
-    /// (See also `FileExtensions#is_compiled`)
+    /// (See also `FileType`)
     pub fn get_source_files(&self) -> Vec<PathBuf> {
         if let Some(ext) = &self.ext {
             match &ext[..] {
@@ -21,7 +21,6 @@ impl<'a> File<'a> {
                 "mjs"   => vec![self.path.with_extension("mts")],  // JavaScript ES Modules source
                 "cjs"   => vec![self.path.with_extension("cts")],  // JavaScript Commonjs Modules source
                 "js"    => vec![self.path.with_extension("coffee"), self.path.with_extension("ts")],  // CoffeeScript, TypeScript
-
                 "aux" |                                          // TeX: auxiliary file
                 "bbl" |                                          // BibTeX bibliography file
                 "bcf" |                                          // biblatex control file
@@ -32,8 +31,8 @@ impl<'a> File<'a> {
                 "lof" |                                          // TeX list of figures
                 "log" |                                          // TeX log file
                 "lot" |                                          // TeX list of tables
-                "out" => vec![self.path.with_extension("tex")],  // hyperref list of bookmarks
-                "toc" => vec![self.path.with_extension("tex")],  // TeX table of contents
+                "out" |                                          // hyperref list of bookmarks
+                "toc" |                                          // TeX table of contents
                 "xdv" => vec![self.path.with_extension("tex")],  // XeTeX dvi
 
                 _ => vec![],  // No source files if none of the above

+ 40 - 0
src/main.rs

@@ -22,6 +22,7 @@
 #![allow(clippy::upper_case_acronyms)]
 #![allow(clippy::wildcard_imports)]
 
+use std::collections::HashMap;
 use std::env;
 use std::ffi::{OsStr, OsString};
 use std::io::{self, Write, ErrorKind};
@@ -31,6 +32,13 @@ use ansiterm::{ANSIStrings, Style};
 
 use log::*;
 
+#[cfg(target_os = "linux")]
+use proc_mounts::MountList;
+
+#[macro_use]
+extern crate lazy_static;
+
+use crate::fs::mounts::MountedFs;
 use crate::fs::{Dir, File};
 use crate::fs::feature::git::GitCache;
 use crate::fs::filter::GitIgnore;
@@ -45,6 +53,38 @@ mod options;
 mod output;
 mod theme;
 
+// A lazily initialised static map of all mounted file systems.
+//
+// The map contains a mapping from the mounted directory path to the
+// corresponding mount information. On Linux systems, this map is populated
+// using the `proc-mounts` crate. If there's an error retrieving the mount
+// list or if we're not running on Linux, the map will be empty.
+//
+// Initialise this at application start so we don't have to look the details
+// up for every directory. Ideally this would only be done if the --mounts
+// option is specified which will be significantly easier once the move
+// to `clap` is complete.
+lazy_static! {
+    static ref ALL_MOUNTS: HashMap<PathBuf, MountedFs> = {
+        #[cfg(target_os = "linux")]
+        match MountList::new() {
+            Ok(mount_list) => {
+                let mut m = HashMap::new();
+                mount_list.0.iter().for_each(|mount| {
+                    m.insert(mount.dest.clone(), MountedFs {
+                        dest: mount.dest.to_string_lossy().into_owned(),
+                        fstype: mount.fstype.clone(),
+                        source: mount.source.to_string_lossy().into(),
+                    });
+                });
+                m
+            }
+            Err(_) => HashMap::new()
+        }
+        #[cfg(not(target_os = "linux"))]
+        HashMap::new()
+    };
+}
 
 fn main() {
     use std::process::exit;

+ 3 - 2
src/options/flags.rs

@@ -54,7 +54,8 @@ pub static TIME:       Arg = Arg { short: Some(b't'), long: "time",       takes_
 pub static ACCESSED:   Arg = Arg { short: Some(b'u'), long: "accessed",   takes_value: TakesValue::Forbidden };
 pub static CREATED:    Arg = Arg { short: Some(b'U'), long: "created",    takes_value: TakesValue::Forbidden };
 pub static TIME_STYLE: Arg = Arg { short: None,       long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
-pub static HYPERLINK:  Arg = Arg { short: None,       long: "hyperlink",  takes_value: TakesValue::Forbidden};
+pub static HYPERLINK:  Arg = Arg { short: None,       long: "hyperlink",  takes_value: TakesValue::Forbidden };
+pub static MOUNTS:     Arg = Arg { short: Some(b'M'), long: "mounts",     takes_value: TakesValue::Forbidden };
 const TIMES: Values = &["modified", "changed", "accessed", "created"];
 const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso", "relative"];
 
@@ -85,7 +86,7 @@ pub static ALL_ARGS: Args = Args(&[
     &IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS,
 
     &BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED,
-    &BLOCKSIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK,
+    &BLOCKSIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK, &MOUNTS,
     &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS,
 
     &GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT,

+ 1 - 0
src/options/help.rs

@@ -53,6 +53,7 @@ LONG VIEW OPTIONS
   -H, --links              list each file's number of hard links
   -i, --inode              list each file's inode number
   -m, --modified           use the modified timestamp field
+  -M, --mounts             show mount details (Linux only)
   -n, --numeric            list numeric user and group IDs
   -S, --blocksize          show size of allocated file system blocks
   -t, --time FIELD         which timestamp field to list (modified, accessed, created)

+ 4 - 1
src/options/view.rs

@@ -82,7 +82,8 @@ impl Mode {
         // user about flags that won’t have any effect.
         if matches.is_strict() {
             for option in &[ &flags::BINARY, &flags::BYTES, &flags::INODE, &flags::LINKS,
-                             &flags::HEADER, &flags::BLOCKSIZE, &flags::TIME, &flags::GROUP, &flags::NUMERIC ] {
+                             &flags::HEADER, &flags::BLOCKSIZE, &flags::TIME, &flags::GROUP, &flags::NUMERIC,
+                             &flags::MOUNTS ] {
                 if matches.has(option)? {
                     return Err(OptionsError::Useless(option, false, &flags::LONG));
                 }
@@ -119,6 +120,7 @@ impl details::Options {
             header: false,
             xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?,
             secattr: xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?,
+            mounts: matches.has(&flags::MOUNTS)?,
         };
 
         Ok(details)
@@ -139,6 +141,7 @@ impl details::Options {
             header: matches.has(&flags::HEADER)?,
             xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?,
             secattr: xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?,
+            mounts: matches.has(&flags::MOUNTS)?,
         })
     }
 }

+ 5 - 0
src/output/details.rs

@@ -92,6 +92,7 @@ use crate::theme::Theme;
 ///
 /// Almost all the heavy lifting is done in a Table object, which handles the
 /// columns for each row.
+#[allow(clippy::struct_excessive_bools)] /// This clearly isn't a state machine
 #[derive(PartialEq, Eq, Debug)]
 pub struct Options {
 
@@ -109,6 +110,9 @@ pub struct Options {
 
     /// Whether to show each file's security attribute.
     pub secattr: bool,
+
+    /// Whether to show a directory's mounted filesystem details
+    pub mounts: bool,
 }
 
 
@@ -288,6 +292,7 @@ impl<'a> Render<'a> {
 
             let file_name = self.file_style.for_file(egg.file, self.theme)
                                 .with_link_paths()
+                                .with_mount_details(self.opts.mounts)
                                 .paint()
                                 .promote();
 

+ 17 - 11
src/output/escape.rs

@@ -2,25 +2,31 @@ use ansiterm::{ANSIString, Style};
 
 
 pub fn escape(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
-    if string.chars().all(|c| c >= 0x20 as char && c != 0x7f as char) {
+    // if the string has no control character
+    if string.chars().all(|c| !c.is_control()) {
         bits.push(good.paint(string));
         return;
     }
 
+    // the lengthier string of non control character can’t be bigger than the whole string
+    let mut regular_char_buff = String::with_capacity(string.len());
     for c in string.chars() {
         // The `escape_default` method on `char` is *almost* what we want here, but
         // it still escapes non-ASCII UTF-8 characters, which are still printable.
 
-        if c >= 0x20 as char && c != 0x7f as char {
-            // TODO: This allocates way too much,
-            // hence the `all` check above.
-            let mut s = String::new();
-            s.push(c);
-            bits.push(good.paint(s));
-        }
-        else {
-            let s = c.escape_default().collect::<String>();
-            bits.push(bad.paint(s));
+        if c.is_control() {
+            if !regular_char_buff.is_empty() {
+                bits.push(good.paint(std::mem::take(&mut regular_char_buff)));
+            }
+            regular_char_buff.extend(c.escape_default());
+            // biased towards regular characters, we push control characters immediately
+            bits.push(bad.paint(std::mem::take(&mut regular_char_buff)));
+        } else {
+            regular_char_buff.push(c);
         }
     }
+    // if last character was not a control character, the buffer is not empty!
+    if !regular_char_buff.is_empty() {
+        bits.push(good.paint(std::mem::take(&mut regular_char_buff)));
+    }
 }

+ 69 - 47
src/output/file_name.rs

@@ -3,12 +3,15 @@ use std::path::Path;
 
 use ansiterm::{ANSIString, Style};
 
+use crate::fs::mounts::MountedFs;
 use crate::fs::{File, FileTarget};
 use crate::output::cell::TextCellContents;
 use crate::output::escape;
 use crate::output::icons::{icon_for_file, iconify_style};
 use crate::output::render::FiletypeColours;
 
+const HYPERLINK_START: &str = "\x1B]8;;";
+const HYPERLINK_END: &str = "\x1B\x5C";
 
 /// Basically a file name factory.
 #[derive(Debug, Copy, Clone)]
@@ -35,7 +38,9 @@ impl Options {
             link_style: LinkStyle::JustFilenames,
             options:    self,
             target:     if file.is_link() { Some(file.link_target()) }
-                                     else { None }
+                                     else { None },
+            mount_style: MountStyle::JustDirectoryNames,
+            mounted_fs: file.mount_point_info(),
         }
     }
 }
@@ -74,6 +79,18 @@ impl Default for Classify {
     }
 }
 
+/// When displaying a directory name, there needs to be some way to handle
+/// mount details, depending on how long the resulting Cell can be.
+#[derive(PartialEq, Debug, Copy, Clone)]
+enum MountStyle {
+
+    /// Just display the directory names.
+    JustDirectoryNames,
+
+    /// Display mount points as directories and include information about
+    /// the filesystem that's mounted there.
+    MountInfo,
+}
 
 /// Whether and how to show icons.
 #[derive(PartialEq, Eq, Debug, Copy, Clone)]
@@ -112,6 +129,12 @@ pub struct FileName<'a, 'dir, C> {
     link_style: LinkStyle,
 
     pub options: Options,
+
+    /// The filesystem details for a mounted filesystem.
+    mounted_fs: Option<&'a MountedFs>,
+
+    /// How to handle displaying a mounted filesystem.
+    mount_style: MountStyle,
 }
 
 impl<'a, 'dir, C> FileName<'a, 'dir, C> {
@@ -122,6 +145,17 @@ impl<'a, 'dir, C> FileName<'a, 'dir, C> {
         self.link_style = LinkStyle::FullLinkPaths;
         self
     }
+
+    /// Sets the flag on this file name to display mounted filesystem
+    ///details.
+    pub fn with_mount_details(mut self, enable: bool) -> Self {
+        self.mount_style = if enable {
+            MountStyle::MountInfo
+        } else {
+            MountStyle::JustDirectoryNames
+        };
+        self
+    }
 }
 
 impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
@@ -190,6 +224,8 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
                             target: None,
                             link_style: LinkStyle::FullLinkPaths,
                             options: target_options,
+                            mounted_fs: None,
+                            mount_style: MountStyle::JustDirectoryNames,
                         };
 
                         for bit in target_name.escaped_file_name() {
@@ -228,6 +264,15 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
             }
         }
 
+        if let (MountStyle::MountInfo, Some(mount_details)) = (self.mount_style, self.mounted_fs.as_ref()) {
+            // This is a filesystem mounted on the directory, output its details
+            bits.push(Style::default().paint(" ["));
+            bits.push(Style::default().paint(mount_details.source.clone()));
+            bits.push(Style::default().paint(" ("));
+            bits.push(Style::default().paint(mount_details.fstype.clone()));
+            bits.push(Style::default().paint(")]"));
+        }
+
         bits.into()
     }
 
@@ -303,59 +348,32 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
         let file_style = self.style();
         let mut bits = Vec::new();
 
-        self.escape_color_and_hyperlinks(
+        let mut display_hyperlink = false;
+        if self.options.embed_hyperlinks == EmbedHyperlinks::On {
+            if let Some(abs_path) = self.file.path.canonicalize().unwrap().as_os_str().to_str() {
+                bits.insert(0, ANSIString::from(format!(
+                    "{}file://{}{}{}",
+                    HYPERLINK_START,
+                    gethostname::gethostname().to_str().unwrap_or(""),
+                    urlencoding::encode(abs_path).replace("%2F", "/"),
+                    HYPERLINK_END,
+                )));
+                display_hyperlink = true;
+            }
+        }
+
+        escape(
+            self.file.name.clone(),
             &mut bits,
             file_style,
             self.colours.control_char(),
         );
 
-        bits
-    }
-
-    // An adapted version of escape::escape.
-    // afaik of all the calls to escape::escape, only for escaped_file_name, the call to escape needs to be checked for hyper links
-    // and if that's the case then I think it's best to not try and generalize escape::escape to this case,
-    // as this adaptation would incur some unneeded operations there
-    pub fn escape_color_and_hyperlinks(&self, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
-        let string = self.file.name.clone();
-
-        if string.chars().all(|c| c >= 0x20 as char && c != 0x7f as char) {
-            let painted = good.paint(string);
-
-            let adjusted_filename = if let EmbedHyperlinks::On = self.options.embed_hyperlinks {
-                ANSIString::from(format!("\x1B]8;;{}\x1B\x5C{}\x1B]8;;\x1B\x5C", self.file.path.display(), painted))
-            } else {
-                painted
-            };
-            bits.push(adjusted_filename);
-            return;
+        if display_hyperlink {
+            bits.push(ANSIString::from(format!("{HYPERLINK_START}{HYPERLINK_END}")));
         }
 
-        // again adapted from escape::escape
-        // still a slow route, but slightly improved to at least not reallocate buff + have a predetermined buff size
-        //
-        // also note that buff would never need more than len,
-        // even tho 'in total' it will be lenghier than len (as we expand with escape_default),
-        // because we clear it after an irregularity
-        let mut buff = String::with_capacity(string.len());
-        for c in string.chars() {
-            // The `escape_default` method on `char` is *almost* what we want here, but
-            // it still escapes non-ASCII UTF-8 characters, which are still printable.
-
-            if c >= 0x20 as char && c != 0x7f as char {
-                buff.push(c);
-            }
-            else {
-                if ! buff.is_empty() {
-                    bits.push(good.paint(std::mem::take(&mut buff)));
-                }
-                // biased towards regular characters, so we still collect on first sight of bad char
-                for e in c.escape_default() {
-                    buff.push(e);
-                }
-                bits.push(bad.paint(std::mem::take(&mut buff)));
-            }
-        }
+        bits
     }
 
     /// Figures out which colour to paint the filename part of the output,
@@ -372,6 +390,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
         }
 
         match self.file {
+            f if f.is_mount_point()      => self.colours.mount_point(),
             f if f.is_directory()        => self.colours.directory(),
             #[cfg(unix)]
             f if f.is_executable_file()  => self.colours.executable_file(),
@@ -424,6 +443,9 @@ pub trait Colours: FiletypeColours {
     /// The style to paint a file that has its executable bit set.
     fn executable_file(&self) -> Style;
 
+    /// The style to paint a directory that has a filesystem mounted on it.
+    fn mount_point(&self) -> Style;
+
     fn colour_file(&self, file: &File<'_>) -> Style;
 }
 

+ 297 - 87
src/output/icons.rs

@@ -11,9 +11,9 @@ impl Icons {
     const BINARY: char          = '\u{eae8}';  // 
     const BOOK: char            = '\u{e28b}';  // 
     const CALENDAR: char        = '\u{eab0}';  // 
+    const CLOCK: char           = '\u{f43a}';  // 
     const COMPRESSED: char      = '\u{f410}';  // 
     const CONFIG: char          = '\u{e615}';  // 
-    const CONFIG_FOLDER: char   = '\u{e5fc}';  // 
     const CSS3: char            = '\u{e749}';  // 
     const DATABASE: char        = '\u{f1c0}';  // 
     const DIFF: char            = '\u{f440}';  // 
@@ -21,115 +21,275 @@ impl Icons {
     const DOCKER: char          = '\u{e650}';  // 
     const DOCUMENT: char        = '\u{f1c2}';  // 
     const EMACS: char           = '\u{e632}';  // 
+    const FILE: char            = '\u{f15b}';  // 
+    const FILE_OUTLINE: char    = '\u{f016}';  // 
+    const FOLDER: char          = '\u{e5ff}';  // 
+    const FOLDER_CONFIG: char   = '\u{e5fc}';  // 
+    const FOLDER_GIT: char      = '\u{e5fb}';  // 
+    const FOLDER_GITHUB: char   = '\u{e5fd}';  // 
+    const FOLDER_HIDDEN: char   = '\u{f179e}'; // 󱞞
+    const FOLDER_KEY: char      = '\u{f08ac}'; // 󰢬
+    const FOLDER_NPM: char      = '\u{e5fa}';  // 
+    const FOLDER_OPEN: char     = '\u{f115}';  // 
     const FONT: char            = '\u{f031}';  // 
     const GIST_SECRET: char     = '\u{eafa}';  // 
     const GIT: char             = '\u{f1d3}';  // 
     const GRADLE: char          = '\u{e660}';  // 
     const GRUNT: char           = '\u{e611}';  // 
     const GULP: char            = '\u{e610}';  // 
-    const HEADER: char          = '\u{f0fd}';  // 
     const HTML5: char           = '\u{f13b}';  // 
     const IMAGE: char           = '\u{f1c5}';  // 
     const INTELLIJ: char        = '\u{e7b5}';  // 
+    const JSON: char            = '\u{e60b}';  // 
     const KEY: char             = '\u{eb11}';  // 
     const KEYPASS: char         = '\u{f23e}';  // 
-    const JSON: char            = '\u{e60b}';  // 
     const LANG_ASSEMBLY: char   = '\u{e637}';  // 
     const LANG_C: char          = '\u{e61e}';  // 
     const LANG_CPP: char        = '\u{e61d}';  // 
     const LANG_CSHARP: char     = '\u{f031b}'; // 󰌛
     const LANG_ELIXIR: char     = '\u{e62d}';  // 
     const LANG_FSHARP: char     = '\u{e7a7}';  // 
-    const LANG_GO: char         = '\u{e724}';  // 
+    const LANG_GO: char         = '\u{e65e}';  // 
     const LANG_HASKELL: char    = '\u{e777}';  // 
     const LANG_JAVA: char       = '\u{e256}';  // 
     const LANG_JAVASCRIPT: char = '\u{e74e}';  // 
+    const LANG_KOTLIN: char     = '\u{e634}';  // 
     const LANG_OCAML: char      = '\u{e67a}';  // 
-    const LANG_PERL: char       = '\u{e769}';  // 
+    const LANG_PERL: char       = '\u{e67e}';  // 
     const LANG_PHP: char        = '\u{e73d}';  // 
     const LANG_PYTHON: char     = '\u{e606}';  // 
     const LANG_R: char          = '\u{f25d}';  // 
     const LANG_RUBY: char       = '\u{e21e}';  // 
     const LANG_RUBYRAILS: char  = '\u{e73b}';  // 
     const LANG_RUST: char       = '\u{e7a8}';  // 
+    const LANG_SASS: char       = '\u{e603}';  // 
     const LANG_STYLUS: char     = '\u{e600}';  // 
     const LANG_TEX: char        = '\u{e69b}';  // 
     const LANG_TYPESCRIPT: char = '\u{e628}';  // 
-    const LOCK: char            = '\u{f023}';  // 
     const LICENSE: char         = '\u{f02d}';  // 
+    const LOCK: char            = '\u{f023}';  // 
+    const MAKE: char            = '\u{e673}';  // 
     const MARKDOWN: char        = '\u{f48a}';  // 
     const MUSTACHE: char        = '\u{e60f}';  // 
     const NODEJS: char          = '\u{e718}';  // 
     const NPM: char             = '\u{e71e}';  // 
-    const OS_APPLE: char        = '\u{f179}';  // 
     const OS_ANDROID: char      = '\u{e70e}';  // 
+    const OS_APPLE: char        = '\u{f179}';  // 
     const OS_LINUX: char        = '\u{f17c}';  // 
     const OS_WINDOWS: char      = '\u{f17a}';  // 
     const OS_WINDOWS_CMD: char  = '\u{ebc4}';  // 
+    const PLAYLIST: char        = '\u{f0cb9}'; // 󰲹
     const POWERSHELL: char      = '\u{ebc7}';  // 
-    const REACT: char           = '\u{e7ba}';  // 
+    const PRIVATE_KEY: char     = '\u{f0306}'; // 󰌆
+    const PUBLIC_KEY: char      = '\u{f0dd6}'; // 󰷖
     const RAZOR: char           = '\u{f1fa}';  // 
+    const REACT: char           = '\u{e7ba}';  // 
     const SHEET: char           = '\u{f1c3}';  // 
-    const SHELL: char           = '\u{f489}';  // 
+    const SHELL_CMD: char       = '\u{f489}';  // 
+    const SHELL: char           = '\u{f1183}'; // 󱆃
+    const SHIELD_CHECK: char    = '\u{f0565}'; // 󰕥
+    const SHIELD_KEY: char      = '\u{f0bc4}'; // 󰯄
+    const SHIELD_LOCK: char     = '\u{f099d}'; // 󰦝
+    const SIGNED_FILE: char     = '\u{f19c3}'; // 󱧃
     const SLIDE: char           = '\u{f1c4}';  // 
+    const SUBLIME: char         = '\u{e7aa}';  // 
+    const SUBTITLE: char        = '\u{f0a16}'; // 󰨖
+    const TERRAFORM: char       = '\u{f1062}'; // 󱁢
     const TEXT: char            = '\u{f15c}';  // 
     const UNITY: char           = '\u{e721}';  // 
+    const VECTOR: char          = '\u{f0559}'; // 󰕙
     const VIDEO: char           = '\u{f03d}';  // 
     const VIM: char             = '\u{e7c5}';  // 
+    const WRENCH: char          = '\u{f0ad}';  // 
     const XML: char             = '\u{f05c0}'; // 󰗀
-    const YAML: char            = '\u{f481}';  //  - Find better icon
+    const YAML: char            = '\u{e6a8}';  // 
+    const YARN: char            = '\u{e6a7}';  // 
 }
 
-/// Mapping from full filenames to file type. This mapping should also contain all the "dot"
-/// files/directories that have a custom icon.
+/// Mapping from full filenames to directory icon. This mapping should contain
+/// all the directories that have a custom icon.
+const DIRECTORY_ICONS: Map<&'static str, char> = phf_map! {
+    ".config"             => Icons::FOLDER_CONFIG,  // 
+    ".git"                => Icons::FOLDER_GIT,     // 
+    ".github"             => Icons::FOLDER_GITHUB,  // 
+    ".npm"                => Icons::FOLDER_NPM,     // 
+    ".ssh"                => Icons::FOLDER_KEY,     // 󰢬
+    ".Trash"              => '\u{f1f8}',            // 
+    "cron.d"              => Icons::FOLDER_CONFIG,  // 
+    "cron.daily"          => Icons::FOLDER_CONFIG,  // 
+    "cron.hourly"         => Icons::FOLDER_CONFIG,  // 
+    "cron.monthly"        => Icons::FOLDER_CONFIG,  // 
+    "cron.weekly"         => Icons::FOLDER_CONFIG,  // 
+    "Desktop"             => '\u{f108}',            // 
+    "Downloads"           => '\u{f024d}',           // 󰉍
+    "config"              => Icons::FOLDER_CONFIG,  // 
+    "etc"                 => Icons::FOLDER_CONFIG,  // 
+    "hidden"              => Icons::FOLDER_HIDDEN,  // 󱞞
+    "include"             => Icons::FOLDER_CONFIG,  // 
+    "Mail"                => '\u{f01f0}',           // 󰇰
+    "Movies"              => '\u{f0fce}',           // 󰿎
+    "Music"               => '\u{f1359}',           // 󱍙
+    "node_modules"        => Icons::FOLDER_NPM,     // 
+    "npm_cache"           => Icons::FOLDER_NPM,     // 
+    "pam.d"               => Icons::FOLDER_KEY,     // 󰢬
+    "Pictures"            => '\u{f024f}',           // 󰉏
+    "ssh"                 => Icons::FOLDER_KEY,     // 󰢬
+    "sudoers.d"           => Icons::FOLDER_KEY,     // 󰢬
+    "Videos"              => '\u{f03d}',            // 
+    "xbps.d"              => Icons::FOLDER_CONFIG,  // 
+    "xorg.conf.d"         => Icons::FOLDER_CONFIG,  // 
+};
+
+/// Mapping from full filenames to file icon. This mapping should also contain
+/// all the "dot" files that have a custom icon.
 const FILENAME_ICONS: Map<&'static str, char> = phf_map! {
     ".atom"               => '\u{e764}',            // 
-    ".bashprofile"        => Icons::CONFIG,         // 
-    ".bashrc"             => Icons::SHELL,          // 
+    ".bashrc"             => Icons::SHELL,          // 󱆃
+    ".bash_history"       => Icons::SHELL,          // 󱆃
+    ".bash_logout"        => Icons::SHELL,          // 󱆃
+    ".bash_profile"       => Icons::SHELL,          // 󱆃
+    ".CFUserTextEncoding" => Icons::OS_APPLE,       // 
+    ".clang-format"       => Icons::CONFIG,         // 
+    ".cshrc"              => Icons::SHELL,          // 󱆃
+    ".DS_Store"           => Icons::OS_APPLE,       // 
     ".emacs"              => Icons::EMACS,          // 
-    ".git"                => Icons::GIT,            // 
     ".gitattributes"      => Icons::GIT,            // 
     ".gitconfig"          => Icons::GIT,            // 
-    ".github"             => '\u{f408}',            // 
     ".gitignore"          => Icons::GIT,            // 
     ".gitignore_global"   => Icons::GIT,            // 
+    ".gitlab-ci.yml"      => '\u{f296}',            // 
     ".gitmodules"         => Icons::GIT,            // 
+    ".htaccess"           => Icons::CONFIG,         // 
+    ".htpasswd"           => Icons::CONFIG,         // 
     ".idea"               => Icons::INTELLIJ,       // 
+    ".ideavimrc"          => Icons::VIM,            // 
+    ".inputrc"            => Icons::CONFIG,         // 
+    ".kshrc"              => Icons::SHELL,          // 󱆃
+    ".login"              => Icons::SHELL,          // 󱆃
+    ".logout"             => Icons::SHELL,          // 󱆃
+    ".node_repl_history"  => Icons::NODEJS,         // 
+    ".npmignore"          => Icons::NPM,            // 
+    ".npmrc"              => Icons::NPM,            // 
+    ".profile"            => Icons::SHELL,          // 󱆃
+    ".python_history"     => Icons::LANG_PYTHON,    // 
+    ".rustfmt.toml"       => Icons::LANG_RUST,      // 
     ".rvm"                => Icons::LANG_RUBY,      // 
-    ".Trash"              => '\u{f1f8}',            // 
+    ".rvmrc"              => Icons::LANG_RUBY,      // 
+    ".tcshrc"             => Icons::SHELL,          // 󱆃
+    ".viminfo"            => Icons::VIM,            // 
     ".vimrc"              => Icons::VIM,            // 
-    ".vscode"             => '\u{f0a1e}',           // 󰨞
-    ".zshrc"              => Icons::SHELL,          // 
-    "bin"                 => Icons::CONFIG_FOLDER,  // 
+    ".Xauthority"         => Icons::CONFIG,         // 
+    ".xinitrc"            => Icons::CONFIG,         // 
+    ".Xresources"         => Icons::CONFIG,         // 
+    ".yarnrc"             => Icons::YARN,           // 
+    ".zlogin"             => Icons::SHELL,          // 󱆃
+    ".zlogout"            => Icons::SHELL,          // 󱆃
+    ".zprofile"           => Icons::SHELL,          // 󱆃
+    ".zshenv"             => Icons::SHELL,          // 󱆃
+    ".zshrc"              => Icons::SHELL,          // 󱆃
+    ".zsh_history"        => Icons::SHELL,          // 󱆃
+    ".zsh_sessions"       => Icons::SHELL,          // 󱆃
+    "._DS_Store"          => Icons::OS_APPLE,       // 
+    "a.out"               => Icons::SHELL_CMD,      // 
+    "authorized_keys"     => '\u{f08c0}',           // 󰣀
+    "bashrc"              => Icons::SHELL,          // 󱆃
+    "bspwmrc"             => Icons::CONFIG,         // 
+    "build.gradle.kts"    => Icons::GRADLE,         // 
     "Cargo.lock"          => Icons::LANG_RUST,      // 
-    "config"              => Icons::CONFIG_FOLDER,  // 
+    "Cargo.toml"          => Icons::LANG_RUST,      // 
+    "CMakeLists.txt"      => Icons::MAKE,           // 
+    "composer.json"       => Icons::LANG_PHP,       // 
+    "composer.lock"       => Icons::LANG_PHP,       // 
+    "config"              => Icons::CONFIG,         // 
+    "config.status"       => Icons::CONFIG,         // 
+    "configure"           => Icons::WRENCH,         // 
+    "configure.ac"        => Icons::CONFIG,         // 
+    "configure.in"        => Icons::CONFIG,         // 
+    "constraints.txt"     => Icons::LANG_PYTHON,    // 
+    "crontab"             => Icons::CONFIG,         // 
+    "crypttab"            => Icons::CONFIG,         // 
+    "csh.cshrc"           => Icons::SHELL,          // 󱆃
+    "csh.login"           => Icons::SHELL,          // 󱆃
+    "csh.logout"          => Icons::SHELL,          // 󱆃
     "docker-compose.yml"  => Icons::DOCKER,         // 
     "Dockerfile"          => Icons::DOCKER,         // 
-    "ds_store"            => Icons::OS_APPLE,       // 
     "Earthfile"           => '\u{f0ac}',            // 
-    "gitignore_global"    => Icons::GIT,            // 
-    "gitlab-ci.yml"       => '\u{f296}',            // 
-    "go.mod"              => Icons::LANG_GO,        // 
-    "go.sum"              => Icons::LANG_GO,        // 
+    "environment"         => Icons::CONFIG,         // 
+    "GNUmakefile"         => Icons::MAKE,           // 
+    "go.mod"              => Icons::LANG_GO,        // 
+    "go.sum"              => Icons::LANG_GO,        // 
+    "go.work"             => Icons::LANG_GO,        // 
     "gradle"              => Icons::GRADLE,         // 
+    "gradle.properties"   => Icons::GRADLE,         // 
+    "gradlew"             => Icons::GRADLE,         // 
+    "gradlew.bat"         => Icons::GRADLE,         // 
+    "group"               => Icons::LOCK,           // 
     "gruntfile.coffee"    => Icons::GRUNT,          // 
     "gruntfile.js"        => Icons::GRUNT,          // 
     "gruntfile.ls"        => Icons::GRUNT,          // 
+    "gshadow"             => Icons::LOCK,           // 
     "gulpfile.coffee"     => Icons::GULP,           // 
     "gulpfile.js"         => Icons::GULP,           // 
     "gulpfile.ls"         => Icons::GULP,           // 
-    "hidden"              => Icons::LOCK,           // 
-    "include"             => Icons::CONFIG_FOLDER,  // 
-    "lib"                 => '\u{f121}',            // 
+    "heroku.yml"          => '\u{e77b}',            // 
+    "hostname"            => Icons::CONFIG,         // 
+    "id_dsa"              => Icons::PRIVATE_KEY,    // 󰌆
+    "id_ecdsa"            => Icons::PRIVATE_KEY,    // 󰌆
+    "id_ecdsa_sk"         => Icons::PRIVATE_KEY,    // 󰌆
+    "id_ed25519"          => Icons::PRIVATE_KEY,    // 󰌆
+    "id_ed25519_sk"       => Icons::PRIVATE_KEY,    // 󰌆
+    "id_rsa"              => Icons::PRIVATE_KEY,    // 󰌆
+    "inputrc"             => Icons::CONFIG,         // 
+    "Jenkinsfile"         => '\u{e66e}',            // 
+    "jsconfig.json"       => Icons::LANG_JAVASCRIPT,// 
+    "Justfile"            => Icons::WRENCH,         // 
+    "known_hosts"         => '\u{f08c0}',           // 󰣀
+    "LICENCE"             => Icons::LICENSE,        // 
+    "LICENCE.md"          => Icons::LICENSE,        // 
+    "LICENCE.txt"         => Icons::LICENSE,        // 
     "LICENSE"             => Icons::LICENSE,        // 
+    "LICENSE.md"          => Icons::LICENSE,        // 
+    "LICENSE.txt"         => Icons::LICENSE,        // 
     "localized"           => Icons::OS_APPLE,       // 
-    "Makefile"            => Icons::SHELL,          // 
-    "node_modules"        => Icons::NODEJS,         // 
-    "npmignore"           => Icons::NPM,            // 
+    "localtime"           => Icons::CLOCK,          // 
+    "Makefile"            => Icons::MAKE,           // 
+    "makefile"            => Icons::MAKE,           // 
+    "Makefile.ac"         => Icons::MAKE,           // 
+    "Makefile.am"         => Icons::MAKE,           // 
+    "Makefile.in"         => Icons::MAKE,           // 
+    "MANIFEST"            => Icons::LANG_PYTHON,    // 
+    "MANIFEST.in"         => Icons::LANG_PYTHON,    // 
+    "npm-shrinkwrap.json" => Icons::NPM,            // 
+    "npmrc"               => Icons::NPM,            // 
+    "package-lock.json"   => Icons::NPM,            // 
+    "package.json"        => Icons::NPM,            // 
+    "passwd"              => Icons::LOCK,           // 
     "PKGBUILD"            => '\u{f303}',            // 
+    "php.ini"             => Icons::LANG_PHP,       // 
+    "pom.xml"             => '\u{e674}',            // 
+    "Procfile"            => '\u{e77b}',            // 
+    "profile"             => Icons::SHELL,          // 󱆃
+    "pyproject.toml"      => Icons::LANG_PYTHON,    // 
+    "Rakefile"            => Icons::LANG_RUBY,      // 
+    "release.toml"        => Icons::LANG_RUST,      // 
+    "requirements.txt"    => Icons::LANG_PYTHON,    // 
+    "robots.txt"          => '\u{f06a9}',           // 󰚩
     "rubydoc"             => Icons::LANG_RUBYRAILS, // 
+    "rvmrc"               => Icons::LANG_RUBY,      // 
+    "settings.gradle.kts" => Icons::GRADLE,         // 
+    "shadow"              => Icons::LOCK,           // 
+    "shells"              => Icons::CONFIG,         // 
+    "sudoers"             => Icons::LOCK,           // 
+    "timezone"            => Icons::CLOCK,          // 
+    "tsconfig.json"       => Icons::LANG_TYPESCRIPT,// 
     "Vagrantfile"         => '\u{2371}',            // ⍱
-    "yarn.lock"           => '\u{e6a7}',            // 
+    "webpack.config.js"   => '\u{f072b}',           // 󰜫
+    "yarn.lock"           => Icons::YARN,           // 
+    "zlogin"              => Icons::SHELL,          // 󱆃
+    "zlogout"             => Icons::SHELL,          // 󱆃
+    "zprofile"            => Icons::SHELL,          // 󱆃
+    "zshenv"              => Icons::SHELL,          // 󱆃
+    "zshrc"               => Icons::SHELL,          // 󱆃
 };
 
 /// Mapping from lowercase file extension to icons.  If an image, video, or audio extension is add
@@ -147,23 +307,25 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "apple"          => Icons::OS_APPLE,         // 
     "ar"             => Icons::COMPRESSED,       // 
     "arw"            => Icons::IMAGE,            // 
+    "asc"            => Icons::SHIELD_LOCK,      // 󰦝
     "asm"            => Icons::LANG_ASSEMBLY,    // 
+    "asp"            => '\u{f121}',              // 
     "avi"            => Icons::VIDEO,            // 
     "avif"           => Icons::IMAGE,            // 
     "avro"           => Icons::JSON,             // 
-    "awk"            => Icons::SHELL,            // 
-    "bash"           => Icons::SHELL,            // 
-    "bashrc"         => Icons::SHELL,            // 
-    "bash_history"   => Icons::SHELL,            // 
-    "bash_profile"   => Icons::SHELL,            // 
+    "awk"            => Icons::SHELL_CMD,        // 
+    "bash"           => Icons::SHELL_CMD,        // 
     "bat"            => Icons::OS_WINDOWS_CMD,   // 
-    "bats"           => Icons::SHELL,            // 
+    "bats"           => Icons::SHELL_CMD,        // 
     "bib"            => Icons::LANG_TEX,         // 
     "bin"            => Icons::BINARY,           // 
     "bmp"            => Icons::IMAGE,            // 
+    "br"             => Icons::COMPRESSED,       // 
     "bst"            => Icons::LANG_TEX,         // 
+    "bundle"         => Icons::OS_APPLE,         // 
     "bz"             => Icons::COMPRESSED,       // 
     "bz2"            => Icons::COMPRESSED,       // 
+    "bz3"            => Icons::COMPRESSED,       // 
     "c"              => Icons::LANG_C,           // 
     "c++"            => Icons::LANG_CPP,         // 
     "cab"            => Icons::OS_WINDOWS,       // 
@@ -177,8 +339,10 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "clj"            => '\u{e768}',              // 
     "cljs"           => '\u{e76a}',              // 
     "cls"            => Icons::LANG_TEX,         // 
+    "cmake"          => Icons::MAKE,             // 
     "cmd"            => Icons::OS_WINDOWS,       // 
     "coffee"         => '\u{f0f4}',              // 
+    "com"            => Icons::OS_WINDOWS_CMD,   // 
     "conf"           => Icons::CONFIG,           // 
     "config"         => Icons::CONFIG,           // 
     "cp"             => Icons::LANG_CPP,         // 
@@ -187,7 +351,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "cr2"            => Icons::IMAGE,            // 
     "crt"            => Icons::GIST_SECRET,      // 
     "cs"             => Icons::LANG_CSHARP,      // 󰌛
-    "csh"            => Icons::SHELL,            // 
+    "csh"            => Icons::SHELL_CMD,        // 
     "cshtml"         => Icons::RAZOR,            // 
     "csproj"         => Icons::LANG_CSHARP,      // 󰌛
     "css"            => Icons::CSS3,             // 
@@ -195,6 +359,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "csx"            => Icons::LANG_CSHARP,      // 󰌛
     "cts"            => Icons::LANG_TYPESCRIPT,  // 
     "cu"             => '\u{e64b}',              // 
+    "cue"            => Icons::PLAYLIST,         // 󰲹
     "cxx"            => Icons::LANG_CPP,         // 
     "d"              => '\u{e7af}',              // 
     "dart"           => '\u{e798}',              // 
@@ -208,27 +373,30 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "dmg"            => Icons::DISK_IMAGE,       // 
     "doc"            => Icons::DOCUMENT,         // 
     "docx"           => Icons::DOCUMENT,         // 
+    "dot"            => '\u{f1049}',             // 󱁉
+    "download"       => '\u{f01da}',             // 󰇚
     "drawio"         => '\u{ebba}',              // 
-    "ds_store"       => Icons::OS_APPLE,         // 
     "dump"           => Icons::DATABASE,         // 
     "dvi"            => Icons::IMAGE,            // 
+    "dylib"          => Icons::OS_APPLE,         // 
     "ebook"          => Icons::BOOK,             // 
     "ebuild"         => '\u{f30d}',              // 
     "editorconfig"   => Icons::CONFIG,           // 
     "ejs"            => '\u{e618}',              // 
     "el"             => Icons::EMACS,            // 
+    "elc"            => Icons::EMACS,            // 
     "elm"            => '\u{e62c}',              // 
     "eml"            => '\u{f003}',              // 
     "env"            => '\u{f462}',              // 
     "eot"            => Icons::FONT,             // 
-    "eps"            => Icons::IMAGE,            // 
+    "eps"            => Icons::VECTOR,           // 󰕙
     "epub"           => Icons::BOOK,             // 
     "erb"            => Icons::LANG_RUBYRAILS,   // 
     "erl"            => '\u{e7b1}',              // 
     "ex"             => Icons::LANG_ELIXIR,      // 
-    "exe"            => Icons::OS_WINDOWS,       // 
+    "exe"            => Icons::OS_WINDOWS_CMD,   // 
     "exs"            => Icons::LANG_ELIXIR,      // 
-    "fish"           => Icons::SHELL,            // 
+    "fish"           => Icons::SHELL_CMD,        // 
     "flac"           => Icons::AUDIO,            // 
     "flv"            => Icons::VIDEO,            // 
     "font"           => Icons::FONT,             // 
@@ -242,35 +410,34 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "gform"          => '\u{f298}',              // 
     "gif"            => Icons::IMAGE,            // 
     "git"            => Icons::GIT,              // 
-    "gitattributes"  => Icons::GIT,              // 
-    "gitignore"      => Icons::GIT,              // 
-    "gitmodules"     => Icons::GIT,              // 
-    "go"             => Icons::LANG_GO,          // 
-    "gpg"            => '\u{e60a}',              // 
+    "go"             => Icons::LANG_GO,          // 
+    "gpg"            => Icons::SHIELD_LOCK,      // 󰦝
     "gradle"         => Icons::GRADLE,           // 
     "groovy"         => '\u{e775}',              // 
     "gsheet"         => Icons::SHEET,            // 
     "gslides"        => Icons::SLIDE,            // 
     "guardfile"      => Icons::LANG_RUBY,        // 
+    "gv"             => '\u{f1049}',             // 󱁉
     "gz"             => Icons::COMPRESSED,       // 
-    "h"              => Icons::HEADER,           // 
+    "h"              => Icons::LANG_C,           // 
     "hbs"            => Icons::MUSTACHE,         // 
-    "heic"           => Icons::VIDEO,            // 
+    "heic"           => Icons::IMAGE,            // 
+    "heics"          => Icons::VIDEO,            // 
     "heif"           => Icons::IMAGE,            // 
-    "hpp"            => Icons::HEADER,           // 
+    "hpp"            => Icons::LANG_CPP,         // 
     "hs"             => Icons::LANG_HASKELL,     // 
     "htm"            => Icons::HTML5,            // 
     "html"           => Icons::HTML5,            // 
-    "hxx"            => Icons::HEADER,           // 
+    "hxx"            => Icons::LANG_CPP,         // 
     "ical"           => Icons::CALENDAR,         // 
     "icalendar"      => Icons::CALENDAR,         // 
     "ico"            => Icons::IMAGE,            // 
     "ics"            => Icons::CALENDAR,         // 
     "ifb"            => Icons::CALENDAR,         // 
-    "image"          => Icons::IMAGE,            // 
+    "image"          => Icons::DISK_IMAGE,       // 
     "img"            => Icons::DISK_IMAGE,       // 
     "iml"            => Icons::INTELLIJ,         // 
-    "ini"            => Icons::OS_WINDOWS,       // 
+    "ini"            => Icons::CONFIG,           // 
     "ipynb"          => '\u{e678}',              // 
     "iso"            => Icons::DISK_IMAGE,       // 
     "j2c"            => Icons::IMAGE,            // 
@@ -293,15 +460,20 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "json"           => Icons::JSON,             // 
     "jsx"            => Icons::REACT,            // 
     "jxl"            => Icons::IMAGE,            // 
+    "kbx"            => Icons::SHIELD_KEY,       // 󰯄
     "kdb"            => Icons::KEYPASS,          // 
     "kdbx"           => Icons::KEYPASS,          // 
     "key"            => Icons::KEY,              // 
     "ko"             => Icons::OS_LINUX,         // 
-    "ksh"            => Icons::SHELL,            // 
+    "ksh"            => Icons::SHELL_CMD,        // 
+    "kt"             => Icons::LANG_KOTLIN,      // 
+    "kts"            => Icons::LANG_KOTLIN,      // 
     "latex"          => Icons::LANG_TEX,         // 
+    "ldb"            => Icons::DATABASE,         // 
     "less"           => '\u{e758}',              // 
     "lhs"            => Icons::LANG_HASKELL,     // 
     "license"        => Icons::LICENSE,          // 
+    "lisp"           => '\u{f0172}',             // 󰅲
     "localized"      => Icons::OS_APPLE,         // 
     "lock"           => Icons::LOCK,             // 
     "log"            => '\u{f18d}',              // 
@@ -314,13 +486,17 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "m"              => Icons::LANG_C,           // 
     "m2ts"           => Icons::VIDEO,            // 
     "m2v"            => Icons::VIDEO,            // 
+    "m3u"            => Icons::PLAYLIST,         // 󰲹
+    "m3u8"           => Icons::PLAYLIST,         // 󰲹
     "m4a"            => Icons::AUDIO,            // 
     "m4v"            => Icons::VIDEO,            // 
     "magnet"         => '\u{f076}',              // 
     "markdown"       => Icons::MARKDOWN,         // 
     "md"             => Icons::MARKDOWN,         // 
+    "md5"            => Icons::SHIELD_CHECK,     // 󰕥
+    "mdb"            => Icons::DATABASE,         // 
     "mjs"            => Icons::LANG_JAVASCRIPT,  // 
-    "mk"             => Icons::SHELL,            // 
+    "mk"             => Icons::MAKE,             // 
     "mka"            => Icons::AUDIO,            // 
     "mkd"            => Icons::MARKDOWN,         // 
     "mkv"            => Icons::VIDEO,            // 
@@ -342,8 +518,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "nef"            => Icons::IMAGE,            // 
     "ninja"          => '\u{f0774}',             // 󰝴
     "nix"            => '\u{f313}',              // 
-    "node"           => '\u{f0399}',             // 󰎙
-    "npmignore"      => Icons::NPM,              // 
+    "node"           => Icons::NODEJS,           // 
     "o"              => Icons::BINARY,           // 
     "odp"            => Icons::SLIDE,            // 
     "ods"            => Icons::SHEET,            // 
@@ -356,36 +531,45 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "org"            => '\u{e633}',              // 
     "otf"            => Icons::FONT,             // 
     "out"            => '\u{eb2c}',              // 
+    "p12"            => Icons::KEY,              // 
     "par"            => Icons::COMPRESSED,       // 
     "part"           => '\u{f43a}',              // 
     "patch"          => Icons::DIFF,             // 
     "pbm"            => Icons::IMAGE,            // 
     "pdf"            => '\u{f1c1}',              // 
     "pem"            => Icons::KEY,              // 
+    "pfx"            => Icons::KEY,              // 
     "pgm"            => Icons::IMAGE,            // 
+    "phar"           => Icons::LANG_PHP,         // 
     "php"            => Icons::LANG_PHP,         // 
-    "pl"             => Icons::LANG_PERL,        // 
-    "plx"            => Icons::LANG_PERL,        // 
-    "pm"             => Icons::LANG_PERL,        // 
+    "pkg"            => '\u{eb29}',              // 
+    "pl"             => Icons::LANG_PERL,        // 
+    "plist"          => Icons::OS_APPLE,         // 
+    "plx"            => Icons::LANG_PERL,        // 
+    "pm"             => Icons::LANG_PERL,        // 
     "png"            => Icons::IMAGE,            // 
     "pnm"            => Icons::IMAGE,            // 
-    "pod"            => Icons::LANG_PERL,        // 
+    "pod"            => Icons::LANG_PERL,        // 
+    "pp"             => '\u{e631}',              // 
     "ppm"            => Icons::IMAGE,            // 
     "ppt"            => Icons::SLIDE,            // 
     "pptx"           => Icons::SLIDE,            // 
-    "procfile"       => Icons::LANG_RUBY,        //   - Can not find a reference to procfile being Ruby
     "properties"     => Icons::JSON,             // 
-    "ps"             => Icons::IMAGE,            // 
+    "ps"             => Icons::VECTOR,           // 󰕙
     "ps1"            => Icons::POWERSHELL,       // 
     "psd"            => '\u{e7b8}',              // 
     "psd1"           => Icons::POWERSHELL,       // 
     "psm1"           => Icons::POWERSHELL,       // 
+    "pub"            => Icons::PUBLIC_KEY,       // 󰷖
     "pxm"            => Icons::IMAGE,            // 
     "py"             => Icons::LANG_PYTHON,      // 
     "pyc"            => Icons::LANG_PYTHON,      // 
+    "pyd"            => Icons::LANG_PYTHON,      // 
+    "pyi"            => Icons::LANG_PYTHON,      // 
+    "pyo"            => Icons::LANG_PYTHON,      // 
+    "qcow"           => Icons::DISK_IMAGE,       // 
     "qcow2"          => Icons::DISK_IMAGE,       // 
     "r"              => Icons::LANG_R,           // 
-    "rakefile"       => Icons::LANG_RUBY,        // 
     "rar"            => Icons::COMPRESSED,       // 
     "raw"            => Icons::IMAGE,            // 
     "razor"          => Icons::RAZOR,            // 
@@ -409,31 +593,53 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "ru"             => Icons::LANG_RUBY,        // 
     "rubydoc"        => Icons::LANG_RUBYRAILS,   // 
     "s"              => Icons::LANG_ASSEMBLY,    // 
-    "sass"           => '\u{e603}',              // 
+    "sass"           => Icons::LANG_SASS,        // 
     "scala"          => '\u{e737}',              // 
-    "scss"           => Icons::CSS3,             // 
+    "scss"           => Icons::LANG_SASS,        // 
     "service"        => '\u{eba2}',              // 
-    "sh"             => Icons::SHELL,            // 
-    "shell"          => Icons::SHELL,            // 
+    "sh"             => Icons::SHELL_CMD,        // 
+    "sha1"           => Icons::SHIELD_CHECK,     // 󰕥
+    "sha224"         => Icons::SHIELD_CHECK,     // 󰕥
+    "sha256"         => Icons::SHIELD_CHECK,     // 󰕥
+    "sha384"         => Icons::SHIELD_CHECK,     // 󰕥
+    "sha512"         => Icons::SHIELD_CHECK,     // 󰕥
+    "shell"          => Icons::SHELL_CMD,        // 
+    "sig"            => Icons::SIGNED_FILE,      // 󱧃
+    "signature"      => Icons::SIGNED_FILE,      // 󱧃
     "slim"           => Icons::LANG_RUBYRAILS,   // 
     "sln"            => '\u{e70c}',              // 
     "so"             => Icons::OS_LINUX,         // 
     "sql"            => Icons::DATABASE,         // 
     "sqlite3"        => '\u{e7c4}',              // 
+    "srt"            => Icons::SUBTITLE,         // 󰨖
     "stl"            => Icons::IMAGE,            // 
     "sty"            => Icons::LANG_TEX,         // 
     "styl"           => Icons::LANG_STYLUS,      // 
     "stylus"         => Icons::LANG_STYLUS,      // 
+    "sub"            => Icons::SUBTITLE,         // 󰨖
+    "sublime-build"  => Icons::SUBLIME,          // 
+    "sublime-keymap" => Icons::SUBLIME,          // 
+    "sublime-menu"   => Icons::SUBLIME,          // 
+    "sublime-options"=> Icons::SUBLIME,          // 
+    "sublime-package"=> Icons::SUBLIME,          // 
+    "sublime-project"=> Icons::SUBLIME,          // 
+    "sublime-session"=> Icons::SUBLIME,          // 
+    "sublime-settings"=>Icons::SUBLIME,          // 
+    "sublime-snippet"=> Icons::SUBLIME,          // 
+    "sublime-theme"  => Icons::SUBLIME,          // 
     "svelte"         => '\u{e697}',              // 
-    "svg"            => Icons::IMAGE,            // 
+    "svg"            => Icons::VECTOR,           // 󰕙
     "swift"          => '\u{e755}',              // 
-    "t"              => Icons::LANG_PERL,        // 
+    "t"              => Icons::LANG_PERL,        // 
     "tar"            => Icons::COMPRESSED,       // 
     "taz"            => Icons::COMPRESSED,       // 
     "tbz"            => Icons::COMPRESSED,       // 
     "tbz2"           => Icons::COMPRESSED,       // 
-    "tc"             => Icons::COMPRESSED,       // 
+    "tc"             => Icons::DISK_IMAGE,       // 
     "tex"            => Icons::LANG_TEX,         // 
+    "tf"             => Icons::TERRAFORM,        // 󱁢
+    "tfstate"        => Icons::TERRAFORM,        // 󱁢
+    "tfvars"         => Icons::TERRAFORM,        // 󱁢
     "tgz"            => Icons::COMPRESSED,       // 
     "tif"            => Icons::IMAGE,            // 
     "tiff"           => Icons::IMAGE,            // 
@@ -443,6 +649,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "ts"             => Icons::LANG_TYPESCRIPT,  // 
     "tsv"            => Icons::SHEET,            // 
     "tsx"            => Icons::REACT,            // 
+    "ttc"            => Icons::FONT,             // 
     "ttf"            => Icons::FONT,             // 
     "twig"           => '\u{e61c}',              // 
     "txt"            => Icons::TEXT,             // 
@@ -462,11 +669,13 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "wav"            => Icons::AUDIO,            // 
     "webm"           => Icons::VIDEO,            // 
     "webp"           => Icons::IMAGE,            // 
+    "whl"            => Icons::LANG_PYTHON,      // 
     "windows"        => Icons::OS_WINDOWS,       // 
     "wma"            => Icons::AUDIO,            // 
     "wmv"            => Icons::VIDEO,            // 
     "woff"           => Icons::FONT,             // 
     "woff2"          => Icons::FONT,             // 
+    "xcf"            => Icons::IMAGE,            // 
     "xhtml"          => Icons::HTML5,            // 
     "xls"            => Icons::SHEET,            // 
     "xlsm"           => Icons::SHEET,            // 
@@ -475,14 +684,13 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "xpm"            => Icons::IMAGE,            // 
     "xul"            => Icons::XML,              // 󰗀
     "xz"             => Icons::COMPRESSED,       // 
-    "yaml"           => Icons::YAML,             // 
-    "yml"            => Icons::YAML,             // 
+    "yaml"           => Icons::YAML,             // 
+    "yml"            => Icons::YAML,             // 
     "z"              => Icons::COMPRESSED,       // 
     "zig"            => '\u{21af}',              // ↯
     "zip"            => Icons::COMPRESSED,       // 
-    "zsh"            => Icons::SHELL,            // 
-    "zsh-theme"      => Icons::SHELL,            // 
-    "zshrc"          => Icons::SHELL,            // 
+    "zsh"            => Icons::SHELL_CMD,        // 
+    "zsh-theme"      => Icons::SHELL,            // 󱆃
     "zst"            => Icons::COMPRESSED,       // 
 };
 
@@ -503,17 +711,19 @@ pub fn iconify_style(style: Style) -> Style {
 /// Lookup the icon for a file based on the file's name, if the entry is a
 /// directory, or by the lowercase file extension.
 pub fn icon_for_file(file: &File<'_>) -> char {
-    if let Some(icon) = FILENAME_ICONS.get(file.name.as_str()) {
+    if file.points_to_directory() {
+        *DIRECTORY_ICONS.get(file.name.as_str()).unwrap_or_else(|| {
+            if file.is_empty_dir() {
+                &Icons::FOLDER_OPEN // 
+            } else {
+                &Icons::FOLDER // 
+            }
+        })
+    } else if let Some(icon) = FILENAME_ICONS.get(file.name.as_str()) {
         *icon
-    } else if file.points_to_directory() {
-        if file.is_empty_dir() {
-            '\u{f115}' // 
-        } else {
-            '\u{f07b}' // 
-        }
     } else if let Some(ext) = file.ext.as_ref() {
-        *EXTENSION_ICONS.get(ext.as_str()).unwrap_or(&'\u{f15b}') // 
+        *EXTENSION_ICONS.get(ext.as_str()).unwrap_or(&Icons::FILE) // 
     } else {
-        '\u{f016}' // 
+        Icons::FILE_OUTLINE // 
     }
 }

+ 2 - 1
src/output/lines.rs

@@ -5,7 +5,7 @@ use ansiterm::ANSIStrings;
 use crate::fs::File;
 use crate::fs::filter::FileFilter;
 use crate::output::cell::TextCellContents;
-use crate::output::file_name::{Options as FileStyle};
+use crate::output::file_name::Options as FileStyle;
 use crate::theme::Theme;
 
 
@@ -32,6 +32,7 @@ impl<'a> Render<'a> {
         self.file_style
             .for_file(file, self.theme)
             .with_link_paths()
+            .with_mount_details(false)
             .paint()
     }
 }

+ 1 - 0
src/theme/default_theme.rs

@@ -20,6 +20,7 @@ impl UiStyles {
                 socket:       Red.bold(),
                 special:      Yellow.normal(),
                 executable:   Green.bold(),
+                mount_point:  Blue.bold().underline(),
             },
 
             perms: Permissions {

+ 3 - 0
src/theme/mod.rs

@@ -327,6 +327,7 @@ impl FileNameColours for Theme {
     fn control_char(&self)        -> Style { self.ui.control_char }
     fn symlink_path(&self)        -> Style { self.ui.symlink_path }
     fn executable_file(&self)     -> Style { self.ui.filekinds.executable }
+    fn mount_point(&self)         -> Style { self.ui.filekinds.mount_point }
 
     fn colour_file(&self, file: &File<'_>) -> Style {
         self.exts.colour_file(file).unwrap_or(self.ui.filekinds.normal)
@@ -534,6 +535,8 @@ mod customs_test {
     test!(exa_cc:  ls "", exa "cc=38;5;134"  =>  colours c -> { c.control_char              = Fixed(134).normal(); });
     test!(exa_bo:  ls "", exa "bO=4"         =>  colours c -> { c.broken_path_overlay       = Style::default().underline(); });
 
+    test!(exa_mp:  ls "", exa "mp=1;34;4"    =>  colours c -> { c.filekinds.mount_point     = Blue.bold().underline(); });
+
     // All the while, LS_COLORS treats them as filenames:
     test!(ls_uu:   ls "uu=38;5;117", exa ""  =>  exts [ ("uu", Fixed(117).normal()) ]);
     test!(ls_un:   ls "un=38;5;118", exa ""  =>  exts [ ("un", Fixed(118).normal()) ]);

+ 3 - 0
src/theme/ui_styles.rs

@@ -39,6 +39,7 @@ pub struct FileKinds {
     pub socket: Style,
     pub special: Style,
     pub executable: Style,
+    pub mount_point: Style,
 }
 
 #[derive(Clone, Copy, Debug, Default, PartialEq)]
@@ -210,6 +211,8 @@ impl UiStyles {
             "cc" => self.control_char             = pair.to_style(),
             "bO" => self.broken_path_overlay      = pair.to_style(),
 
+            "mp" => self.filekinds.mount_point    = pair.to_style(),
+
              _   => return false,
         }