From 8ce14a7c86266e4408c3b109bbddf480c342eff1 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:51:30 -0600 Subject: [PATCH] replace icons in grid with devicons + color (#14827) # Description This PR replaces the home-grown icons in the `grid` command with the `devicons` crate. ### Before ![image](https://github.com/user-attachments/assets/05e8de84-1655-45b9-ab88-40b8faa0d950) ### After ![image](https://github.com/user-attachments/assets/2134e92d-fba8-41f7-a630-fd83c0a9449c) # User-Facing Changes # Tests + Formatting # After Submitting --- Cargo.lock | 10 + Cargo.toml | 1 + crates/nu-command/Cargo.toml | 1 + crates/nu-command/src/viewers/griddle.rs | 18 +- crates/nu-command/src/viewers/icons.rs | 649 ----------------------- crates/nu-command/src/viewers/mod.rs | 1 - 6 files changed, 19 insertions(+), 661 deletions(-) delete mode 100644 crates/nu-command/src/viewers/icons.rs diff --git a/Cargo.lock b/Cargo.lock index 7247fa23b7..8169e0b30b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1598,6 +1598,15 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "devicons" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830e47e2f330cf4fdd5a958dcef921b9523ffc21ab6713aa5e77ba2cce03904b" +dependencies = [ + "lazy_static", +] + [[package]] name = "dialoguer" version = "0.11.0" @@ -3775,6 +3784,7 @@ dependencies = [ "crossterm 0.28.1", "csv", "data-encoding", + "devicons", "dialoguer", "digest", "dirs", diff --git a/Cargo.toml b/Cargo.toml index db3edcd49d..604d2d2260 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,7 @@ crossbeam-channel = "0.5.8" crossterm = "0.28.1" csv = "1.3" ctrlc = "3.4" +devicons = "0.6.12" dialoguer = { default-features = false, version = "0.11" } digest = { default-features = false, version = "0.10" } dirs = "5.0" diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index d00ea542aa..c630e5e5fa 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -45,6 +45,7 @@ chrono-humanize = { workspace = true } chrono-tz = { workspace = true } crossterm = { workspace = true, optional = true } csv = { workspace = true } +devicons = { workspace = true } dialoguer = { workspace = true, default-features = false, features = ["fuzzy-select"] } digest = { workspace = true, default-features = false } dtparse = { workspace = true } diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index 336026fa38..56c1cb1132 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -1,6 +1,6 @@ -// use super::icons::{icon_for_file, iconify_style_ansi_to_nu}; -use super::icons::icon_for_file; +use devicons::icon_for_file; use lscolors::Style; +use nu_color_config::lookup_ansi_color_style; use nu_engine::{command_prelude::*, env_to_string}; use nu_protocol::Config; use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions}; @@ -214,13 +214,9 @@ fn create_grid_output( if icons_param { let no_ansi = nu_utils::strip_ansi_unlikely(&value); let path = cwd.join(no_ansi.as_ref()); - let icon = icon_for_file(&path, call.head)?; + let file_icon = icon_for_file(&path, &None); let ls_colors_style = ls_colors.style_for_path(path); - - let icon_style = match ls_colors_style { - Some(c) => c.to_nu_ansi_term_style(), - None => nu_ansi_term::Style::default(), - }; + let icon_style = lookup_ansi_color_style(file_icon.color); let ansi_style = ls_colors_style .map(Style::to_nu_ansi_term_style) @@ -228,7 +224,7 @@ fn create_grid_output( let item = format!( "{} {}", - icon_style.paint(String::from(icon)), + icon_style.paint(String::from(file_icon.icon)), ansi_style.paint(value) ); @@ -247,8 +243,8 @@ fn create_grid_output( } else if icons_param { let no_ansi = nu_utils::strip_ansi_unlikely(&value); let path = cwd.join(no_ansi.as_ref()); - let icon = icon_for_file(&path, call.head)?; - let item = format!("{} {}", String::from(icon), value); + let file_icon = icon_for_file(&path, &None); + let item = format!("{} {}", String::from(file_icon.icon), value); let mut cell = Cell::from(item); cell.alignment = Alignment::Left; grid.add(cell); diff --git a/crates/nu-command/src/viewers/icons.rs b/crates/nu-command/src/viewers/icons.rs deleted file mode 100644 index 5ab11ed213..0000000000 --- a/crates/nu-command/src/viewers/icons.rs +++ /dev/null @@ -1,649 +0,0 @@ -use nu_protocol::{ShellError, Span}; -use std::sync::LazyLock; -use std::{collections::HashMap, path::Path}; - -// Attribution: Thanks exa. Most of this file is taken from around here -// https://github.com/ogham/exa/blob/dbd11d38042284cc890fdd91760c2f93b65e8553/src/output/icons.rs - -pub trait FileIcon { - fn icon_file(&self, file: &Path) -> Option; -} - -#[derive(Copy, Clone)] -pub enum Icons { - Audio, - Image, - Video, -} - -impl Icons { - pub fn value(self) -> char { - match self { - Self::Audio => '\u{f001}', - Self::Image => '\u{f1c5}', - Self::Video => '\u{f03d}', - } - } -} - -// keeping this for now in case we have to revert to ansi style instead of crossterm style -// Helper function to convert ansi_term style to nu_ansi_term. unfortunately -// this is necessary because ls_colors has a dependency on ansi_term vs nu_ansi_term -// double unfortunately, now we have a dependency on both. we may have to bring -// in ls_colors crate to nushell -// pub fn iconify_style_ansi_to_nu<'a>(style: ansi_term::Style) -> nu_ansi_term::Style { -// let bg = match style.background { -// Some(c) => match c { -// ansi_term::Color::Black => Some(nu_ansi_term::Color::Black), -// ansi_term::Color::Red => Some(nu_ansi_term::Color::Red), -// ansi_term::Color::Green => Some(nu_ansi_term::Color::Green), -// ansi_term::Color::Yellow => Some(nu_ansi_term::Color::Yellow), -// ansi_term::Color::Blue => Some(nu_ansi_term::Color::Blue), -// ansi_term::Color::Purple => Some(nu_ansi_term::Color::Purple), -// ansi_term::Color::Cyan => Some(nu_ansi_term::Color::Cyan), -// ansi_term::Color::White => Some(nu_ansi_term::Color::White), -// ansi_term::Color::Fixed(f) => Some(nu_ansi_term::Color::Fixed(f)), -// ansi_term::Color::RGB(r, g, b) => Some(nu_ansi_term::Color::Rgb(r, g, b)), -// }, -// None => None, -// }; - -// let fg = match style.foreground { -// Some(c) => match c { -// ansi_term::Color::Black => Some(nu_ansi_term::Color::Black), -// ansi_term::Color::Red => Some(nu_ansi_term::Color::Red), -// ansi_term::Color::Green => Some(nu_ansi_term::Color::Green), -// ansi_term::Color::Yellow => Some(nu_ansi_term::Color::Yellow), -// ansi_term::Color::Blue => Some(nu_ansi_term::Color::Blue), -// ansi_term::Color::Purple => Some(nu_ansi_term::Color::Purple), -// ansi_term::Color::Cyan => Some(nu_ansi_term::Color::Cyan), -// ansi_term::Color::White => Some(nu_ansi_term::Color::White), -// ansi_term::Color::Fixed(f) => Some(nu_ansi_term::Color::Fixed(f)), -// ansi_term::Color::RGB(r, g, b) => Some(nu_ansi_term::Color::Rgb(r, g, b)), -// }, -// None => None, -// }; - -// let nu_style = nu_ansi_term::Style { -// foreground: fg, -// background: bg, -// is_blink: style.is_blink, -// is_bold: style.is_bold, -// is_dimmed: style.is_dimmed, -// is_hidden: style.is_hidden, -// is_italic: style.is_italic, -// is_underline: style.is_underline, -// is_reverse: style.is_reverse, -// is_strikethrough: style.is_strikethrough, -// }; - -// nu_style -// .background -// .or(nu_style.foreground) -// .map(nu_ansi_term::Style::from) -// .unwrap_or_default() -// } - -static MAP_BY_NAME: LazyLock> = LazyLock::new(|| { - [ - (".Trash", '\u{f1f8}'), //  - (".atom", '\u{e764}'), //  - (".bashprofile", '\u{e615}'), //  - (".bashrc", '\u{f489}'), //  - (".git", '\u{f1d3}'), //  - (".gitattributes", '\u{f1d3}'), //  - (".gitconfig", '\u{f1d3}'), //  - (".github", '\u{f408}'), //  - (".gitignore", '\u{f1d3}'), //  - (".gitmodules", '\u{f1d3}'), //  - (".rvm", '\u{e21e}'), //  - (".vimrc", '\u{e62b}'), //  - (".vscode", '\u{e70c}'), //  - (".zshrc", '\u{f489}'), //  - ("Cargo.lock", '\u{e7a8}'), //  - ("bin", '\u{e5fc}'), //  - ("config", '\u{e5fc}'), //  - ("docker-compose.yml", '\u{f308}'), //  - ("Dockerfile", '\u{f308}'), //  - ("Earthfile", '\u{f0ac}'), //  - ("ds_store", '\u{f179}'), //  - ("gitignore_global", '\u{f1d3}'), //  - ("gitlab-ci.yml", '\u{f296}'), //  - ("go.mod", '\u{e626}'), //  - ("go.sum", '\u{e626}'), //  - ("gradle", '\u{e256}'), //  - ("gradle", '\u{e70e}'), //  - ("gruntfile.coffee", '\u{e611}'), //  - ("gruntfile.js", '\u{e611}'), //  - ("gruntfile.ls", '\u{e611}'), //  - ("gulpfile.coffee", '\u{e610}'), //  - ("gulpfile.js", '\u{e610}'), //  - ("gulpfile.ls", '\u{e610}'), //  - ("hidden", '\u{f023}'), //  - ("include", '\u{e5fc}'), //  - ("lib", '\u{f121}'), //  - ("localized", '\u{f179}'), //  - ("Makefile", '\u{e779}'), //  - ("node_modules", '\u{e718}'), //  - ("npmignore", '\u{e71e}'), //  - ("rubydoc", '\u{e73b}'), //  - ("yarn.lock", '\u{e718}'), //  - ] - .into_iter() - .collect() -}); - -pub fn icon_for_file(file_path: &Path, span: Span) -> Result { - let extensions = Box::new(FileExtensions); - let fp = format!("{}", file_path.display()); - - if let Some(icon) = MAP_BY_NAME.get(&fp[..]) { - Ok(*icon) - } else if file_path.is_dir() { - let str = file_path - .file_name() - .ok_or_else(|| ShellError::GenericError { - error: "File name error".into(), - msg: "Unable to get file name".into(), - span: Some(span), - help: None, - inner: vec![], - })? - .to_str() - .ok_or_else(|| ShellError::GenericError { - error: "Unable to get str error".into(), - msg: "Unable to convert to str file name".into(), - span: Some(span), - help: None, - inner: vec![], - })?; - Ok(match str { - "bin" => '\u{e5fc}', //  - ".git" => '\u{f1d3}', //  - ".idea" => '\u{e7b5}', //  - _ => '\u{f115}', //  - }) - } else if let Some(icon) = extensions.icon_file(file_path) { - Ok(icon) - } else if let Some(ext) = file_path.extension().as_ref() { - let str = ext.to_str().ok_or_else(|| ShellError::GenericError { - error: "Unable to get str error".into(), - msg: "Unable to convert to str file name".into(), - span: Some(span), - help: None, - inner: vec![], - })?; - Ok(match str { - "a" => '\u{f17c}', //  - "acf" => '\u{f1b6}', //  - "ai" => '\u{e7b4}', //  - "android" => '\u{e70e}', //  - "apk" => '\u{e70e}', //  - "apple" => '\u{f179}', //  - "asm" => '\u{e637}', //  - "avi" => '\u{f03d}', //  - "avro" => '\u{e60b}', //  - "awk" => '\u{f489}', //  - "bash" => '\u{f489}', //  - "bash_history" => '\u{f489}', //  - "bash_profile" => '\u{f489}', //  - "bashrc" => '\u{f489}', //  - "bat" => '\u{ebc4}', //  - "bib" => '\u{e69b}', //  - "bin" => '\u{eae8}', //  - "bmp" => '\u{f1c5}', //  - "bst" => '\u{e69b}', //  - "bz" => '\u{f410}', //  - "bz2" => '\u{f410}', //  - "c" => '\u{e61e}', //  - "c++" => '\u{e61d}', //  - "cab" => '\u{e70f}', //  - "cc" => '\u{e61d}', //  - "cert" => '\u{eafa}', //  - "cfg" => '\u{e615}', //  - "cjs" => '\u{e74e}', //  - "class" => '\u{e256}', //  - "clj" => '\u{e768}', //  - "cljs" => '\u{e76a}', //  - "cls" => '\u{e69b}', //  - "cmd" => '\u{e70f}', //  - "coffee" => '\u{f0f4}', //  - "conf" => '\u{e615}', //  - "config" => '\u{e615}', //  - "cp" => '\u{e61d}', //  - "cpp" => '\u{e61d}', //  - "crt" => '\u{eafa}', //  - "cs" => '\u{f031b}', // 󰌛 - "csh" => '\u{f489}', //  - "cshtml" => '\u{f1fa}', //  - "csproj" => '\u{f031b}', // 󰌛 - "css" => '\u{e749}', //  - "csv" => '\u{f1c3}', //  - "csx" => '\u{f031b}', // 󰌛 - "cu" => '\u{e64b}', //  - "cxx" => '\u{e61d}', //  - "d" => '\u{e7af}', //  - "dart" => '\u{e798}', //  - "db" => '\u{f1c0}', //  - "deb" => '\u{e77d}', //  - "desktop" => '\u{ebd1}', //  - "diff" => '\u{f440}', //  - "djvu" => '\u{f02d}', //  - "dll" => '\u{e70f}', //  - "doc" => '\u{f1c2}', //  - "docx" => '\u{f1c2}', //  - "drawio" => '\u{ebba}', //  - "ds_store" => '\u{f179}', //  - "DS_store" => '\u{f179}', //  - "dump" => '\u{f1c0}', //  - "ebook" => '\u{e28b}', //  - "editorconfig" => '\u{e615}', //  - "ejs" => '\u{e618}', //  - "elm" => '\u{e62c}', //  - "eml" => '\u{f003}', //  - "env" => '\u{f462}', //  - "eot" => '\u{f031}', //  - "epub" => '\u{e28a}', //  - "erb" => '\u{e73b}', //  - "erl" => '\u{e7b1}', //  - "ex" => '\u{e62d}', //  - "exe" => '\u{f17a}', //  - "exs" => '\u{e62d}', //  - "fish" => '\u{f489}', //  - "flac" => '\u{f001}', //  - "flv" => '\u{f03d}', //  - "font" => '\u{f031}', //  - "gdoc" => '\u{f1c2}', //  - "gem" => '\u{e21e}', //  - "gemfile" => '\u{e21e}', //  - "gemspec" => '\u{e21e}', //  - "gform" => '\u{f298}', //  - "gif" => '\u{f1c5}', //  - "git" => '\u{f1d3}', //  - "gitattributes" => '\u{f1d3}', //  - "gitignore" => '\u{f1d3}', //  - "gitmodules" => '\u{f1d3}', //  - "go" => '\u{e626}', //  - "gradle" => '\u{e70e}', //  - "groovy" => '\u{e775}', //  - "gsheet" => '\u{f1c3}', //  - "gslides" => '\u{f1c4}', //  - "guardfile" => '\u{e21e}', //  - "gz" => '\u{f410}', //  - "h" => '\u{f0fd}', //  - "hbs" => '\u{e60f}', //  - "hpp" => '\u{f0fd}', //  - "hs" => '\u{e777}', //  - "htm" => '\u{f13b}', //  - "html" => '\u{f13b}', //  - "hxx" => '\u{f0fd}', //  - "ical" => '\u{eab0}', //  - "icalendar" => '\u{eab0}', //  - "ico" => '\u{f1c5}', //  - "image" => '\u{f1c5}', //  - "iml" => '\u{e7b5}', //  - "ini" => '\u{f17a}', //  - "ipynb" => '\u{e606}', //  - "iso" => '\u{e271}', //  - "jad" => '\u{e256}', //  - "jar" => '\u{e204}', //  - "java" => '\u{e204}', //  - "jpeg" => '\u{f1c5}', //  - "jpg" => '\u{f1c5}', //  - "js" => '\u{e74e}', //  - "json" => '\u{e60b}', //  - "jsx" => '\u{e7ba}', //  - "kdb" => '\u{f23e}', //  - "kdbx" => '\u{f23e}', //  - "key" => '\u{eb11}', //  - "ko" => '\u{f17c}', //  - "ksh" => '\u{f489}', //  - "latex" => '\u{e69b}', //  - "less" => '\u{e758}', //  - "lhs" => '\u{e777}', //  - "license" => '\u{f0fc3}', // 󰿃 - "localized" => '\u{f179}', //  - "lock" => '\u{f023}', //  - "log" => '\u{f18d}', //  - "lua" => '\u{e620}', //  - "lz" => '\u{f410}', //  - "lzh" => '\u{f410}', //  - "lzma" => '\u{f410}', //  - "lzo" => '\u{f410}', //  - "m" => '\u{e61e}', //  - "ml" => '\u{e67a}', //  - "mli" => '\u{e67a}', //  - "mll" => '\u{e67a}', //  - "mly" => '\u{e67a}', //  - "mm" => '\u{e61d}', //  - "m4a" => '\u{f001}', //  - "magnet" => '\u{f076}', //  - "markdown" => '\u{f48a}', //  - "md" => '\u{f48a}', //  - "mjs" => '\u{e74e}', //  - "mkd" => '\u{f48a}', //  - "mkv" => '\u{f03d}', //  - "mobi" => '\u{e28b}', //  - "mov" => '\u{f03d}', //  - "mp3" => '\u{f001}', //  - "mp4" => '\u{f03d}', //  - "msi" => '\u{e70f}', //  - "mustache" => '\u{e60f}', //  - "nix" => '\u{f313}', //  - "node" => '\u{f0399}', // 󰎙 - "npmignore" => '\u{e71e}', //  - "o" => '\u{eae8}', //  - "odp" => '\u{f1c4}', //  - "ods" => '\u{f1c3}', //  - "odt" => '\u{f1c2}', //  - "ogg" => '\u{f001}', //  - "ogv" => '\u{f03d}', //  - "otf" => '\u{f031}', //  - "out" => '\u{eb2c}', //  - "patch" => '\u{f440}', //  - "pdf" => '\u{f1c1}', //  - "pem" => '\u{eb11}', //  - "php" => '\u{e73d}', //  - "pl" => '\u{e769}', //  - "png" => '\u{f1c5}', //  - "ppt" => '\u{f1c4}', //  - "pptx" => '\u{f1c4}', //  - "procfile" => '\u{e21e}', //  - "properties" => '\u{e60b}', //  - "ps1" => '\u{ebc7}', //  - "psd" => '\u{e7b8}', //  - "psd1" => '\u{ebc7}', //  - "psm1" => '\u{ebc7}', //  - "pxm" => '\u{f1c5}', //  - "py" => '\u{e606}', //  - "pyc" => '\u{e606}', //  - "qcow2" => '\u{e271}', //  - "r" => '\u{f25d}', //  - "rakefile" => '\u{e21e}', //  - "rar" => '\u{f410}', //  - "razor" => '\u{f1fa}', //  - "rb" => '\u{e21e}', //  - "rdata" => '\u{f25d}', //  - "rdb" => '\u{e76d}', //  - "rdoc" => '\u{f48a}', //  - "rds" => '\u{f25d}', //  - "readme" => '\u{f48a}', //  - "rlib" => '\u{e7a8}', //  - "rmd" => '\u{f48a}', //  - "rpm" => '\u{e7bb}', //  - "rs" => '\u{e7a8}', //  - "rspec" => '\u{e21e}', //  - "rspec_parallel" => '\u{e21e}', //  - "rspec_status" => '\u{e21e}', //  - "rss" => '\u{f09e}', //  - "rtf" => '\u{f0219}', // 󰈙 - "ru" => '\u{e21e}', //  - "rubydoc" => '\u{e73b}', //  - "s" => '\u{e637}', //  - "sass" => '\u{e603}', //  - "scala" => '\u{e737}', //  - "scss" => '\u{e749}', //  - "service" => '\u{eba2}', //  - "sh" => '\u{f489}', //  - "shell" => '\u{f489}', //  - "slim" => '\u{e73b}', //  - "sln" => '\u{e70c}', //  - "so" => '\u{f17c}', //  - "sql" => '\u{f1c0}', //  - "sqlite3" => '\u{e7c4}', //  - "sty" => '\u{e69b}', //  - "styl" => '\u{e600}', //  - "stylus" => '\u{e600}', //  - "svg" => '\u{f1c5}', //  - "swift" => '\u{e755}', //  - "tar" => '\u{f410}', //  - "taz" => '\u{f410}', //  - "tbz" => '\u{f410}', //  - "tbz2" => '\u{f410}', //  - "tex" => '\u{e69b}', //  - "tiff" => '\u{f1c5}', //  - "toml" => '\u{e615}', //  - "ts" => '\u{e628}', //  - "tsv" => '\u{f1c3}', //  - "tsx" => '\u{e7ba}', //  - "ttf" => '\u{f031}', //  - "twig" => '\u{e61c}', //  - "txt" => '\u{f15c}', //  - "tz" => '\u{f410}', //  - "tzo" => '\u{f410}', //  - "unity" => '\u{e721}', //  - "unity3d" => '\u{e721}', //  - "vdi" => '\u{e271}', //  - "vhd" => '\u{e271}', //  - "video" => '\u{f03d}', //  - "vim" => '\u{e62b}', //  - "vmdk" => '\u{e271}', //  - "vue" => '\u{f0844}', // 󰡄 - "war" => '\u{e256}', //  - "wav" => '\u{f001}', //  - "webm" => '\u{f03d}', //  - "webp" => '\u{f1c5}', //  - "windows" => '\u{f17a}', //  - "woff" => '\u{f031}', //  - "woff2" => '\u{f031}', //  - "xhtml" => '\u{f13b}', //  - "xls" => '\u{f1c3}', //  - "xlsm" => '\u{f1c3}', //  - "xlsx" => '\u{f1c3}', //  - "xml" => '\u{f05c0}', // 󰗀 - "xul" => '\u{f05c0}', // 󰗀 - "xz" => '\u{f410}', //  - "yaml" => '\u{f481}', //  - "yml" => '\u{f481}', //  - "zip" => '\u{f410}', //  - "zsh" => '\u{f489}', //  - "zsh-theme" => '\u{f489}', //  - "zshrc" => '\u{f489}', //  - "7z" => '\u{f410}', //  - _ => '\u{f15b}', //  - }) - } else { - Ok('\u{f016}') - } -} - -/// Whether this file’s extension is any of the strings that get passed in. -/// -/// This will always return `false` if the file has no extension. -pub fn extension_is_one_of(path: &Path, choices: &[&str]) -> bool { - match path.extension() { - Some(os_ext) => match os_ext.to_str() { - Some(ext) => choices.contains(&ext), - None => false, - }, - None => false, - } -} - -/// Whether this file’s name, including extension, is any of the strings -/// that get passed in. -// pub fn name_is_one_of(name: &str, choices: &[&str]) -> bool { -// choices.contains(&&name[..]) -// } - -#[derive(Debug, Default, PartialEq, Eq)] -pub struct FileExtensions; - -// TODO: We may want to re-add these FileExtensions impl fns back. I have disabled -// it now because it's hard coding colors which kind of defeats the LS_COLORS -// functionality. We may want to enable and augment at some point. - -impl FileExtensions { - // /// An “immediate” 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. - // #[allow(clippy::case_sensitive_file_extension_comparisons)] - // #[allow(dead_code)] - // fn is_immediate(&self, file_path: &Path) -> bool { - // file_path - // .file_name() - // .unwrap() - // .to_str() - // .unwrap() - // .to_lowercase() - // .starts_with("readme") - // || file_path - // .file_name() - // .unwrap() - // .to_str() - // .unwrap() - // .ends_with(".ninja") - // || name_is_one_of( - // file_path.file_name().unwrap().to_str().unwrap(), - // &[ - // "Makefile", - // "Cargo.toml", - // "SConstruct", - // "CMakeLists.txt", - // "build.gradle", - // "pom.xml", - // "Rakefile", - // "package.json", - // "Gruntfile.js", - // "Gruntfile.coffee", - // "BUILD", - // "BUILD.bazel", - // "WORKSPACE", - // "build.xml", - // "Podfile", - // "webpack.config.js", - // "meson.build", - // "composer.json", - // "RoboFile.php", - // "PKGBUILD", - // "Justfile", - // "Procfile", - // "Dockerfile", - // "Containerfile", - // "Vagrantfile", - // "Brewfile", - // "Gemfile", - // "Pipfile", - // "build.sbt", - // "mix.exs", - // "bsconfig.json", - // "tsconfig.json", - // ], - // ) - // } - - fn is_image(&self, file: &Path) -> bool { - extension_is_one_of( - file, - &[ - "png", "jfi", "jfif", "jif", "jpe", "jpeg", "jpg", "gif", "bmp", "tiff", "tif", - "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw", "svg", "stl", "eps", "dvi", "ps", - "cbr", "jpf", "cbz", "xpm", "ico", "cr2", "orf", "nef", "heif", "avif", "jxl", - ], - ) - } - - fn is_video(&self, file: &Path) -> bool { - extension_is_one_of( - file, - &[ - "avi", "flv", "m2v", "m4v", "mkv", "mov", "mp4", "mpeg", "mpg", "ogm", "ogv", - "vob", "wmv", "webm", "m2ts", "heic", - ], - ) - } - - fn is_music(&self, file: &Path) -> bool { - extension_is_one_of(file, &["aac", "m4a", "mp3", "ogg", "wma", "mka", "opus"]) - } - - // Lossless music, rather than any other kind of data... - fn is_lossless(&self, file: &Path) -> bool { - extension_is_one_of(file, &["alac", "ape", "flac", "wav"]) - } - - // #[allow(dead_code)] - // fn is_crypto(&self, file: &Path) -> bool { - // extension_is_one_of( - // file, - // &["asc", "enc", "gpg", "pgp", "sig", "signature", "pfx", "p12"], - // ) - // } - - // #[allow(dead_code)] - // fn is_document(&self, file: &Path) -> bool { - // extension_is_one_of( - // file, - // &[ - // "djvu", "doc", "docx", "dvi", "eml", "eps", "fotd", "key", "keynote", "numbers", - // "odp", "odt", "pages", "pdf", "ppt", "pptx", "rtf", "xls", "xlsx", - // ], - // ) - // } - - // #[allow(dead_code)] - // fn is_compressed(&self, file: &Path) -> bool { - // extension_is_one_of( - // file, - // &[ - // "zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z", "iso", "dmg", "tc", "rar", - // "par", "tgz", "xz", "txz", "lz", "tlz", "lzma", "deb", "rpm", "zst", "lz4", - // ], - // ) - // } - - // #[allow(dead_code)] - // fn is_temp(&self, file: &Path) -> bool { - // file.file_name().unwrap().to_str().unwrap().ends_with('~') - // || (file.file_name().unwrap().to_str().unwrap().starts_with('#') - // && file.file_name().unwrap().to_str().unwrap().ends_with('#')) - // || extension_is_one_of(file, &["tmp", "swp", "swo", "swn", "bak", "bkp", "bk"]) - // } - - // #[allow(dead_code)] - // fn is_compiled(&self, file: &Path) -> bool { - // if extension_is_one_of(file, &["class", "elc", "hi", "o", "pyc", "zwc", "ko"]) { - // true - // // } else if let Some(dir) = file.parent() { - // // file.get_source_files() - // // .iter() - // // .any(|path| dir.contains(path)) - // } else { - // false - // } - // } - // } - - // impl FileColours for FileExtensions { - // fn colour_file(&self, file: &Path) -> Option