Преглед на файлове

Merge branch 'main' of https://github.com/eza-community/eza into feature/add-recursive-dir-size

xempt преди 2 години
родител
ревизия
7b1e8979ec

+ 1 - 1
.github/workflows/flake.yml

@@ -16,6 +16,6 @@ jobs:
       - name: Check Nix flake inputs
         uses: DeterminateSystems/flake-checker-action@v5 # This action
       - name: Install Nix
-        uses: DeterminateSystems/nix-installer-action@v5
+        uses: DeterminateSystems/nix-installer-action@v6
       - name: Nix Flake Check
         run: nix flake check --all-systems

+ 37 - 0
CHANGELOG.md

@@ -1,5 +1,38 @@
 # Changelog
 
+## [0.15.0] - 2023-10-19
+
+### Bug Fixes
+
+- Reenable debug symbols in debug builds
+- Clippy lint
+- Merge conflict with main
+
+### Documentation
+
+- Correct color option spellings
+
+### Features
+
+- Add option --smart-group
+- Add completions, man for --smart-group
+- Add icons=always,auto,never. dont display icons in a tty|piped
+- Fix auto value for colors and icons + documentation
+- [**breaking**] Remove --no-icons in favor of --icons=always,auto,never. default is auto
+
+### Miscellaneous Tasks
+
+- Upgrade to uutils_term_grid from unmaintained term_grid
+
+### Build
+
+- Bump DeterminateSystems/nix-installer-action from 5 to 6
+
+### Ci
+
+- Remove stalebot, is super annoying
+- Adjust test case to icons=auto (no icons should show due to tty)
+
 ## [0.14.2] - 2023-10-12
 
 ### Bug Fixes
@@ -18,6 +51,10 @@
 - Add missing nu shell completions
 - Adding the EZA_OVERRIDE_GIT env var
 
+### Miscellaneous Tasks
+
+- Release eza v0.14.2
+
 ### Refactor
 
 - Use musl target for amd64 deb package

+ 11 - 11
Cargo.lock

