From 46d7bc57c3ca7982625246eb01ae1027665ae236 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 25 Jun 2023 16:18:31 +0200 Subject: [PATCH 1/3] add indicator style --- CHANGELOG.md | 1 + README.md | 7 +- doc/lsd.md | 11 ++- src/app.rs | 12 ++++ src/config_file.rs | 8 +++ src/flags.rs | 40 +++++++++-- src/flags/indicator_style.rs | 132 +++++++++++++++++++++++++++++++++++ src/meta/indicator.rs | 46 ++++++++++-- 8 files changed, 245 insertions(+), 12 deletions(-) create mode 100644 src/flags/indicator_style.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index fc4485338..811d2f174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add many icons from https://github.com/Peltoche/lsd/issues/764 [@TruncatedDinosour](https://ari-web.xyz/gh) - Add support for localization from [scarf](https://github.com/scarf005) - Add icons for cjs, cts and mts from [Han Yeong-woo](https://github.com/nix6839) +- Add indicator-style - Fix obsolete Nerd Font icons from [Han Yeong-woo](https://github.com/nix6839) ### Fixed diff --git a/README.md b/README.md index 9ab912824..90ae0d203 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,11 @@ icons: # Possible values: false, true indicators: false +# == Indicator Style == +# Specifies which indicators style to use +# Possible values: none, slash, file-type, classify +indicator-style: classify + # == Layout == # Which layout to use. "oneline" might be a bit confusing here and should be # called "one-per-line". It might be changed in the future. @@ -244,7 +249,7 @@ The valid theme configurations are: - `custom`: use a custom color scheme defined in `colors.yaml` - *(deprecated) theme_file_name(yaml): use the theme file to specify colors(without the `yaml` extension)* -When set to `custom`, `lsd` will look for `colors.yaml` in the +When set to `custom`, `lsd` will look for `colors.yaml` in the XDG Base Directory, e.g. ~/.config/lsd/colors.yaml When configured with the `theme-file-name` which is a `yaml` file, diff --git a/doc/lsd.md b/doc/lsd.md index 0adec7b16..a5975168f 100644 --- a/doc/lsd.md +++ b/doc/lsd.md @@ -53,9 +53,18 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich `--config-file ` : Provide the config file from a custom location -`-F`, `--classify` +`--indicator-style ` +: Append indicator with style WORD to entry names: none (default), slash (-p), file-type (--file-type), classify (-F) + +`-F`, `--classify`, `--indicator-style=classify` : Append indicator (one of \*/=>@|) at the end of the file names +`--file-type`, `--indicator-style=file-type` +: likewise, except do not append '*' + +`-p`, `--indicator-style=slash` +: Append / indicator to directories + `-i`, `--inode` : Display the index number of each file diff --git a/src/app.rs b/src/app.rs index bd6defaeb..b159d45c8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -28,10 +28,22 @@ pub struct Cli { #[arg(long, value_name = "THEME", value_parser = ["fancy", "unicode"])] pub icon_theme: Option, + /// Choose indicator style + #[arg(long, value_name = "WORD", value_parser = ["none", "slash", "file-type", "classify"])] + pub indicator_style: Option, + /// Append indicator (one of */=>@|) at the end of the file names #[arg(short = 'F', long = "classify")] pub indicators: bool, + /// Likewise, except do not append '*' + #[arg(long)] + pub file_type: bool, + + /// Append / indicator to directories + #[arg(short = 'p')] + pub slash: bool, + /// Display extended file metadata as a table #[arg(short, long)] pub long: bool, diff --git a/src/config_file.rs b/src/config_file.rs index c9392c838..7f512d0d6 100644 --- a/src/config_file.rs +++ b/src/config_file.rs @@ -35,6 +35,7 @@ pub struct Config { pub icons: Option, pub ignore_globs: Option>, pub indicators: Option, + pub indicator_style: Option, pub layout: Option, pub recursion: Option, pub size: Option, @@ -87,6 +88,7 @@ impl Config { icons: None, ignore_globs: None, indicators: None, + indicator_style: None, layout: None, recursion: None, size: None, @@ -267,6 +269,11 @@ icons: # Possible values: false, true indicators: false +# == Indicator Style == +# Specifies which indicators style to use +# Possible values: none, slash, file-type, classify +indicator-style: classify + # == Layout == # Which layout to use. "oneline" might be a bit confusing here and should be # called "one-per-line". It might be changed in the future. @@ -372,6 +379,7 @@ mod tests { }), ignore_globs: None, indicators: Some(false), + indicator_style: Some("classify".to_string()), layout: Some(Layout::Grid), recursion: Some(config_file::Recursion { enabled: Some(false), diff --git a/src/flags.rs b/src/flags.rs index a73ac6534..816eeee67 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -7,6 +7,7 @@ pub mod header; pub mod hyperlink; pub mod icons; pub mod ignore_globs; +pub mod indicator_style; pub mod indicators; pub mod layout; pub mod permission; @@ -30,6 +31,7 @@ pub use icons::IconSeparator; pub use icons::IconTheme; pub use icons::Icons; pub use ignore_globs::IgnoreGlobs; +pub use indicator_style::IndicatorStyle; pub use indicators::Indicators; pub use layout::Layout; pub use permission::PermissionFlag; @@ -60,6 +62,7 @@ pub struct Flags { pub dereference: Dereference, pub display: Display, pub display_indicators: Indicators, + pub indicator_style: IndicatorStyle, pub icons: Icons, pub ignore_globs: IgnoreGlobs, pub layout: Layout, @@ -83,6 +86,18 @@ impl Flags { /// This can return an [Error], when either the building of the ignore globs or the parsing of /// the recursion depth parameter fails. pub fn configure_from(cli: &Cli, config: &Config) -> Result { + let mut display_indicators = Indicators::configure_from_option(cli, config); + let mut indicator_style = IndicatorStyle::configure_from_option(cli, config); + if cli.file_type { + indicator_style = Some(IndicatorStyle::FileType); + } else if cli.slash { + indicator_style = Some(IndicatorStyle::Slash); + } + if display_indicators.is_none() && indicator_style.is_some() { + display_indicators = Some(Indicators(true)); + } + let display_indicators = display_indicators.unwrap_or_default(); + let indicator_style = indicator_style.unwrap_or_default(); Ok(Self { blocks: Blocks::configure_from(cli, config), color: Color::configure_from(cli, config), @@ -92,7 +107,8 @@ impl Flags { layout: Layout::configure_from(cli, config), size: SizeFlag::configure_from(cli, config), permission: PermissionFlag::configure_from(cli, config), - display_indicators: Indicators::configure_from(cli, config), + display_indicators, + indicator_style, icons: Icons::configure_from(cli, config), ignore_globs: IgnoreGlobs::configure_from(cli, config)?, no_symlink: NoSymlink::configure_from(cli, config), @@ -125,19 +141,33 @@ where /// The configuration file's Yaml is read in any case, to be able to check for errors and print /// out warnings. fn configure_from(cli: &Cli, config: &Config) -> T { + Self::configure_from_option(cli, config).unwrap_or_default() + } + + /// Returns an optional value from either [Cli], a [Config] or the environment value. + /// The first value that is not [None] is used. The order of precedence for the value used is: + /// - [from_cli](Configurable::from_cli) + /// - [from_environment](Configurable::from_environment) + /// - [from_config](Configurable::from_config) + /// + /// # Note + /// + /// The configuration file's Yaml is read in any case, to be able to check for errors and print + /// out warnings. + fn configure_from_option(cli: &Cli, config: &Config) -> Option { if let Some(value) = Self::from_cli(cli) { - return value; + return Some(value); } if let Some(value) = Self::from_environment() { - return value; + return Some(value); } if let Some(value) = Self::from_config(config) { - return value; + return Some(value); } - Default::default() + None } /// The method to implement the value fetching from command line parameters. diff --git a/src/flags/indicator_style.rs b/src/flags/indicator_style.rs new file mode 100644 index 000000000..be7b3c611 --- /dev/null +++ b/src/flags/indicator_style.rs @@ -0,0 +1,132 @@ +//! This module defines the [IndicatorStyle] flag. To set it up from [Cli], a [Config] and its +//! [Default] value, use the [configure_from](Configurable::configure_from) method. + +use super::Configurable; + +use crate::app::Cli; +use crate::config_file::Config; + +/// The option to choose which indicator to print +#[derive(Clone, Debug, Copy, PartialEq, Eq)] +pub enum IndicatorStyle { + None, + Slash, + FileType, + Classify, +} + +impl std::convert::From<&str> for IndicatorStyle { + fn from(input: &str) -> Self { + match input { + "none" => Self::None, + "slash" => Self::Slash, + "file-type" => Self::FileType, + _ => Self::Classify, + } + } +} + +impl std::default::Default for IndicatorStyle { + fn default() -> Self { + Self::Classify + } +} + +impl Configurable for IndicatorStyle { + /// Get a potential `IndicatorStyle` value from [Cli]. + /// + /// If the "indicator_style" argument is passed, this returns an `IndicatorStyle` with its value + /// in a [Some]. Otherwise this returns [None]. + fn from_cli(cli: &Cli) -> Option { + cli.indicator_style + .as_ref() + .map(|indicator_style| Self::from(indicator_style.as_str())) + } + + /// Get a potential `IndicatorStyle` value from a [Config]. + /// + /// If the `Config::indicator_style` has value, + /// this returns its value as the value of the `IndicatorStyle`, in a [Some]. + /// Otherwise this returns [None]. + fn from_config(config: &Config) -> Option { + config + .indicator_style + .clone() + .map(|v| Self::from(v.as_str())) + } +} + +#[cfg(test)] +mod test { + use clap::Parser; + + use super::IndicatorStyle; + + use crate::app::Cli; + use crate::config_file::Config; + use crate::flags::{Configurable, Flags}; + + #[test] + fn test_from_cli_empty() { + let argv = ["lsd"]; + let cli = Cli::try_parse_from(argv).unwrap(); + assert_eq!(None, IndicatorStyle::from_cli(&cli)); + } + + #[test] + fn test_from_cli_none() { + let argv = ["lsd", "--indicator-style=none"]; + let cli = Cli::try_parse_from(argv).unwrap(); + assert_eq!(Some(IndicatorStyle::None), IndicatorStyle::from_cli(&cli)); + } + + #[test] + fn test_from_cli_slash() { + let argv = ["lsd", "--indicator-style=slash"]; + let cli = Cli::try_parse_from(argv).unwrap(); + assert_eq!(Some(IndicatorStyle::Slash), IndicatorStyle::from_cli(&cli)); + } + + #[test] + fn test_from_cli_p() { + let argv = ["lsd", "-p"]; + let cli = Cli::try_parse_from(argv).unwrap(); + let flags = Flags::configure_from(&cli, &Config::default()); + assert_eq!(IndicatorStyle::Slash, flags.unwrap().indicator_style); + } + + #[test] + fn test_from_cli_no_p() { + let argv = ["lsd"]; + let cli = Cli::try_parse_from(argv).unwrap(); + let flags = Flags::configure_from(&cli, &Config::default()); + assert_eq!(IndicatorStyle::Classify, flags.unwrap().indicator_style); + } + + #[test] + fn test_from_cli_file_style() { + let argv = ["lsd", "--file-type"]; + let cli = Cli::try_parse_from(argv).unwrap(); + let flags = Flags::configure_from(&cli, &Config::default()); + assert_eq!(IndicatorStyle::FileType, flags.unwrap().indicator_style); + } + + #[test] + fn test_from_config_empty() { + assert_eq!(None, IndicatorStyle::from_config(&Config::with_none())); + } + + #[test] + fn test_from_config_none() { + let mut c = Config::with_none(); + c.indicator_style = Some("none".to_string()); + assert_eq!(Some(IndicatorStyle::None), IndicatorStyle::from_config(&c)); + } + + #[test] + fn test_from_config_slash() { + let mut c = Config::with_none(); + c.indicator_style = Some("slash".to_string()); + assert_eq!(Some(IndicatorStyle::Slash), IndicatorStyle::from_config(&c)); + } +} diff --git a/src/meta/indicator.rs b/src/meta/indicator.rs index c81758b99..4e8811c4b 100644 --- a/src/meta/indicator.rs +++ b/src/meta/indicator.rs @@ -1,5 +1,6 @@ use crate::color::{ColoredString, Colors}; use crate::flags::Flags; +use crate::flags::IndicatorStyle; use crate::meta::FileType; #[derive(Clone, Debug)] @@ -22,18 +23,24 @@ impl From for Indicator { impl Indicator { pub fn render(&self, flags: &Flags) -> ColoredString { - if flags.display_indicators.0 { - ColoredString::new(Colors::default_style(), self.0.to_string()) + let ind = if flags.display_indicators.0 { + match (flags.indicator_style, self.0) { + (IndicatorStyle::Classify, c) => c, + (IndicatorStyle::FileType, c) if c != "*" => c, + (IndicatorStyle::Slash, c) if c == "/" => c, + _ => "", + } } else { - ColoredString::new(Colors::default_style(), "".into()) - } + "" + }; + ColoredString::new(Colors::default_style(), ind.into()) } } #[cfg(test)] mod test { use super::Indicator; - use crate::flags::{Flags, Indicators}; + use crate::flags::{Flags, IndicatorStyle, Indicators}; use crate::meta::FileType; #[test] @@ -63,6 +70,22 @@ mod test { assert_eq!("*", file_type.render(&flags).to_string()); } + #[test] + fn test_executable_file_indicator_slash() { + let flags = Flags { + display_indicators: Indicators(true), + indicator_style: IndicatorStyle::Slash, + ..Default::default() + }; + + let file_type = Indicator::from(FileType::File { + uid: false, + exec: true, + }); + + assert_eq!("", file_type.render(&flags).to_string()); + } + #[test] fn test_socket_indicator() { let flags = Flags { @@ -75,6 +98,19 @@ mod test { assert_eq!("=", file_type.render(&flags).to_string()); } + #[test] + fn test_socket_indicator_slash() { + let flags = Flags { + display_indicators: Indicators(true), + indicator_style: IndicatorStyle::Slash, + ..Default::default() + }; + + let file_type = Indicator::from(FileType::Socket); + + assert_eq!("", file_type.render(&flags).to_string()); + } + #[test] fn test_symlink_indicator() { let flags = Flags { From 5f8cdd9b5e1c5ede812eb2654b9b8dacfcf9ed78 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 25 Jun 2023 17:39:49 +0200 Subject: [PATCH 2/3] indicator style: conflicts with each others --- src/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.rs b/src/app.rs index b159d45c8..857ad838b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -41,7 +41,7 @@ pub struct Cli { pub file_type: bool, /// Append / indicator to directories - #[arg(short = 'p')] + #[arg(short = 'p', conflicts_with_all = ["file_type", "indicators"])] pub slash: bool, /// Display extended file metadata as a table From c0e07a1813a78128b3dbf69f38fc874a5a86b0e8 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Sun, 25 Jun 2023 17:52:46 +0200 Subject: [PATCH 3/3] fix CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 811d2f174..b6726a786 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add many icons from https://github.com/Peltoche/lsd/issues/764 [@TruncatedDinosour](https://ari-web.xyz/gh) - Add support for localization from [scarf](https://github.com/scarf005) - Add icons for cjs, cts and mts from [Han Yeong-woo](https://github.com/nix6839) -- Add indicator-style +- Add indicator-style from [nim65s](https://github.com/nim65s) - Fix obsolete Nerd Font icons from [Han Yeong-woo](https://github.com/nix6839) ### Fixed