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

✨ theme: bring icon theme to lsd #707

Merged
merged 16 commits into from Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- In keeping with the coreutils change, add quotes and escapes for necessary filenames from [merelymyself](https://github.com/merelymyself)
- Add support for icon theme from [zwpaper](https://github.com/zwpaper)

## [0.23.1] - 2022-09-13

Expand All @@ -20,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Reduce the binary size and improve the performance from [sabify](https://github.com/sabify)
### Fixed
- Fix rendering issues in Windows from [meain](https://gitHub.com/meain)
- Fix rendering issues in Windows from [meain](https://github.com/meain)

## [0.22.0] - 2022-06-12
### Added
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -27,6 +27,7 @@ libc = "0.2.*"
human-sort = "0.2.2"
term_grid = "0.1.*"
terminal_size = "0.1.*"
thiserror = "1.0"
chrono = "0.4.*"
chrono-humanize = "0.1.*"
unicode-width = "0.1.*"
Expand Down
74 changes: 69 additions & 5 deletions README.md
Expand Up @@ -233,9 +233,11 @@ header: false

## Theme

`lsd` can be configured with a theme file to set the colors.
`lsd` can be configured with theme files to set the colors or icons.

Theme can be configured in the [configuration file](#configuration)(color.theme),
### Color Theme

Color theme can be configured in the [configuration file](#configuration)(color.theme),
The valid theme configurations are:

- `default`: the default color scheme shipped in `lsd`
Expand All @@ -244,12 +246,12 @@ The valid theme configurations are:
when configured with the `theme-file-name` which is a `yaml` file,
`lsd` will look up the theme file in the following way:

- relative name: check the themes under XDG Base Directory, e.g. ~/.config/lsd/themes/<theme-file-name>.yaml
meain marked this conversation as resolved.
Show resolved Hide resolved
- relative name: check the XDG Base Directory, e.g. ~/.config/lsd/<theme-file-name>.yaml
- absolute name: use the file path and name to find theme file

Check [Theme file content](#theme-file-content) for details.
Check [Color Theme file content](#color-theme-file-content) for details.

### Theme file content
#### Color Theme file content

Theme file use the [crossterm](https://crates.io/crates/crossterm)
to configure the colors, check [crossterm](https://docs.rs/crossterm/0.20.0/crossterm/style/enum.Color.html)
Expand Down Expand Up @@ -297,6 +299,68 @@ and then change its colors, the items missed would fallback to use the default c
Please also notice that an empty theme is **NOT** supported due to
[a bug in serde lib](https://github.com/dtolnay/serde-yaml/issues/86).

### Icon Theme

Icon theme can be configured in a fixed location, `$XDG_CONFIG_DIR/lsd/icons.yaml`,
for example, `~/.config/lsd/icons.yaml` on macOS,
please check [Config file location](#config-file-location) to make sure where is `$XDG_CONFIG_DIR`.

As the file name indicated, the icon theme file is a `yaml` file.

Check [Icon Theme file content](#icon-theme-file-content) for details.

#### Icon Theme file content

lsd support 3 kinds of icon configuration:
- filetype
- name
- extension

The default icon theme scheme shipped with `lsd` can be check in [icon theme source code](src/theme/icon.rs), we will load the default theme, and overwrite it with user defined parts, here is a example for icon theme.

lsd icon theme support both nerd font and Unicode in the same time, you can use any one to config the theme, or even combine them.
meain marked this conversation as resolved.
Show resolved Hide resolved

nerd font:

```yaml
name:
.trash: 
.cargo: 
.emacs.d: 
a.out: 
extension:
go: 
hs: 
rs: 
filetype:
dir: 
file: 
pipe: 
socket: 
executable: 
symlink-dir: 
symlink-file: 
device-char: 
device-block: ﰩ
special: 
```

Unicode:

```yaml
name:
.trash: 🗑
extension:
rs: 🦀
filetype:
dir: 📂
file: 📄
pipe: 📩
```

When creating a theme for `lsd`, you can specify any part of the default theme,
and then change its colors, the items missed would fallback to use the default colors.
meain marked this conversation as resolved.
Show resolved Hide resolved

## External Configurations

### Required
Expand Down
2 changes: 1 addition & 1 deletion src/app.rs
Expand Up @@ -53,9 +53,9 @@ pub fn build() -> App<'static> {
.arg(
Arg::with_name("icon-theme")
.long("icon-theme")
.default_value("fancy")
.possible_value("fancy")
.possible_value("unicode")
.default_value("fancy")
.multiple_occurrences(true)
.takes_value(true)
.number_of_values(1)
Expand Down
57 changes: 31 additions & 26 deletions src/color.rs
@@ -1,14 +1,11 @@
mod theme;

use crossterm::style::{Attribute, ContentStyle, StyledContent, Stylize};
use theme::Theme;

pub use crate::flags::color::ThemeOption;

use crossterm::style::Color;
use crossterm::style::{Attribute, ContentStyle, StyledContent, Stylize};
use lscolors::{Indicator, LsColors};
use std::path::Path;

pub use crate::flags::color::ThemeOption;
use crate::theme::{color::ColorTheme, Theme};

#[allow(dead_code)]
#[derive(Hash, Debug, Eq, PartialEq, Clone)]
pub enum Elem {
Expand Down Expand Up @@ -71,7 +68,7 @@ impl Elem {
matches!(self, Elem::Dir { uid: true } | Elem::File { uid: true, .. })
}

fn get_color(&self, theme: &theme::Theme) -> Color {
pub fn get_color(&self, theme: &ColorTheme) -> Color {
match self {
Elem::File {
exec: true,
Expand Down Expand Up @@ -131,16 +128,24 @@ impl Elem {
pub type ColoredString = StyledContent<String>;

pub struct Colors {
theme: Option<Theme>,
theme: Option<ColorTheme>,
lscolors: Option<LsColors>,
}

impl Colors {
pub fn new(t: ThemeOption) -> Self {
let theme = match t {
ThemeOption::NoColor => None,
ThemeOption::Default | ThemeOption::NoLscolors => Some(Theme::default()),
ThemeOption::Custom(ref file) => Some(Theme::from_path(file).unwrap_or_default()),
ThemeOption::Default | ThemeOption::NoLscolors => Some(Theme::default().color),
ThemeOption::Custom(ref file) => {
zwpaper marked this conversation as resolved.
Show resolved Hide resolved
// TODO: drop the `themes` dir prefix, adding it here only for backwards compatibility
Some(
Theme::from_path::<ColorTheme>(
Path::new("themes").join(file).to_str().unwrap_or(file),
)
.unwrap_or_default(),
)
}
};
let lscolors = match t {
ThemeOption::Default | ThemeOption::Custom(_) => {
Expand Down Expand Up @@ -301,8 +306,8 @@ fn to_content_style(ls: &lscolors::Style) -> ContentStyle {
#[cfg(test)]
mod tests {
use super::Colors;
use crate::color::Theme;
use crate::color::ThemeOption;
use crate::theme::color::ColorTheme;
#[test]
fn test_color_new_no_color_theme() {
assert!(Colors::new(ThemeOption::NoColor).theme.is_none());
Expand All @@ -312,31 +317,31 @@ mod tests {
fn test_color_new_default_theme() {
assert_eq!(
Colors::new(ThemeOption::Default).theme,
Some(Theme::default_dark()),
Some(ColorTheme::default_dark()),
);
}

#[test]
fn test_color_new_bad_custom_theme() {
assert_eq!(
Colors::new(ThemeOption::Custom("not-existed".to_string())).theme,
Some(Theme::default_dark()),
Some(ColorTheme::default_dark()),
);
}
}

#[cfg(test)]
mod elem {
use super::Elem;
use crate::color::{theme, Theme};
use crate::theme::{color, color::ColorTheme};
use crossterm::style::Color;

#[cfg(test)]
fn test_theme() -> Theme {
Theme {
fn test_theme() -> ColorTheme {
ColorTheme {
user: Color::AnsiValue(230), // Cornsilk1
group: Color::AnsiValue(187), // LightYellow3
permission: theme::Permission {
permission: color::Permission {
read: Color::Green,
write: Color::Yellow,
exec: Color::Red,
Expand All @@ -346,19 +351,19 @@ mod elem {
acl: Color::DarkCyan,
context: Color::Cyan,
},
file_type: theme::FileType {
file: theme::File {
file_type: color::FileType {
file: color::File {
exec_uid: Color::AnsiValue(40), // Green3
uid_no_exec: Color::AnsiValue(184), // Yellow3
exec_no_uid: Color::AnsiValue(40), // Green3
no_exec_no_uid: Color::AnsiValue(184), // Yellow3
},
dir: theme::Dir {
dir: color::Dir {
uid: Color::AnsiValue(33), // DodgerBlue1
no_uid: Color::AnsiValue(33), // DodgerBlue1
},
pipe: Color::AnsiValue(44), // DarkTurquoise
symlink: theme::Symlink {
symlink: color::Symlink {
default: Color::AnsiValue(44), // DarkTurquoise
broken: Color::AnsiValue(124), // Red3
missing_target: Color::AnsiValue(124), // Red3
Expand All @@ -368,22 +373,22 @@ mod elem {
socket: Color::AnsiValue(44), // DarkTurquoise
special: Color::AnsiValue(44), // DarkTurquoise
},
date: theme::Date {
date: color::Date {
hour_old: Color::AnsiValue(40), // Green3
day_old: Color::AnsiValue(42), // SpringGreen2
older: Color::AnsiValue(36), // DarkCyan
},
size: theme::Size {
size: color::Size {
none: Color::AnsiValue(245), // Grey
small: Color::AnsiValue(229), // Wheat1
medium: Color::AnsiValue(216), // LightSalmon1
large: Color::AnsiValue(172), // Orange3
},
inode: theme::INode {
inode: color::INode {
valid: Color::AnsiValue(13), // Pink
invalid: Color::AnsiValue(245), // Grey
},
links: theme::Links {
links: color::Links {
valid: Color::AnsiValue(13), // Pink
invalid: Color::AnsiValue(245), // Grey
},
Expand Down
16 changes: 5 additions & 11 deletions src/core.rs
@@ -1,10 +1,7 @@
use crate::color::Colors;
use crate::display;
use crate::flags::{
ColorOption, Display, Flags, HyperlinkOption, IconOption, IconTheme, Layout, SortOrder,
ThemeOption,
};
use crate::icon::{self, Icons};
use crate::flags::{ColorOption, Display, Flags, HyperlinkOption, Layout, SortOrder, ThemeOption};
use crate::icon::Icons;
use crate::meta::Meta;
use crate::{print_error, print_output, sort, ExitCode};
use std::path::PathBuf;
Expand Down Expand Up @@ -47,11 +44,8 @@ impl Core {
_ => flags.color.theme.clone(),
};

let icon_theme = match (tty_available, flags.icons.when, flags.icons.theme) {
(_, IconOption::Never, _) | (false, IconOption::Auto, _) => icon::Theme::NoIcon,
(_, _, IconTheme::Fancy) => icon::Theme::Fancy,
(_, _, IconTheme::Unicode) => icon::Theme::Unicode,
};
let icon_when = flags.icons.when;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to move this logic to inside Icons::new?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logic here is all about how to set icons, it should be handled inside the Icons package

let icon_theme = flags.icons.theme.clone();

// TODO: Rework this so that flags passed downstream does not
// have Auto option for any (icon, color, hyperlink).
Expand All @@ -78,7 +72,7 @@ impl Core {
Self {
flags,
colors: Colors::new(color_theme),
icons: Icons::new(icon_theme, icon_separator),
icons: Icons::new(tty_available, icon_when, icon_theme, icon_separator),
sorters,
}
}
Expand Down