@@ -356,7 +356,7 @@ dependencies = [
 
 [[package]]
 name = "eza"
-version = "0.14.2"
+version = "0.15.0"
 dependencies = [
  "ansiterm",
  "chrono",
@@ -374,11 +374,11 @@ dependencies = [
  "phf",
  "proc-mounts",
  "scoped_threadpool",
- "term_grid",
  "terminal_size",
  "timeago",
  "trycmd",
  "unicode-width",
+ "uutils_term_grid",
  "uzers",
  "windows-sys",
  "zoneinfo_compiled",
@@ -1067,15 +1067,6 @@ dependencies = [
  "windows-sys",
 ]
 
-[[package]]
-name = "term_grid"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf"
-dependencies = [
- "unicode-width",
-]
-
 [[package]]
 name = "terminal_size"
 version = "0.3.0"
@@ -1223,6 +1214,15 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
+[[package]]
+name = "uutils_term_grid"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b389452a568698688dda38802068378a16c15c4af9b153cdd99b65391292bbc7"
+dependencies = [
+ "unicode-width",
+]
+
 [[package]]
 name = "uzers"
 version = "0.11.3"

+ 2 - 2
Cargo.toml

@@ -16,7 +16,7 @@ readme = "README.md"
 homepage = "https://github.com/eza-community/eza"
 license = "MIT"
 repository = "https://github.com/eza-community/eza"
-version = "0.14.2"
+version = "0.15.0"
 
 
 [package.metadata.deb]
@@ -84,7 +84,7 @@ number_prefix = "0.4"
 percent-encoding = "2.3.0"
 phf = { version = "0.11.2", features = ["macros"] }
 scoped_threadpool = "0.1"
-term_grid = "0.1"
+uutils_term_grid = "0.3"
 terminal_size = "0.3.0"
 timeago = { version = "0.4.2", default-features = false }
 unicode-width = "0.1"

+ 2 - 3
README.md

@@ -310,10 +310,9 @@ eza’s options are almost, but not quite, entirely unlike `ls`’s.
 - **-T**, **--tree**: recurse into directories as a tree
 - **-x**, **--across**: sort the grid across, rather than downwards
 - **-F**, **--classify**: display type indicator by file names
-- **--colo[u]r**: when to use terminal colours
+- **--colo[u]r=(when)**: when to use terminal colours (always, auto, never)
 - **--colo[u]r-scale**: highlight levels of file sizes distinctly
-- **--icons**: display icons
-- **--no-icons**: don't display icons (always overrides --icons)
+- **--icons=(when)**: when to display icons (always, auto, never)
 - **--hyperlink**: display entries as hyperlinks
 - **-w**, **--width=(columns)**: set screen width in columns
 

+ 5 - 0
completions/bash/eza

@@ -13,6 +13,11 @@ _eza() {
             return
             ;;
 
+        --icons)
+            mapfile -t COMPREPLY < <(compgen -W 'always automatic auto never' -- "$cur")
+            return
+            ;;
+
         -L|--level)
             mapfile -t COMPREPLY < <(compgen -W '{0..9}' -- "$cur")
             return

+ 6 - 2
completions/fish/eza.fish

@@ -20,8 +20,12 @@ complete -c eza -l color \
 "
 complete -c eza -l color-scale \
     -l colour-scale -d "Highlight levels of file sizes distinctly"
-complete -c eza -l icons -d "Display icons"
-complete -c eza -l no-icons -d "Don't display icons"
+complete -c eza -l icons -d "When to display icons" -x -a "
+  always\t'Always display icons'
+  auto\t'Display icons if standard output is a terminal'
+  automatic\t'Display icons if standard output is a terminal'
+  never\t'Never display icons'
+"
 complete -c eza -l no-quotes -d "Don't quote file names with spaces"
 complete -c eza -l hyperlink -d "Display entries as hyperlinks"
 complete -c eza -l smart-group -d "Only show group if it has a different name from owner"

+ 1 - 2
completions/nush/eza.nu

@@ -13,8 +13,7 @@ export extern "eza" [
     --colour                   # When to use terminal colours
     --color-scale              # Highlight levels of file sizes distinctly
     --colour-scale             # Highlight levels of file sizes distinctly
-    --icons                    # Display icons
-    --no-icons                 # Don't display icons
+    --icons                    # When to display icons
     --no-quotes                # Don't quote file names with spaces
     --hyperlink                # Display entries as hyperlinks
     --group-directories-first  # Sort directories before other files

+ 1 - 2
completions/zsh/_eza

@@ -22,8 +22,7 @@ __eza() {
         {-F,--classify}"[Display type indicator by file names]" \
         --colo{,u}r="[When to use terminal colours]:(when):(always auto automatic never)" \
         --colo{,u}r-scale"[Highlight levels of file sizes distinctly]" \
-        --icons"[Display icons]" \
-        --no-icons"[Hide icons]" \
+        --icons="[When to display icons]:(when):(always auto automatic never)" \
         --no-quotes"[Don't quote filenames with spaces]" \
         --hyperlink"[Display entries as hyperlinks]" \
         --group-directories-first"[Sort directories before other files]" \

+ 5 - 3
man/eza.1.md

@@ -88,11 +88,13 @@ Manually setting this option overrides `NO_COLOR` environment.
 `--color-scale`, `--colour-scale`
 : Colour file sizes on a scale.
 
-`--icons`
+`--icons=WHEN`
 : Display icons next to file names.
 
-`--no-icons`
-: Don't display icons. (Always overrides --icons)
+Valid settings are ‘`always`’, ‘`automatic`’ (‘`auto`’ for short), and ‘`never`’.
+The default value is ‘`automatic`’.
+
+`automatic` or `auto` will display icons only when the standard output is connected to a real terminal. If `eza` is ran while in a `tty`, or the output of `eza` is either redirected to a file or piped into another program, icons will not be used. Setting this option to ‘`always`’ causes `eza` to always display icons, while ‘`never`’ disables the use of icons.
 
 `--no-quotes`
 : Don't quote file names with spaces.

+ 2 - 1
man/eza_colors-explanation.5.md

@@ -40,8 +40,9 @@ files; setting `EZA_COLORS="reset"` will highlight nothing.
 - Documents (pdf, doc, dvi) are a less faint blue.
 - Compressed files (zip, tgz, Z) are red.
 - Temporary files (tmp, swp, ~) are grey.
-- Compiled files (class, o, pyc) are faint orange. A file is also counted as compiled if it uses a common extension and is
+- Compiled files (class, o, pyc) are yellow. A file is also counted as compiled if it uses a common extension and is
 in the same directory as one of its source files: styles.css will count as compiled when next to styles.less or styles.sass, and scripts.js when next to scripts.ts or scripts.coffee.
+- Source files (cpp, js, java) are bright yellow.
 
 
 ## See also

+ 3 - 0
man/eza_colors.5.md

@@ -282,6 +282,9 @@ LIST OF CODES
 `bu`
 : a regular file that is used to build a project (ex: Makefile)
 
+`sc`
+: a regular file that is source code
+
 `Sn`
 : No security context on a file
 

+ 103 - 2
src/info/filetype.rs

@@ -23,8 +23,9 @@ pub enum FileType {
     Temp,
     Compiled,
     Build, // A “build file is something that can be run or activated somehow in order to
-           // kick off the build of a project. It’s usually only present in directories full of
-           // source code.
+    // kick off the build of a project. It’s usually only present in directories full of
+    // source code.
+    Source,
 }
 
 /// Mapping from full filenames to file type.
@@ -131,6 +132,7 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     /* Video files */
     "avi"        => FileType::Video,
     "flv"        => FileType::Video,
+    "h264"       => FileType::Video,
     "heics"      => FileType::Video,
     "m2ts"       => FileType::Video,
     "m2v"        => FileType::Video,
@@ -156,10 +158,15 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "opus"       => FileType::Music,
     "wma"        => FileType::Music,
     /* Lossless music, rather than any other kind of data... */
+    "aif"        => FileType::Lossless,
+    "aifc"       => FileType::Lossless,
+    "aiff"       => FileType::Lossless,
     "alac"       => FileType::Lossless,
     "ape"        => FileType::Lossless,
     "flac"       => FileType::Lossless,
+    "pcm"        => FileType::Lossless,
     "wav"        => FileType::Lossless,
+    "wv"         => FileType::Lossless,
     /* Cryptology files */
     "asc"        => FileType::Crypto, // GnuPG ASCII armored file
     "gpg"        => FileType::Crypto, // GnuPG encrypted file
@@ -201,6 +208,7 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     /* Compressed/archive files */
     "7z"         => FileType::Compressed,
     "ar"         => FileType::Compressed,
+    "arj"        => FileType::Compressed,
     "br"         => FileType::Compressed,
     "bz"         => FileType::Compressed,
     "bz2"        => FileType::Compressed,
@@ -240,8 +248,10 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "bak"        => FileType::Temp,
     "bk"         => FileType::Temp,
     "bkp"        => FileType::Temp,
+    "crdownload" => FileType::Temp,
     "download"   => FileType::Temp,
     "fdmdownload"=> FileType::Temp,
+    "part"       => FileType::Temp,
     "swn"        => FileType::Temp,
     "swo"        => FileType::Temp,
     "swp"        => FileType::Temp,
@@ -250,6 +260,10 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "a"          => FileType::Compiled, // Unix static library
     "bundle"     => FileType::Compiled, // Mac OS X application bundle
     "class"      => FileType::Compiled, // Java class file
+    "cma"        => FileType::Compiled, // OCaml bytecode library
+    "cmi"        => FileType::Compiled, // OCaml interface
+    "cmo"        => FileType::Compiled, // OCaml bytecode object
+    "cmx"        => FileType::Compiled, // OCaml bytecode object for inlining
     "dll"        => FileType::Compiled, // Windows dynamic link library
     "dylib"      => FileType::Compiled, // Mach-O dynamic library
     "elc"        => FileType::Compiled, // Emacs compiled lisp
@@ -262,6 +276,93 @@ const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! {
     "pyo"        => FileType::Compiled, // Python optimized code
     "so"         => FileType::Compiled, // Unix shared library
     "zwc"        => FileType::Compiled, // zsh compiled file
+    /* Source code */
+    "applescript"=> FileType::Source, // Apple script
+    "as"         => FileType::Source, // Action script
+    "asa"        => FileType::Source, // asp
+    "awk"        => FileType::Source, // awk
+    "c"          => FileType::Source, // C/C++
+    "c++"        => FileType::Source, // C/C++
+    "cabal"      => FileType::Source, // Cabal
+    "cc"         => FileType::Source, // C/C++
+    "clj"        => FileType::Source, // Clojure
+    "cp"         => FileType::Source, // C/C++ Xcode
+    "cpp"        => FileType::Source, // C/C++
+    "cr"         => FileType::Source, // Crystal
+    "cs"         => FileType::Source, // C#
+    "css"        => FileType::Source, // css
+    "csx"        => FileType::Source, // C#
+    "cu"         => FileType::Source, // CUDA
+    "cxx"        => FileType::Source, // C/C++
+    "d"          => FileType::Source, // D
+    "dart"       => FileType::Source, // Dart
+    "di"         => FileType::Source, // D
+    "dpr"        => FileType::Source, // Delphi Pascal
+    "el"         => FileType::Source, // Lisp
+    "elm"        => FileType::Source, // Elm
+    "erl"        => FileType::Source, // Erlang
+    "ex"         => FileType::Source, // Elixir
+    "exs"        => FileType::Source, // Elixir
+    "fs"         => FileType::Source, // F#
+    "fsh"        => FileType::Source, // Fragment shader
+    "fsi"        => FileType::Source, // F#
+    "fsx"        => FileType::Source, // F#
+    "go"         => FileType::Source, // Go
+    "gradle"     => FileType::Source, // Gradle
+    "groovy"     => FileType::Source, // Groovy
+    "gvy"        => FileType::Source, // Groovy
+    "h"          => FileType::Source, // C/C++
+    "h++"        => FileType::Source, // C/C++
+    "hpp"        => FileType::Source, // C/C++
+    "hs"         => FileType::Source, // Haskell
+    "htc"        => FileType::Source, // Javascript
+    "hxx"        => FileType::Source, // C/C++
+    "inc"        => FileType::Source,
+    "inl"        => FileType::Source, // C/C++ Microsoft
+    "ipynb"      => FileType::Source, // Jupyter Notebook
+    "java"       => FileType::Source, // Java
+    "jl"         => FileType::Source, // Julia
+    "js"         => FileType::Source, // Javascript
+    "jsx"        => FileType::Source, // React
+    "kt"         => FileType::Source, // Kotlin
+    "kts"        => FileType::Source, // Kotlin
+    "less"       => FileType::Source, // less
+    "lhs"        => FileType::Source, // Haskell
+    "lisp"       => FileType::Source, // Lisp
+    "ltx"        => FileType::Source, // LaTeX
+    "lua"        => FileType::Source, // Lua
+    "m"          => FileType::Source, // Matlab
+    "matlab"     => FileType::Source, // Matlab
+    "ml"         => FileType::Source, // OCaml
+    "mli"        => FileType::Source, // OCaml
+    "mn"         => FileType::Source, // Matlab
+    "nb"         => FileType::Source, // Mathematica
+    "p"          => FileType::Source, // Pascal
+    "pas"        => FileType::Source, // Pascal
+    "php"        => FileType::Source, // PHP
+    "pl"         => FileType::Source, // Perl
+    "pm"         => FileType::Source, // Perl
+    "pod"        => FileType::Source, // Perl
+    "pp"         => FileType::Source, // Puppet
+    "ps1"        => FileType::Source, // PowerShell
+    "psd1"       => FileType::Source, // PowerShell
+    "psm1"       => FileType::Source, // PowerShell
+    "purs"       => FileType::Source, // PureScript
+    "py"         => FileType::Source, // Python
+    "r"          => FileType::Source, // R
+    "rb"         => FileType::Source, // Ruby
+    "rs"         => FileType::Source, // Rust
+    "sass"       => FileType::Source, // Sass
+    "scala"      => FileType::Source, // Scala
+    "scss"       => FileType::Source, // Sass
+    "sql"        => FileType::Source, // SQL
+    "swift"      => FileType::Source, // Swift
+    "tcl"        => FileType::Source, // TCL
+    "tex"        => FileType::Source, // LaTeX
+    "ts"         => FileType::Source, // TypeScript
+    "v"          => FileType::Source, // V
+    "vb"         => FileType::Source, // Visual Basic
+    "vsh"        => FileType::Source, // Vertex shader
 };
 
 impl FileType {

+ 38 - 9
src/options/file_name.rs

@@ -5,7 +5,11 @@ use crate::options::{flags, NumberSource, OptionsError};
 use crate::output::file_name::{Classify, EmbedHyperlinks, Options, QuoteStyle, ShowIcons};
 
 impl Options {
-    pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
+    pub fn deduce<V: Vars>(
+        matches: &MatchedFlags<'_>,
+        vars: &V,
+        is_a_tty: bool,
+    ) -> Result<Self, OptionsError> {
         let classify = Classify::deduce(matches)?;
         let show_icons = ShowIcons::deduce(matches, vars)?;
 
@@ -17,6 +21,7 @@ impl Options {
             show_icons,
             quote_style,
             embed_hyperlinks,
+            is_a_tty,
         })
     }
 }
@@ -35,24 +40,48 @@ impl Classify {
 
 impl ShowIcons {
     pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
-        if matches.has(&flags::NO_ICONS)? || !matches.has(&flags::ICONS)? {
-            Ok(Self::Off)
-        } else if let Some(columns) = vars
-            .get_with_fallback(vars::EZA_ICON_SPACING, vars::EXA_ICON_SPACING)
+        enum AlwaysOrAuto {
+            Always,
+            Automatic,
+        }
+
+        let mode_opt = matches.get(&flags::ICONS)?;
+        if !matches.has(&flags::ICONS)? && mode_opt.is_none() {
+            return Ok(Self::Never);
+        }
+
+        let mode = match mode_opt {
+            Some(word) => match word.to_str() {
+                Some("always") => AlwaysOrAuto::Always,
+                Some("auto" | "automatic") => AlwaysOrAuto::Automatic,
+                Some("never") => return Ok(Self::Never),
+                None => AlwaysOrAuto::Automatic,
+                _ => return Err(OptionsError::BadArgument(&flags::ICONS, word.into())),
+            },
+            None => AlwaysOrAuto::Automatic,
+        };
+
+        let width = if let Some(columns) = vars
+            .get_with_fallback(vars::EXA_ICON_SPACING, vars::EZA_ICON_SPACING)
             .and_then(|s| s.into_string().ok())
         {
             match columns.parse() {
-                Ok(width) => Ok(Self::On(width)),
+                Ok(width) => width,
                 Err(e) => {
                     let source = NumberSource::Env(
-                        vars.source(vars::EZA_ICON_SPACING, vars::EXA_ICON_SPACING)
+                        vars.source(vars::EXA_ICON_SPACING, vars::EZA_ICON_SPACING)
                             .unwrap(),
                     );
-                    Err(OptionsError::FailedParse(columns, source, e))
+                    return Err(OptionsError::FailedParse(columns, source, e));
                 }
             }
         } else {
-            Ok(Self::On(1))
+            1
+        };
+
+        match mode {
+            AlwaysOrAuto::Always => Ok(Self::Always(width)),
+            AlwaysOrAuto::Automatic => Ok(Self::Automatic(width)),
         }
     }
 }

+ 6 - 7
src/options/flags.rs

@@ -16,9 +16,9 @@ pub static DEREF_LINKS: Arg = Arg { short: Some(b'X'), long: "dereference", take
 pub static WIDTH:       Arg = Arg { short: Some(b'w'), long: "width",       takes_value: TakesValue::Necessary(None) };
 pub static NO_QUOTES:   Arg = Arg { short: None,       long: "no-quotes",   takes_value: TakesValue::Forbidden };
 
-pub static COLOR:  Arg = Arg { short: None, long: "color",  takes_value: TakesValue::Necessary(Some(COLOURS)) };
-pub static COLOUR: Arg = Arg { short: None, long: "colour", takes_value: TakesValue::Necessary(Some(COLOURS)) };
-const COLOURS: &[&str] = &["always", "auto", "never"];
+pub static COLOR:  Arg = Arg { short: None, long: "color",  takes_value: TakesValue::Optional(Some(WHEN)) };
+pub static COLOUR: Arg = Arg { short: None, long: "colour", takes_value: TakesValue::Optional(Some(WHEN)) };
+const WHEN: &[&str] = &["always", "auto", "never"];
 
 pub static COLOR_SCALE:  Arg = Arg { short: None, long: "color-scale",  takes_value: TakesValue::Forbidden };
 pub static COLOUR_SCALE: Arg = Arg { short: None, long: "colour-scale", takes_value: TakesValue::Forbidden };
@@ -45,13 +45,13 @@ pub static BYTES:       Arg = Arg { short: Some(b'B'), long: "bytes",       take
 pub static GROUP:       Arg = Arg { short: Some(b'g'), long: "group",       takes_value: TakesValue::Forbidden };
 pub static NUMERIC:     Arg = Arg { short: Some(b'n'), long: "numeric",     takes_value: TakesValue::Forbidden };
 pub static HEADER:      Arg = Arg { short: Some(b'h'), long: "header",      takes_value: TakesValue::Forbidden };
-pub static ICONS:       Arg = Arg { short: None,       long: "icons",       takes_value: TakesValue::Forbidden };
+pub static ICONS:       Arg = Arg { short: None,       long: "icons",       takes_value: TakesValue::Optional(Some(WHEN))};
 pub static INODE:       Arg = Arg { short: Some(b'i'), long: "inode",       takes_value: TakesValue::Forbidden };
 pub static LINKS:       Arg = Arg { short: Some(b'H'), long: "links",       takes_value: TakesValue::Forbidden };
 pub static MODIFIED:    Arg = Arg { short: Some(b'm'), long: "modified",    takes_value: TakesValue::Forbidden };
 pub static CHANGED:     Arg = Arg { short: None,       long: "changed",     takes_value: TakesValue::Forbidden };
 pub static BLOCKSIZE:   Arg = Arg { short: Some(b'S'), long: "blocksize",   takes_value: TakesValue::Forbidden };
-pub static TOTALSIZE:   Arg = Arg { short: None,       long: "total-size",   takes_value: TakesValue::Forbidden };
+pub static TOTALSIZE:   Arg = Arg { short: None,       long: "total-size",  takes_value: TakesValue::Forbidden };
 pub static TIME:        Arg = Arg { short: Some(b't'), long: "time",        takes_value: TakesValue::Necessary(Some(TIMES)) };
 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 };
@@ -67,7 +67,6 @@ pub static NO_PERMISSIONS: Arg = Arg { short: None, long: "no-permissions", take
 pub static NO_FILESIZE: Arg = Arg { short: None, long: "no-filesize", takes_value: TakesValue::Forbidden };
 pub static NO_USER: Arg = Arg { short: None, long: "no-user", takes_value: TakesValue::Forbidden };
 pub static NO_TIME: Arg = Arg { short: None, long: "no-time", takes_value: TakesValue::Forbidden };
-pub static NO_ICONS: Arg = Arg { short: None, long: "no-icons", takes_value: TakesValue::Forbidden };
 
 // optional feature options
 pub static GIT:               Arg = Arg { short: None,       long: "git",                  takes_value: TakesValue::Forbidden };
@@ -89,7 +88,7 @@ pub static ALL_ARGS: Args = Args(&[
 
     &BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED,
     &BLOCKSIZE, &TOTALSIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK, &MOUNTS,
-    &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS, &SMART_GROUP,
+    &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &SMART_GROUP,
 
     &GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT,
     &EXTENDED, &OCTAL, &SECURITY_CONTEXT

+ 2 - 3
src/options/help.rs

@@ -22,8 +22,7 @@ DISPLAY OPTIONS
   -F, --classify     display type indicator by file names
   --colo[u]r=WHEN    when to use terminal colours (always, auto, never)
   --colo[u]r-scale   highlight levels of file sizes distinctly
-  --icons            display icons
-  --no-icons         don't display icons (always overrides --icons)
+  --icons=WHEN       when to display icons (always, auto, never)
   --no-quotes        don't quote file names with spaces
   --hyperlink        display entries as hyperlinks
   -w, --width COLS   set screen width in columns
@@ -153,6 +152,6 @@ mod test {
     fn unhelpful() {
         let args = vec![];
         let opts = Options::parse(args, &None);
-        assert!(!matches!(opts, OptionsResult::Help(_))) // no help when --help isn’t passed
+        assert!(!matches!(opts, OptionsResult::Help(_))); // no help when --help isn’t passed
     }
 }

+ 15 - 7
src/options/parser.rs

@@ -152,7 +152,7 @@ impl Args {
         // Iterate over the inputs with “while let” because we need to advance
         // the iterator manually whenever an argument that takes a value
         // doesn’t have one in its string so it needs the next one.
-        let mut inputs = inputs.into_iter();
+        let mut inputs = inputs.into_iter().peekable();
         while let Some(arg) = inputs.next() {
             let bytes = os_str_to_bytes(arg);
 
@@ -198,13 +198,12 @@ impl Args {
                                 return Err(ParseError::NeedsValue { flag, values });
                             }
                         }
-                        TakesValue::Optional(_) => {
-                            if let Some(next_arg) = inputs.next() {
-                                result_flags.push((flag, Some(next_arg)));
-                            } else {
-                                result_flags.push((flag, None));
+                        TakesValue::Optional(_) => match inputs.peek() {
+                            Some(next_arg) if is_optional_arg(next_arg) => {
+                                result_flags.push((flag, Some(inputs.next().unwrap())));
                             }
-                        }
+                            _ => result_flags.push((flag, None)),
+                        },
                     }
                 }
             }
@@ -332,6 +331,15 @@ impl Args {
     }
 }
 
+fn is_optional_arg(arg: &OsStr) -> bool {
+    let bytes = os_str_to_bytes(arg);
+    match bytes {
+        // The only optional arguments allowed
+        b"always" | b"auto" | b"automatic" | b"never" => true,
+        _ => false,
+    }
+}
+
 /// The **matches** are the result of parsing the user’s command-line strings.
 #[derive(PartialEq, Eq, Debug)]
 pub struct Matches<'args> {

+ 2 - 2
src/options/view.rs

@@ -12,10 +12,10 @@ use crate::output::{details, grid, Mode, TerminalWidth, View};
 impl View {
     pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
         let mode = Mode::deduce(matches, vars)?;
-        let width = TerminalWidth::deduce(matches, vars)?;
-        let file_style = FileStyle::deduce(matches, vars)?;
         let deref_links = matches.has(&flags::DEREF_LINKS)?;
         let total_size = matches.has(&flags::TOTALSIZE)?;
+        let width = TerminalWidth::deduce(matches, vars)?;
+        let file_style = FileStyle::deduce(matches, vars, width.actual_terminal_width().is_some())?;
         Ok(Self {
             mode,
             width,

+ 22 - 13
src/output/file_name.rs

@@ -24,6 +24,9 @@ pub struct Options {
 
     /// Whether to make file names hyperlinks.
     pub embed_hyperlinks: EmbedHyperlinks,
+
+    /// Whether we are in a console or redirecting the output
+    pub is_a_tty: bool,
 }
 
 impl Options {
@@ -94,14 +97,17 @@ enum MountStyle {
 }
 
 /// Whether and how to show icons.
-#[derive(PartialEq, Eq, Debug, Copy, Clone)]
+#[derive(PartialEq, Debug, Copy, Clone)]
 pub enum ShowIcons {
-    /// Don’t show icons at all.
-    Off,
+    /// Display icons next to file names, with the given number of spaces between
+    /// the icon and the file name, even when output isn’t going to a terminal.
+    Always(u32),
+
+    /// Same as Always, but only when output is going to a terminal, not otherwise.
+    Automatic(u32),
 
-    /// Show icons next to file names, with the given number of spaces between
-    /// the icon and the file name.
-    On(usize),
+    /// Never display them, even when output is going to a terminal.
+    Never,
 }
 
 /// Whether to embed hyperlinks.
@@ -176,13 +182,17 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
     pub fn paint(&self) -> TextCellContents {
         let mut bits = Vec::new();
 
-        if let ShowIcons::On(spaces_count) = self.options.show_icons {
+        let spaces_count_opt = match self.options.show_icons {
+            ShowIcons::Always(spaces_count) => Some(spaces_count),
+            ShowIcons::Automatic(spaces_count) if self.options.is_a_tty => Some(spaces_count),
+            _ => None,
+        };
+
+        if let Some(spaces_count) = spaces_count_opt {
             let style = iconify_style(self.style());
             let file_icon = icon_for_file(self.file).to_string();
-
             bits.push(style.paint(file_icon));
-
-            bits.push(style.paint(" ".repeat(spaces_count)));
+            bits.push(style.paint(" ".repeat(spaces_count as usize)));
         }
 
         if self.file.parent_dir.is_none() {
@@ -217,11 +227,10 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
                     if !target.name.is_empty() {
                         let target_options = Options {
                             classify: Classify::JustFilenames,
-                            show_icons: ShowIcons::Off,
-
                             quote_style: QuoteStyle::QuoteSpaces,
-
+                            show_icons: ShowIcons::Never,
                             embed_hyperlinks: EmbedHyperlinks::Off,
+                            is_a_tty: self.options.is_a_tty,
                         };
 
                         let target_name = FileName {

+ 8 - 4
src/output/grid.rs

@@ -61,12 +61,16 @@ impl<'a> Render<'a> {
                 filename.options.embed_hyperlinks,
                 filename.options.show_icons,
             ) {
-                (EmbedHyperlinks::On, ShowIcons::On(spacing)) => {
-                    filename.bare_width() + classification_width + 1 + spacing
-                }
-                (EmbedHyperlinks::On, ShowIcons::Off) => {
+                #[rustfmt::skip]
+                (EmbedHyperlinks::On, ShowIcons::Always(spacing)
+                | ShowIcons::Automatic(spacing))                  => filename.bare_width() + classification_width + 1 + (spacing as usize),
+                (EmbedHyperlinks::On, ShowIcons::Never) => {
                     filename.bare_width() + classification_width
                 }
+                (
+                    EmbedHyperlinks::Off,
+                    ShowIcons::Always(spacing) | ShowIcons::Automatic(spacing),
+                ) => filename.bare_width() + 1 + (spacing as usize),
                 (EmbedHyperlinks::Off, _) => *contents.width(),
             };
 

+ 3 - 2
src/output/grid_details.rs

@@ -170,8 +170,9 @@ impl<'a> Render<'a> {
                 let contents = filename.paint();
                 #[rustfmt::skip]
                 let width = match (filename.options.embed_hyperlinks, filename.options.show_icons) {
-                    (EmbedHyperlinks::On, ShowIcons::On(spacing)) => filename.bare_width() + 1 + spacing,
-                    (EmbedHyperlinks::On, ShowIcons::Off) => filename.bare_width(),
+                    (EmbedHyperlinks::On, ShowIcons::Automatic(spacing)) => filename.bare_width() + 1 + (spacing as usize),
+                    (EmbedHyperlinks::On, ShowIcons::Always(spacing)) => filename.bare_width() + 1 + (spacing as usize),
+                    (EmbedHyperlinks::On, ShowIcons::Never) => filename.bare_width(),
                     (EmbedHyperlinks::Off, _) => *contents.width(),
                 };
 

+ 50 - 9
src/output/icons.rs

@@ -23,6 +23,7 @@ impl Icons {
     const DOCUMENT: char        = '\u{f1c2}';  // 
     const DOWNLOAD: char        = '\u{f01da}'; // 󰇚
     const EMACS: char           = '\u{e632}';  // 
+    const ESLINT: char          = '\u{e655}';  // 
     const FILE: char            = '\u{f15b}';  // 
     const FILE_OUTLINE: char    = '\u{f016}';  // 
     const FOLDER: char          = '\u{e5ff}';  // 
@@ -49,9 +50,11 @@ impl Icons {
     const LANG_C: char          = '\u{e61e}';  // 
     const LANG_CPP: char        = '\u{e61d}';  // 
     const LANG_CSHARP: char     = '\u{f031b}'; // 󰌛
+    const LANG_D: char          = '\u{e7af}';  // 
     const LANG_ELIXIR: char     = '\u{e62d}';  // 
     const LANG_FSHARP: char     = '\u{e7a7}';  // 
     const LANG_GO: char         = '\u{e65e}';  // 
+    const LANG_GROOVY: char     = '\u{e775}';  // 
     const LANG_HASKELL: char    = '\u{e777}';  // 
     const LANG_JAVA: char       = '\u{e256}';  // 
     const LANG_JAVASCRIPT: char = '\u{e74e}';  // 
@@ -89,8 +92,8 @@ impl Icons {
     const REACT: char           = '\u{e7ba}';  // 
     const README: char          = '\u{f00ba}'; // 󰂺
     const SHEET: char           = '\u{f1c3}';  // 
-    const SHELL_CMD: char       = '\u{f489}';  // 
     const SHELL: char           = '\u{f1183}'; // 󱆃
+    const SHELL_CMD: char       = '\u{f489}';  // 
     const SHIELD_CHECK: char    = '\u{f0565}'; // 󰕥
     const SHIELD_KEY: char      = '\u{f0bc4}'; // 󰯄
     const SHIELD_LOCK: char     = '\u{f099d}'; // 󰦝
@@ -119,6 +122,7 @@ const DIRECTORY_ICONS: Map<&'static str, char> = phf_map! {
     ".npm"                => Icons::FOLDER_NPM,     // 
     ".ssh"                => Icons::FOLDER_KEY,     // 󰢬
     ".Trash"              => '\u{f1f8}',            // 
+    "config"              => Icons::FOLDER_CONFIG,  // 
     "Contacts"            => '\u{f024c}',           // 󰉌
     "cron.d"              => Icons::FOLDER_CONFIG,  // 
     "cron.daily"          => Icons::FOLDER_CONFIG,  // 
@@ -127,7 +131,6 @@ const DIRECTORY_ICONS: Map<&'static str, char> = phf_map! {
     "cron.weekly"         => Icons::FOLDER_CONFIG,  // 
     "Desktop"             => '\u{f108}',            // 
     "Downloads"           => '\u{f024d}',           // 󰉍
-    "config"              => Icons::FOLDER_CONFIG,  // 
     "etc"                 => Icons::FOLDER_CONFIG,  // 
     "Favorites"           => '\u{f069d}',           // 󰚝
     "hidden"              => Icons::FOLDER_HIDDEN,  // 󱞞
@@ -160,6 +163,11 @@ const FILENAME_ICONS: Map<&'static str, char> = phf_map! {
     ".cshrc"              => Icons::SHELL,          // 󱆃
     ".DS_Store"           => Icons::OS_APPLE,       // 
     ".emacs"              => Icons::EMACS,          // 
+    ".eslintrc.cjs"       => Icons::ESLINT,         // 
+    ".eslintrc.js"        => Icons::ESLINT,         // 
+    ".eslintrc.json"      => Icons::ESLINT,         // 
+    ".eslintrc.yaml"      => Icons::ESLINT,         // 
+    ".eslintrc.yml"       => Icons::ESLINT,         // 
     ".gitattributes"      => Icons::GIT,            // 
     ".gitconfig"          => Icons::GIT,            // 
     ".gitignore"          => Icons::GIT,            // 
@@ -174,6 +182,7 @@ const FILENAME_ICONS: Map<&'static str, char> = phf_map! {
     ".kshrc"              => Icons::SHELL,          // 󱆃
     ".login"              => Icons::SHELL,          // 󱆃
     ".logout"             => Icons::SHELL,          // 󱆃
+    ".mailmap"            => Icons::GIT,            // 
     ".node_repl_history"  => Icons::NODEJS,         // 
     ".npmignore"          => Icons::NPM,            // 
     ".npmrc"              => Icons::NPM,            // 
@@ -213,6 +222,8 @@ const FILENAME_ICONS: Map<&'static str, char> = phf_map! {
     "configure.ac"        => Icons::CONFIG,         // 
     "configure.in"        => Icons::CONFIG,         // 
     "constraints.txt"     => Icons::LANG_PYTHON,    // 
+    "COPYING"             => Icons::LICENSE,        // 
+    "COPYRIGHT"           => Icons::LICENSE,        // 
     "crontab"             => Icons::CONFIG,         // 
     "crypttab"            => Icons::CONFIG,         // 
     "csh.cshrc"           => Icons::SHELL,          // 󱆃
@@ -255,6 +266,8 @@ const FILENAME_ICONS: Map<&'static str, char> = phf_map! {
     "LICENCE.md"          => Icons::LICENSE,        // 
     "LICENCE.txt"         => Icons::LICENSE,        // 
     "LICENSE"             => Icons::LICENSE,        // 
+    "LICENSE-APACHE"      => Icons::LICENSE,        // 
+    "LICENSE-MIT"         => Icons::LICENSE,        // 
     "LICENSE.md"          => Icons::LICENSE,        // 
     "LICENSE.txt"         => Icons::LICENSE,        // 
     "localized"           => Icons::OS_APPLE,       // 
@@ -271,8 +284,8 @@ const FILENAME_ICONS: Map<&'static str, char> = phf_map! {
     "package-lock.json"   => Icons::NPM,            // 
     "package.json"        => Icons::NPM,            // 
     "passwd"              => Icons::LOCK,           // 
-    "PKGBUILD"            => '\u{f303}',            // 
     "php.ini"             => Icons::LANG_PHP,       // 
+    "PKGBUILD"            => '\u{f303}',            // 
     "pom.xml"             => '\u{e674}',            // 
     "Procfile"            => '\u{e77b}',            // 
     "profile"             => Icons::SHELL,          // 󱆃
@@ -308,12 +321,16 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "acc"            => Icons::AUDIO,            // 
     "acf"            => '\u{f1b6}',              // 
     "ai"             => '\u{e7b4}',              // 
+    "aif"            => Icons::AUDIO,            // 
+    "aifc"           => Icons::AUDIO,            // 
+    "aiff"           => Icons::AUDIO,            // 
     "alac"           => Icons::AUDIO,            // 
     "android"        => Icons::OS_ANDROID,       // 
     "ape"            => Icons::AUDIO,            // 
     "apk"            => Icons::OS_ANDROID,       // 
     "apple"          => Icons::OS_APPLE,         // 
     "ar"             => Icons::COMPRESSED,       // 
+    "arj"            => Icons::COMPRESSED,       // 
     "arw"            => Icons::IMAGE,            // 
     "asc"            => Icons::SHIELD_LOCK,      // 󰦝
     "asm"            => Icons::LANG_ASSEMBLY,    // 
@@ -357,7 +374,9 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "cp"             => Icons::LANG_CPP,         // 
     "cpio"           => Icons::COMPRESSED,       // 
     "cpp"            => Icons::LANG_CPP,         // 
+    "cr"             => '\u{e62f}',              // 
     "cr2"            => Icons::IMAGE,            // 
+    "crdownload"     => Icons::DOWNLOAD,         // 󰇚
     "crt"            => Icons::GIST_SECRET,      // 
     "cs"             => Icons::LANG_CSHARP,      // 󰌛
     "csh"            => Icons::SHELL_CMD,        // 
@@ -370,11 +389,12 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "cu"             => '\u{e64b}',              // 
     "cue"            => Icons::PLAYLIST,         // 󰲹
     "cxx"            => Icons::LANG_CPP,         // 
-    "d"              => '\u{e7af}',              // 
+    "d"              => Icons::LANG_D,           // 
     "dart"           => '\u{e798}',              // 
     "db"             => Icons::DATABASE,         // 
     "deb"            => '\u{e77d}',              // 
     "desktop"        => '\u{ebd1}',              // 
+    "di"             => Icons::LANG_D,           // 
     "diff"           => Icons::DIFF,             // 
     "djv"            => Icons::DOCUMENT,         // 
     "djvu"           => Icons::DOCUMENT,         // 
@@ -409,6 +429,8 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "fish"           => Icons::SHELL_CMD,        // 
     "flac"           => Icons::AUDIO,            // 
     "flv"            => Icons::VIDEO,            // 
+    "fnt"            => Icons::FONT,             // 
+    "fon"            => Icons::FONT,             // 
     "font"           => Icons::FONT,             // 
     "fs"             => Icons::LANG_FSHARP,      // 
     "fsi"            => Icons::LANG_FSHARP,      // 
@@ -423,13 +445,17 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "go"             => Icons::LANG_GO,          // 
     "gpg"            => Icons::SHIELD_LOCK,      // 󰦝
     "gradle"         => Icons::GRADLE,           // 
-    "groovy"         => '\u{e775}',              // 
+    "groovy"         => Icons::LANG_GROOVY,      // 
     "gsheet"         => Icons::SHEET,            // 
     "gslides"        => Icons::SLIDE,            // 
     "guardfile"      => Icons::LANG_RUBY,        // 
     "gv"             => '\u{f1049}',             // 󱁉
+    "gvy"            => Icons::LANG_GROOVY,      // 
     "gz"             => Icons::COMPRESSED,       // 
     "h"              => Icons::LANG_C,           // 
+    "h++"            => Icons::LANG_CPP,         // 
+    "h264"           => Icons::VIDEO,            // 
+    "haml"           => '\u{e664}',              // 
     "hbs"            => Icons::MUSTACHE,         // 
     "heic"           => Icons::IMAGE,            // 
     "heics"          => Icons::VIDEO,            // 
@@ -447,6 +473,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "image"          => Icons::DISK_IMAGE,       // 
     "img"            => Icons::DISK_IMAGE,       // 
     "iml"            => Icons::INTELLIJ,         // 
+    "inl"            => Icons::LANG_C,           // 
     "ini"            => Icons::CONFIG,           // 
     "ipynb"          => '\u{e678}',              // 
     "iso"            => Icons::DISK_IMAGE,       // 
@@ -482,12 +509,13 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "ldb"            => Icons::DATABASE,         // 
     "less"           => '\u{e758}',              // 
     "lhs"            => Icons::LANG_HASKELL,     // 
+    "lib"            => Icons::LIBRARY,          // 
     "license"        => Icons::LICENSE,          // 
     "lisp"           => '\u{f0172}',             // 󰅲
-    "lib"            => Icons::LIBRARY,          // 
     "localized"      => Icons::OS_APPLE,         // 
     "lock"           => Icons::LOCK,             // 
     "log"            => '\u{f18d}',              // 
+    "ltx"            => Icons::LANG_TEX,         // 
     "lua"            => '\u{e620}',              // 
     "lz"             => Icons::COMPRESSED,       // 
     "lz4"            => Icons::COMPRESSED,       // 
@@ -506,6 +534,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "md"             => Icons::MARKDOWN,         // 
     "md5"            => Icons::SHIELD_CHECK,     // 󰕥
     "mdb"            => Icons::DATABASE,         // 
+    "mid"            => '\u{f08f2}',             // 󰣲
     "mjs"            => Icons::LANG_JAVASCRIPT,  // 
     "mk"             => Icons::MAKE,             // 
     "mka"            => Icons::AUDIO,            // 
@@ -544,9 +573,10 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "out"            => '\u{eb2c}',              // 
     "p12"            => Icons::KEY,              // 
     "par"            => Icons::COMPRESSED,       // 
-    "part"           => '\u{f43a}',              // 
+    "part"           => Icons::DOWNLOAD,         // 󰇚
     "patch"          => Icons::DIFF,             // 
     "pbm"            => Icons::IMAGE,            // 
+    "pcm"            => Icons::AUDIO,            // 
     "pdf"            => '\u{f1c1}',              // 
     "pem"            => Icons::KEY,              // 
     "pfx"            => Icons::KEY,              // 
@@ -563,6 +593,8 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "pod"            => Icons::LANG_PERL,        // 
     "pp"             => '\u{e631}',              // 
     "ppm"            => Icons::IMAGE,            // 
+    "pps"            => Icons::SLIDE,            // 
+    "ppsx"           => Icons::SLIDE,            // 
     "ppt"            => Icons::SLIDE,            // 
     "pptx"           => Icons::SLIDE,            // 
     "properties"     => Icons::JSON,             // 
@@ -570,9 +602,10 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "ps1"            => Icons::POWERSHELL,       // 
     "psd"            => '\u{e7b8}',              // 
     "psd1"           => Icons::POWERSHELL,       // 
-    "psm1"           => Icons::POWERSHELL,       // 
     "psf"            => Icons::FONT,             // 
+    "psm1"           => Icons::POWERSHELL,       // 
     "pub"            => Icons::PUBLIC_KEY,       // 󰷖
+    "purs"           => '\u{e630}',              // 
     "pxm"            => Icons::IMAGE,            // 
     "py"             => Icons::LANG_PYTHON,      // 
     "pyc"            => Icons::LANG_PYTHON,      // 
@@ -606,6 +639,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "rubydoc"        => Icons::LANG_RUBYRAILS,   // 
     "s"              => Icons::LANG_ASSEMBLY,    // 
     "sass"           => Icons::LANG_SASS,        // 
+    "sbt"            => Icons::SUBTITLE,         // 󰨖
     "scala"          => '\u{e737}',              // 
     "scss"           => Icons::LANG_SASS,        // 
     "service"        => '\u{eba2}',              // 
@@ -616,6 +650,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "sha384"         => Icons::SHIELD_CHECK,     // 󰕥
     "sha512"         => Icons::SHIELD_CHECK,     // 󰕥
     "shell"          => Icons::SHELL_CMD,        // 
+    "shtml"          => Icons::HTML5,            // 
     "sig"            => Icons::SIGNED_FILE,      // 󱧃
     "signature"      => Icons::SIGNED_FILE,      // 󱧃
     "slim"           => Icons::LANG_RUBYRAILS,   // 
@@ -624,6 +659,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "sql"            => Icons::DATABASE,         // 
     "sqlite3"        => '\u{e7c4}',              // 
     "srt"            => Icons::SUBTITLE,         // 󰨖
+    "ssa"            => Icons::SUBTITLE,         // 󰨖
     "stl"            => Icons::IMAGE,            // 
     "sty"            => Icons::LANG_TEX,         // 
     "styl"           => Icons::LANG_STYLUS,      // 
@@ -656,6 +692,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "tif"            => Icons::IMAGE,            // 
     "tiff"           => Icons::IMAGE,            // 
     "tlz"            => Icons::COMPRESSED,       // 
+    "tml"            => Icons::CONFIG,           // 
     "toml"           => Icons::CONFIG,           // 
     "torrent"        => '\u{e275}',              // 
     "ts"             => Icons::LANG_TYPESCRIPT,  // 
@@ -670,6 +707,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "tzo"            => Icons::COMPRESSED,       // 
     "unity"          => Icons::UNITY,            // 
     "unity3d"        => Icons::UNITY,            // 
+    "v"              => '\u{e6ac}',              // 
     "vdi"            => Icons::DISK_IMAGE,       // 
     "vhd"            => Icons::DISK_IMAGE,       // 
     "video"          => Icons::VIDEO,            // 
@@ -680,6 +718,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "war"            => Icons::LANG_JAVA,        // 
     "wav"            => Icons::AUDIO,            // 
     "webm"           => Icons::VIDEO,            // 
+    "webmanifest"    => Icons::JSON,             // 
     "webp"           => Icons::IMAGE,            // 
     "whl"            => Icons::LANG_PYTHON,      // 
     "windows"        => Icons::OS_WINDOWS,       // 
@@ -687,8 +726,10 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "wmv"            => Icons::VIDEO,            // 
     "woff"           => Icons::FONT,             // 
     "woff2"          => Icons::FONT,             // 
+    "wv"             => Icons::AUDIO,            // 
     "xcf"            => Icons::IMAGE,            // 
     "xhtml"          => Icons::HTML5,            // 
+    "xlr"            => Icons::SHEET,            // 
     "xls"            => Icons::SHEET,            // 
     "xlsm"           => Icons::SHEET,            // 
     "xlsx"           => Icons::SHEET,            // 
@@ -699,7 +740,7 @@ const EXTENSION_ICONS: Map<&'static str, char> = phf_map! {
     "yaml"           => Icons::YAML,             // 
     "yml"            => Icons::YAML,             // 
     "z"              => Icons::COMPRESSED,       // 
-    "zig"            => '\u{21af}',              // ↯
+    "zig"            => '\u{e6a9}',              // 
     "zip"            => Icons::COMPRESSED,       // 
     "zsh"            => Icons::SHELL_CMD,        // 
     "zsh-theme"      => Icons::SHELL,            // 󱆃

+ 1 - 0
src/theme/default_theme.rs

@@ -104,6 +104,7 @@ impl UiStyles {
                 temp:       White.normal(),
                 compiled:   Yellow.normal(),
                 build:      Yellow.bold().underline(),
+                source:     Yellow.bold(), // Need to discuss color
             },
 
             punctuation: DarkGray.bold(),

+ 3 - 0
src/theme/mod.rs

@@ -216,6 +216,7 @@ impl FileStyle for FileTypes {
             Some(FileType::Temp)       => Some(theme.ui.file_type.temp),
             Some(FileType::Compiled)   => Some(theme.ui.file_type.compiled),
             Some(FileType::Build)      => Some(theme.ui.file_type.build),
+            Some(FileType::Source)     => Some(theme.ui.file_type.source),
             None                       => None
         };
     }
@@ -602,6 +603,8 @@ mod customs_test {
     test!(exa_tm:  ls "", exa "tm=38;5;135"  =>  colours c -> { c.file_type.temp                        = Fixed(135).normal(); });
     test!(exa_cm:  ls "", exa "cm=38;5;136"  =>  colours c -> { c.file_type.compiled                    = Fixed(136).normal(); });
     test!(exa_ie:  ls "", exa "bu=38;5;137"  =>  colours c -> { c.file_type.build                       = Fixed(137).normal(); });
+    test!(exa_bu:  ls "", exa "bu=38;5;137"  =>  colours c -> { c.file_type.build                       = Fixed(137).normal(); });
+    test!(exa_sc:  ls "", exa "sc=38;5;138"  =>  colours c -> { c.file_type.source                      = Fixed(138).normal(); });
 
     test!(exa_Sn:  ls "", exa "Sn=38;5;128"  =>  colours c -> { c.security_context.none                 = Fixed(128).normal(); });
     test!(exa_Su:  ls "", exa "Su=38;5;129"  =>  colours c -> { c.security_context.selinux.user         = Fixed(129).normal(); });

+ 2 - 0
src/theme/ui_styles.rs

@@ -155,6 +155,7 @@ pub struct FileType {
     pub temp: Style,        // tm - temporary file
     pub compiled: Style,    // cm - compilation artifact
     pub build: Style,       // bu - file that is used to build a project
+    pub source: Style,      // sc - source code
 }
 
 impl UiStyles {
@@ -269,6 +270,7 @@ impl UiStyles {
             "tm" => self.file_type.temp                 = pair.to_style(),
             "cm" => self.file_type.compiled             = pair.to_style(),
             "bu" => self.file_type.build                = pair.to_style(),
+            "sc" => self.file_type.source               = pair.to_style(),
 
             "Sn" => self.security_context.none          = pair.to_style(),
             "Su" => self.security_context.selinux.user  = pair.to_style(),

+ 1 - 1
tests/cmd/icons_all.toml

@@ -1,2 +1,2 @@
 bin.name = "eza"
-args = "tests/itest --icons"
+args = "tests/itest --icons=always"

+ 0 - 0
tests/cmd/long_icons_always.stderr


+ 21 - 0
tests/cmd/long_icons_always.stdout

@@ -0,0 +1,21 @@
+.rw-r--r--  0 nixbld  1 Jan  1970  a
+.rw-r--r--  0 nixbld  1 Jan  1970  b
+.rw-r--r--  0 nixbld  1 Jan  1970  c
+.rw-r--r--  0 nixbld  1 Jan  1970  d
+.rw-r--r--  0 nixbld  1 Jan  1970  e
+drwxr-xr-x  - nixbld  1 Jan  1970  exa
+.rw-r--r--  0 nixbld  1 Jan  1970  f
+.rw-r--r--  0 nixbld  1 Jan  1970  g
+.rw-r--r--  0 nixbld  1 Jan  1970  h
+.rw-r--r--  0 nixbld  1 Jan  1970  i
+.rw-r--r--  0 nixbld  1 Jan  1970  image.jpg.img.c.rs.log.png
+.rw-r--r-- 19 nixbld  1 Jan  1970 󰕙 index.svg
+.rw-r--r--  0 nixbld  1 Jan  1970  j
+.rw-r--r--  0 nixbld  1 Jan  1970  k
+.rw-r--r--  0 nixbld  1 Jan  1970  l
+.rw-r--r--  0 nixbld  1 Jan  1970  m
+.rw-r--r--  0 nixbld  1 Jan  1970  n
+.rw-r--r--  0 nixbld  1 Jan  1970  o
+.rw-r--r--  0 nixbld  1 Jan  1970  p
+.rw-r--r--  0 nixbld  1 Jan  1970  q
+drwxr-xr-x  - nixbld  1 Jan  1970  vagrant

+ 2 - 0
tests/cmd/long_icons_always.toml

@@ -0,0 +1,2 @@
+bin.name = "eza"
+args = "tests/itest --long --icons=always"

+ 21 - 21
tests/cmd/long_icons_nix.stdout

@@ -1,21 +1,21 @@
-.rw-r--r--  0 nixbld  1 Jan  1970 a
-.rw-r--r--  0 nixbld  1 Jan  1970 b
-.rw-r--r--  0 nixbld  1 Jan  1970 c
-.rw-r--r--  0 nixbld  1 Jan  1970 d
-.rw-r--r--  0 nixbld  1 Jan  1970 e
-drwxr-xr-x  - nixbld  1 Jan  1970 exa
-.rw-r--r--  0 nixbld  1 Jan  1970 f
-.rw-r--r--  0 nixbld  1 Jan  1970 g
-.rw-r--r--  0 nixbld  1 Jan  1970 h
-.rw-r--r--  0 nixbld  1 Jan  1970 i
-.rw-r--r--  0 nixbld  1 Jan  1970 image.jpg.img.c.rs.log.png
-.rw-r--r-- 19 nixbld  1 Jan  1970 󰕙 index.svg
-.rw-r--r--  0 nixbld  1 Jan  1970 j
-.rw-r--r--  0 nixbld  1 Jan  1970 k
-.rw-r--r--  0 nixbld  1 Jan  1970 l
-.rw-r--r--  0 nixbld  1 Jan  1970 m
-.rw-r--r--  0 nixbld  1 Jan  1970 n
-.rw-r--r--  0 nixbld  1 Jan  1970 o
-.rw-r--r--  0 nixbld  1 Jan  1970 p
-.rw-r--r--  0 nixbld  1 Jan  1970 q
-drwxr-xr-x  - nixbld  1 Jan  1970 vagrant
+.rw-r--r--  0 nixbld  1 Jan  1970 a
+.rw-r--r--  0 nixbld  1 Jan  1970 b
+.rw-r--r--  0 nixbld  1 Jan  1970 c
+.rw-r--r--  0 nixbld  1 Jan  1970 d
+.rw-r--r--  0 nixbld  1 Jan  1970 e
+drwxr-xr-x  - nixbld  1 Jan  1970 exa
+.rw-r--r--  0 nixbld  1 Jan  1970 f
+.rw-r--r--  0 nixbld  1 Jan  1970 g
+.rw-r--r--  0 nixbld  1 Jan  1970 h
+.rw-r--r--  0 nixbld  1 Jan  1970 i
+.rw-r--r--  0 nixbld  1 Jan  1970 image.jpg.img.c.rs.log.png
+.rw-r--r-- 19 nixbld  1 Jan  1970 index.svg
+.rw-r--r--  0 nixbld  1 Jan  1970 j
+.rw-r--r--  0 nixbld  1 Jan  1970 k
+.rw-r--r--  0 nixbld  1 Jan  1970 l
+.rw-r--r--  0 nixbld  1 Jan  1970 m
+.rw-r--r--  0 nixbld  1 Jan  1970 n
+.rw-r--r--  0 nixbld  1 Jan  1970 o
+.rw-r--r--  0 nixbld  1 Jan  1970 p
+.rw-r--r--  0 nixbld  1 Jan  1970 q
+drwxr-xr-x  - nixbld  1 Jan  1970 vagrant

+ 1 - 1
tests/cmd/long_icons_nix.toml

@@ -1,2 +1,2 @@
 bin.name = "eza"
-args = "tests/itest --long --icons"
+args = "tests/itest --long --icons=auto"