Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add indicator style #863

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 from [nim65s](https://github.com/nim65s)
- Fix obsolete Nerd Font icons from [Han Yeong-woo](https://github.com/nix6839)

### Fixed
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 10 additions & 1 deletion doc/lsd.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,18 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich
`--config-file <path>`
: Provide the config file from a custom location

`-F`, `--classify`
`--indicator-style <word>`
: 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

Expand Down
12 changes: 12 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,22 @@ pub struct Cli {
#[arg(long, value_name = "THEME", value_parser = ["fancy", "unicode"])]
pub icon_theme: Option<String>,

/// Choose indicator style
#[arg(long, value_name = "WORD", value_parser = ["none", "slash", "file-type", "classify"])]
pub indicator_style: Option<String>,

/// 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', conflicts_with_all = ["file_type", "indicators"])]
pub slash: bool,

/// Display extended file metadata as a table
#[arg(short, long)]
pub long: bool,
Expand Down
8 changes: 8 additions & 0 deletions src/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct Config {
pub icons: Option<Icons>,
pub ignore_globs: Option<Vec<String>>,
pub indicators: Option<bool>,
pub indicator_style: Option<String>,
pub layout: Option<Layout>,
pub recursion: Option<Recursion>,
pub size: Option<SizeFlag>,
Expand Down Expand Up @@ -87,6 +88,7 @@ impl Config {
icons: None,
ignore_globs: None,
indicators: None,
indicator_style: None,
layout: None,
recursion: None,
size: None,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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),
Expand Down
40 changes: 35 additions & 5 deletions src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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<Self, Error> {
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),
Expand All @@ -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),
Expand Down Expand Up @@ -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<T> {
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.
Expand Down
132 changes: 132 additions & 0 deletions src/flags/indicator_style.rs
Original file line number Diff line number Diff line change
@@ -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<Self> 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<Self> {
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<Self> {
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));
}
}
Loading