diff options
-rw-r--r-- | src/file/mod.rs | 16 | ||||
-rw-r--r-- | src/file/tree/filter.rs | 5 | ||||
-rw-r--r-- | src/icon.rs | 331 | ||||
-rw-r--r-- | src/main.rs | 3 | ||||
-rw-r--r-- | src/render/row/mod.rs | 92 | ||||
-rw-r--r-- | src/user/mod.rs | 4 |
6 files changed, 432 insertions, 19 deletions
diff --git a/src/file/mod.rs b/src/file/mod.rs index 3f34397..0bd1ec7 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -1,9 +1,6 @@ use crate::{ - disk, - user::{ - args::{Metric, TimeFormat, TimeStamp}, - Context, - }, + disk, icon, + user::{args::Metric, Context}, }; use ignore::DirEntry; use std::{ @@ -15,7 +12,10 @@ use std::{ }; #[cfg(unix)] -use crate::file::unix::permissions::{file_type::FileType, SymbolicNotation}; +use crate::{ + file::unix::permissions::{file_type::FileType, SymbolicNotation}, + user::args::{TimeFormat, TimeStamp}, +}; /// Concerned with querying information about a file's underlying inode. pub mod inode; @@ -160,6 +160,10 @@ impl File { } } + pub fn icon(&self) -> &str { + icon::compute(self) + } + #[cfg(unix)] pub fn unix_attrs(&self) -> &unix::Attrs { &self.unix_attrs diff --git a/src/file/tree/filter.rs b/src/file/tree/filter.rs index 0410159..8adac89 100644 --- a/src/file/tree/filter.rs +++ b/src/file/tree/filter.rs @@ -56,7 +56,10 @@ impl Tree { let mut to_remove = vec![]; for n in self.root_id.descendants(&self.arena).skip(1) { - if !n.is_removed(&self.arena) && self.arena[n].get().is_dir() && n.children(&self.arena).count() == 0 { + if !n.is_removed(&self.arena) + && self.arena[n].get().is_dir() + && n.children(&self.arena).count() == 0 + { to_remove.push(n); } } diff --git a/src/icon.rs b/src/icon.rs new file mode 100644 index 0000000..fa52eb6 --- /dev/null +++ b/src/icon.rs @@ -0,0 +1,331 @@ +use crate::file::File; +use ahash::HashMap; +use once_cell::sync::Lazy; +use std::ffi::OsString; + +/// The precedent from highest to lowest in terms of which parameters determine the icon used +/// is as followed: file-type, file-extension, and then file-name. If an icon cannot be +/// computed the fall-back default icon is used. +pub fn compute(file: &File) -> &str { + from_file_type(file) + .or_else(|| from_ext(file)) + .or_else(|| from_file_name(file)) + .unwrap_or(DEFAULT_ICON) +} + +fn from_file_type(file: &File) -> Option<&str> { + if file.is_dir() { + FILE_TYPE_ICON_MAP.get("dir").copied() + } else if file.is_symlink() { + FILE_TYPE_ICON_MAP.get("symlink").copied() + } else { + None + } +} + +fn from_ext(file: &File) -> Option<&str> { + file.path() + .extension() + .and_then(|ext| EXT_ICON_MAP.get(ext).copied()) +} + +fn from_file_name(file: &File) -> Option<&str> { + FILE_NAME_ICON_MAP.get(file.file_name()).copied() +} + +/// Ruby-like way to crate a hashmap. +macro_rules! hash { + ( $( $( $k:literal)|* => $v:expr ),* ) => { + { + let mut hash = HashMap::default(); + $( + $( hash.insert($k, $v); )* + )* + hash + } + }; + ( $( $k:expr => $v:expr ),* ) => { + { + let mut hash = HashMap::default(); + $( hash.insert($k, $v); )* + hash + } + }; +} + +/// Default fallback icon. +const DEFAULT_ICON: &str = "\u{f15b}"; + +/// Lazily evaluated static hash-map of special file-types and their corresponding styled icons. +/// These icons will take on the color properties of their associated file which is based on +/// `LS_COLORS`. +/// +/// Dev icons sourced from [`exa`](https://github.com/ogham/exa/blob/master/src/output/icons.rs) +static FILE_TYPE_ICON_MAP: Lazy<HashMap<&str, &str>> = Lazy::new(|| { + hash!( + "dir" => "\u{f413}", // + "symlink" => "\u{f482}" // + ) +}); + +/// Lazily evaluated static hash-map of special named and their corresponding icons. These icons +/// will take on the color properties of their associated file which is based on `LS_COLORS`. +/// +/// Dev icons sourced from [`exa`](https://github.com/ogham/exa/blob/master/src/output/icons.rs) +static FILE_NAME_ICON_MAP: Lazy<HashMap<OsString, &str>> = Lazy::new(|| { + hash!( + OsString::from(".Trash") => "\u{f1f8}", // + OsString::from(".atom") => "\u{e764}", // + OsString::from(".bashprofile") => "\u{e615}", // + OsString::from(".bashrc") => "\u{f489}", // + OsString::from(".git") => "\u{f1d3}", // + OsString::from(".gitattributes") => "\u{f1d3}", // + OsString::from(".gitconfig") => "\u{f1d3}", // + OsString::from(".github") => "\u{f408}", // + OsString::from(".gitignore") => "\u{f1d3}", // + OsString::from(".gitmodules") => "\u{f1d3}", // + OsString::from(".rvm") => "\u{e21e}", // + OsString::from(".vimrc") => "\u{e62b}", // + OsString::from(".vscode") => "\u{e70c}", // + OsString::from(".zshrc") => "\u{f489}", // + OsString::from("Cargo.lock") => "\u{e7a8}", // + OsString::from("bin") => "\u{e5fc}", // + OsString::from("config") => "\u{e5fc}", // + OsString::from("docker-compose.yml") => "\u{f308}", // + OsString::from("Dockerfile") => "\u{f308}", // + OsString::from(".DS_Store") => "\u{f179}", // + OsString::from("gitignore_global") => "\u{f1d3}", // + OsString::from("go.mod") => "\u{e626}", // + OsString::from("go.sum") => "\u{e626}", // + OsString::from("gradle") => "\u{e256}", // + OsString::from("gruntfile.coffee") => "\u{e611}", // + OsString::from("gruntfile.js") => "\u{e611}", // + OsString::from("gruntfile.ls") => "\u{e611}", // + OsString::from("gulpfile.coffee") => "\u{e610}", // + OsString::from("gulpfile.js") => "\u{e610}", // + OsString::from("gulpfile.ls") => "\u{e610}", // + OsString::from("hidden") => "\u{f023}", // + OsString::from("include") => "\u{e5fc}", // + OsString::from("lib") => "\u{f121}", // + OsString::from("license") => "\u{e60a}", // + OsString::from("LICENSE") => "\u{e60a}", // + OsString::from("licence") => "\u{e60a}", // + OsString::from("LICENCE") => "\u{e60a}", // + OsString::from("localized") => "\u{f179}", // + OsString::from("Makefile") => "\u{f489}", // + OsString::from("node_modules") => "\u{e718}", // + OsString::from("npmignore") => "\u{e71e}", // + OsString::from("PKGBUILD") => "\u{f303}", // + OsString::from("rubydoc") => "\u{e73b}", // + OsString::from("yarn.lock") => "\u{e718}" // + ) +}); + +/// Lazily evaluated static hash-map of various file extensions and their corresponding icons. The +/// key is the file extension while the associated value Unicode scalar value for the corresponding icon. +/// +/// Dev icons sourced from [`nvim-web-devicons`](https://github.com/nvim-tree/nvim-web-devicons/blob/master/lua/nvim-web-devicons.lua). +static EXT_ICON_MAP: Lazy<HashMap<OsString, &str>> = Lazy::new(|| { + hash!( + OsString::from("ai") => "\u{e7b4}", // + OsString::from("awk") => "\u{e795}", // + OsString::from("bash") => "\u{e795}", // + OsString::from("bat") => "\u{e615}", // + OsString::from("bmp") => "\u{e60d}", // + OsString::from("cbl") => "\u{2699}", // ⚙ + OsString::from("c++") => "\u{e61d}", // + OsString::from("c") => "\u{e61e}", // + OsString::from("cc") => "\u{e61d}", // + OsString::from("cfg") => "\u{e7a3}", // + OsString::from("cljc") => "\u{e768}", // + OsString::from("clj") => "\u{e768}", // + OsString::from("cljd") => "\u{e76a}", // + OsString::from("cljs") => "\u{e76a}", // + OsString::from("cmake") => "\u{e615}", // + OsString::from("cob") => "\u{2699}", // ⚙ + OsString::from("cobol") => "\u{2699}", // ⚙ + OsString::from("coffee") => "\u{e61b}", // + OsString::from("conf") => "\u{e615}", // + OsString::from("config.ru") => "\u{e791}", // + OsString::from("cp") => "\u{e61d}", // + OsString::from("cpp") => "\u{e61d}", // + OsString::from("cpy") => "\u{2699}", // ⚙ + OsString::from("cr") => "\u{e24f}", // + OsString::from("cs") => "\u{f031b}", // + OsString::from("csh") => "\u{e795}", // + OsString::from("cson") => "\u{e60b}", // + OsString::from("css") => "\u{e749}", // + OsString::from("csv") => "\u{f0219}", // + OsString::from("cxx") => "\u{e61d}", // + OsString::from("dart") => "\u{e798}", // + OsString::from("db") => "\u{e706}", // + OsString::from("d") => "\u{e7af}", // + OsString::from("desktop") => "\u{f108}", // + OsString::from("diff") => "\u{e728}", // + OsString::from("doc") => "\u{f022c}", // + OsString::from("drl") => "\u{e28c}", // + OsString::from("dropbox") => "\u{e707}", // + OsString::from("dump") => "\u{e706}", // + OsString::from("edn") => "\u{e76a}", // + OsString::from("eex") => "\u{e62d}", // + OsString::from("ejs") => "\u{e60e}", // + OsString::from("elm") => "\u{e62c}", // + OsString::from("epp") => "\u{e631}", // + OsString::from("erb") => "\u{e60e}", // + OsString::from("erl") => "\u{e7b1}", // + OsString::from("ex") => "\u{e62d}", // + OsString::from("exs") => "\u{e62d}", // + OsString::from("f#") => "\u{e7a7}", // + OsString::from("fish") => "\u{e795}", // + OsString::from("fnl") => "\u{1f31c}", // 🌜 + OsString::from("fs") => "\u{e7a7}", // + OsString::from("fsi") => "\u{e7a7}", // + OsString::from("fsscript") => "\u{e7a7}", // + OsString::from("fsx") => "\u{e7a7}", // + OsString::from("GNUmakefile") => "\u{e779}", // + OsString::from("gd") => "\u{e615}", // + OsString::from("gemspec") => "\u{e791}", // + OsString::from("gif") => "\u{e60d}", // + OsString::from("git") => "\u{e702}", // + OsString::from("glb") => "\u{f1b2}", // + OsString::from("go") => "\u{e627}", // + OsString::from("godot") => "\u{e7a3}", // + OsString::from("gql") => "\u{f20e}", // + OsString::from("graphql") => "\u{f20e}", // + OsString::from("haml") => "\u{e60e}", // + OsString::from("hbs") => "\u{e60f}", // + OsString::from("h") => "\u{f0fd}", // + OsString::from("heex") => "\u{e62d}", // + OsString::from("hh") => "\u{f0fd}", // + OsString::from("hpp") => "\u{f0fd}", // + OsString::from("hrl") => "\u{e7b1}", // + OsString::from("hs") => "\u{e61f}", // + OsString::from("htm") => "\u{e60e}", // + OsString::from("html") => "\u{e736}", // + OsString::from("hxx") => "\u{f0fd}", // + OsString::from("ico") => "\u{e60d}", // + OsString::from("import") => "\u{f0c6}", // + OsString::from("ini") => "\u{e615}", // + OsString::from("java") => "\u{e738}", // + OsString::from("jl") => "\u{e624}", // + OsString::from("jpeg") => "\u{e60d}", // + OsString::from("jpg") => "\u{e60d}", // + OsString::from("js") => "\u{e60c}", // + OsString::from("json5") => "\u{f0626}", // + OsString::from("json") => "\u{e60b}", // + OsString::from("jsx") => "\u{e625}", // + OsString::from("ksh") => "\u{e795}", // + OsString::from("kt") => "\u{e634}", // + OsString::from("kts") => "\u{e634}", // + OsString::from("leex") => "\u{e62d}", // + OsString::from("less") => "\u{e614}", // + OsString::from("lhs") => "\u{e61f}", // + OsString::from("license") => "\u{e60a}", // + OsString::from("licence") => "\u{e60a}", // + OsString::from("lock") => "\u{f13e}", // + OsString::from("log") => "\u{f0331}", // + OsString::from("lua") => "\u{e620}", // + OsString::from("luau") => "\u{e620}", // + OsString::from("makefile") => "\u{e779}", // + OsString::from("markdown") => "\u{e609}", // + OsString::from("Makefile") => "\u{e779}", // + OsString::from("material") => "\u{f0509}", // + OsString::from("md") => "\u{f48a}", // + OsString::from("mdx") => "\u{f48a}", // + OsString::from("mint") => "\u{f032a}", // + OsString::from("mjs") => "\u{e60c}", // + OsString::from("mk") => "\u{e779}", // + OsString::from("ml") => "\u{3bb}", // λ + OsString::from("mli") => "\u{3bb}", // λ + OsString::from("mo") => "\u{221e}", // ∞ + OsString::from("mustache") => "\u{e60f}", // + OsString::from("nim") => "\u{e677}", // + OsString::from("nix") => "\u{f313}", // + OsString::from("opus") => "\u{f0223}", // + OsString::from("otf") => "\u{f031}", // + OsString::from("pck") => "\u{f487}", // + OsString::from("pdf") => "\u{f0226}", // + OsString::from("php") => "\u{e608}", // + OsString::from("pl") => "\u{e769}", // + OsString::from("pm") => "\u{e769}", // + OsString::from("png") => "\u{e60d}", // + OsString::from("pp") => "\u{e631}", // + OsString::from("ppt") => "\u{f0227}", // + OsString::from("prisma") => "\u{e684}", // + OsString::from("pro") => "\u{e7a1}", // + OsString::from("ps1") => "\u{f0a0a}", // + OsString::from("psb") => "\u{e7b8}", // + OsString::from("psd1") => "\u{f0a0a}", // + OsString::from("psd") => "\u{e7b8}", // + OsString::from("psm1") => "\u{f0a0a}", // + OsString::from("pyc") => "\u{e606}", // + OsString::from("py") => "\u{e606}", // + OsString::from("pyd") => "\u{e606}", // + OsString::from("pyo") => "\u{e606}", // + OsString::from("query") => "\u{e21c}", // + OsString::from("rake") => "\u{e791}", // + OsString::from("rb") => "\u{e791}", // + OsString::from("r") => "\u{f07d4}", // + OsString::from("rlib") => "\u{e7a8}", // + OsString::from("rmd") => "\u{e609}", // + OsString::from("rproj") => "\u{f07d4}", // + OsString::from("rs") => "\u{e7a8}", // + OsString::from("rss") => "\u{e619}", // + OsString::from("sass") => "\u{e603}", // + OsString::from("sbt") => "\u{e737}", // + OsString::from("scala") => "\u{e737}", // + OsString::from("scm") => "\u{f0627}", // + OsString::from("scss") => "\u{e603}", // + OsString::from("sh") => "\u{e795}", // + OsString::from("sig") => "\u{3bb}", // λ + OsString::from("slim") => "\u{e60e}", // + OsString::from("sln") => "\u{e70c}", // + OsString::from("sml") => "\u{3bb}", // λ + OsString::from("sol") => "\u{f07bb}", // + OsString::from("sql") => "\u{e706}", // + OsString::from("sqlite3") => "\u{e706}", // + OsString::from("sqlite") => "\u{e706}", // + OsString::from("styl") => "\u{e600}", // + OsString::from("sublime") => "\u{e7aa}", // + OsString::from("suo") => "\u{e70c}", // + OsString::from("sv") => "\u{f035b}", // + OsString::from("svelte") => "\u{f260}", // + OsString::from("svg") => "\u{f0721}", // + OsString::from("svh") => "\u{f035b}", // + OsString::from("swift") => "\u{e755}", // + OsString::from("tbc") => "\u{f06d3}", // + OsString::from("t") => "\u{e769}", // + OsString::from("tcl") => "\u{f06d3}", // + OsString::from("terminal") => "\u{f489}", // + OsString::from("test.js") => "\u{e60c}", // + OsString::from("tex") => "\u{f0669}", // + OsString::from("tf") => "\u{e2a6}", // + OsString::from("tfvars") => "\u{f15b}", // + OsString::from("toml") => "\u{e615}", // + OsString::from("tres") => "\u{e706}", // + OsString::from("ts") => "\u{e628}", // + OsString::from("tscn") => "\u{f0381}", // + OsString::from("tsx") => "\u{e7ba}", // + OsString::from("twig") => "\u{e61c}", // + OsString::from("txt") => "\u{f0219}", // + OsString::from("vala") => "\u{e69e}", // + OsString::from("v") => "\u{f035b}", // + OsString::from("vh") => "\u{f035b}", // + OsString::from("vhd") => "\u{f035b}", // + OsString::from("vhdl") => "\u{f035b}", // + OsString::from("vim") => "\u{e62b}", // + OsString::from("vue") => "\u{f0844}", // + OsString::from("wasm") => "\u{e6a1}", // + OsString::from("webmanifest") => "\u{e60b}", // + OsString::from("webpack") => "\u{f072b}", // + OsString::from("webp") => "\u{e60d}", // + OsString::from("xcplayground") => "\u{e755}", // + OsString::from("xls") => "\u{f021b}", // + OsString::from("xml") => "\u{f05c0}", // + OsString::from("xul") => "\u{e745}", // + OsString::from("yaml") => "\u{e615}", // + OsString::from("yml") => "\u{e615}", // + OsString::from("zig") => "\u{f0e7}", // + OsString::from("zsh") => "\u{e795}" // + ) +}); diff --git a/src/main.rs b/src/main.rs index a7488bb..7cb402e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,9 @@ use error::prelude::*; /// Erdtree's representation of a file. mod file; +/// Concerned with file icons. +mod icon; + /// Concerned with logging throughout the application. mod logging; diff --git a/src/render/row/mod.rs b/src/render/row/mod.rs index f59fa91..870c0d1 100644 --- a/src/render/row/mod.rs +++ b/src/render/row/mod.rs @@ -23,13 +23,24 @@ pub fn formatter<'a>(buf: &'a mut String, ctx: &'a Context) -> Result<RowFormatt let base = root.ancestors().nth(1); let name = file.display_path(base); let column::Metadata { max_size_width, .. } = ctx.column_metadata; - writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{size:>max_size_width$} {prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + } })), (false, true) => Ok(Box::new(move |file, prefix| { let base = root.ancestors().nth(1); let name = file.display_path(base); - writeln!(buf, "{prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{prefix}{name}") + } })), (true, false) => Ok(Box::new(move |file, prefix| { @@ -39,14 +50,28 @@ pub fn formatter<'a>(buf: &'a mut String, ctx: &'a Context) -> Result<RowFormatt let col_widths = ctx.column_metadata; let column::Metadata { max_size_width, .. } = col_widths; let long_format = long::Format::new(file, ctx); - writeln!(buf, "{long_format} {size:>max_size_width$} {prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!( + buf, + "{long_format} {size:>max_size_width$} {prefix}{icon:<2}{name}" + ) + } else { + writeln!(buf, "{long_format} {size:>max_size_width$} {prefix}{name}") + } })), (true, true) => Ok(Box::new(move |file, prefix| { let base = root.ancestors().nth(1); let name = file.display_path(base); let long_format = long::Format::new(file, ctx); - writeln!(buf, "{long_format} {prefix}{name}") + + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{long_format} {prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{long_format} {prefix}{name}") + } })), } }, @@ -55,12 +80,22 @@ pub fn formatter<'a>(buf: &'a mut String, ctx: &'a Context) -> Result<RowFormatt let size = format!("{}", file.size()); let name = file.display_name(); let column::Metadata { max_size_width, .. } = ctx.column_metadata; - writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{size:>max_size_width$} {prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + } })), (false, true) => Ok(Box::new(|file, prefix| { let name = file.display_name(); - writeln!(buf, "{prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{prefix}{name}") + } })), (true, false) => Ok(Box::new(|file, prefix| { @@ -69,13 +104,26 @@ pub fn formatter<'a>(buf: &'a mut String, ctx: &'a Context) -> Result<RowFormatt let col_widths = ctx.column_metadata; let column::Metadata { max_size_width, .. } = col_widths; let long_format = long::Format::new(file, ctx); - writeln!(buf, "{long_format} {size:>max_size_width$} {prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!( + buf, + "{long_format} {size:>max_size_width$} {prefix}{icon:<2}{name}" + ) + } else { + writeln!(buf, "{long_format} {size:>max_size_width$} {prefix}{name}") + } })), (true, true) => Ok(Box::new(|file, prefix| { let name = file.display_name(); let long_format = long::Format::new(file, ctx); - writeln!(buf, "{long_format} {prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{long_format} {prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{long_format} {prefix}{name}") + } })), }, } @@ -91,7 +139,12 @@ pub fn formatter<'a>(buf: &'a mut String, ctx: &'a Context) -> Result<RowFormatt Ok(Box::new(move |file, prefix| { let base = root.ancestors().nth(1); let name = file.display_path(base); - writeln!(buf, "{prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{prefix}{name}") + } })) } else { Ok(Box::new(move |file, prefix| { @@ -99,7 +152,12 @@ pub fn formatter<'a>(buf: &'a mut String, ctx: &'a Context) -> Result<RowFormatt let base = root.ancestors().nth(1); let name = file.display_path(base); let column::Metadata { max_size_width, .. } = ctx.column_metadata; - writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{size:>max_size_width$} {prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + } })) } }, @@ -107,14 +165,24 @@ pub fn formatter<'a>(buf: &'a mut String, ctx: &'a Context) -> Result<RowFormatt if ctx.suppress_size { Ok(Box::new(|file, prefix| { let name = file.display_name(); - writeln!(buf, "{prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{prefix}{name}") + } })) } else { Ok(Box::new(|file, prefix| { let size = format!("{}", file.size()); let name = file.display_name(); let column::Metadata { max_size_width, .. } = ctx.column_metadata; - writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + if ctx.icons { + let icon = file.icon(); + writeln!(buf, "{size:>max_size_width$} {prefix}{icon:<2}{name}") + } else { + writeln!(buf, "{size:>max_size_width$} {prefix}{name}") + } })) } }, diff --git a/src/user/mod.rs b/src/user/mod.rs index aa4fe0d..522dac1 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -50,6 +50,10 @@ pub struct Context { #[arg(short = 'i', long)] pub gitignore: bool, + /// Display file icons + #[arg(short = 'I', long)] + pub icons: bool, + /// Ignore files that match rules in the global .gitignore file #[arg(long)] pub global_gitignore: bool, |