🔨 simplify icon theme and updates due to review

Signed-off-by: Wei Zhang <kweizh@gmail.com>
This commit is contained in:
Wei Zhang 2022-09-28 01:18:51 +08:00 committed by Abin Simon
parent 9a7a3eaf13
commit acae1201a8
4 changed files with 68 additions and 58 deletions

View file

@ -246,7 +246,7 @@ The valid theme configurations are:
when configured with the `theme-file-name` which is a `yaml` file, when configured with the `theme-file-name` which is a `yaml` file,
`lsd` will look up the theme file in the following way: `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 - 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 - absolute name: use the file path and name to find theme file
Check [Color Theme file content](#color-theme-file-content) for details. Check [Color Theme file content](#color-theme-file-content) for details.
@ -301,9 +301,9 @@ Please also notice that an empty theme is **NOT** supported due to
### Icon Theme ### Icon Theme
Icon theme can be configured in a fixed location, `LSD_CONFIG_DIR/themes/icons.yaml`, Icon theme can be configured in a fixed location, `$XDG_CONFIG_DIR/lsd/icons.yaml`,
for example, `~/.config/lsd/themes/icons.yam` on macOS, for example, `~/.config/lsd/icons.yaml` on macOS,
please check [Config file location](#config-file-location) to make sure where is `LSD_CONFIG_DIR`. 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. As the file name indicated, the icon theme file is a `yaml` file.
@ -312,9 +312,9 @@ Check [Icon Theme file content](#icon-theme-file-content) for details.
#### Icon Theme file content #### Icon Theme file content
lsd support 3 kinds of icon configuration: lsd support 3 kinds of icon configuration:
- icons-by-filetype - filetype
- icons-by-name - name
- icons-by-extension - 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. 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.
@ -323,16 +323,16 @@ lsd icon theme support both nerd font and Unicode in the same time, you can use
nerd font: nerd font:
```yaml ```yaml
icons-by-name: name:
.trash:  .trash: 
.cargo:  .cargo: 
.emacs.d:  .emacs.d: 
a.out:  a.out: 
icons-by-extension: extension:
go:  go: 
hs:  hs: 
rs:  rs: 
icons-by-filetype: filetype:
dir:  dir: 
file:  file: 
pipe:  pipe: 
@ -348,11 +348,11 @@ icons-by-filetype:
Unicode: Unicode:
```yaml ```yaml
icons-by-name: name:
.trash: 🗑 .trash: 🗑
icons-by-extension: extension:
rs: 🦀 rs: 🦀
icons-by-filetype: filetype:
dir: 📂 dir: 📂
file: 📄 file: 📄
pipe: 📩 pipe: 📩
@ -361,9 +361,6 @@ icons-by-filetype:
When creating a theme for `lsd`, you can specify any part of the default theme, 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. and then change its colors, the items missed would fallback to use the default colors.
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).
## External Configurations ## External Configurations
### Required ### Required

View file

@ -38,34 +38,29 @@ impl Icons {
// Check file types // Check file types
let file_type: FileType = name.file_type(); let file_type: FileType = name.file_type();
let icon = match file_type { let icon = match file_type {
FileType::SymLink { is_dir: true } => &t.icons_by_filetype.symlink_dir, FileType::SymLink { is_dir: true } => &t.filetype.symlink_dir,
FileType::SymLink { is_dir: false } => &t.icons_by_filetype.symlink_file, FileType::SymLink { is_dir: false } => &t.filetype.symlink_file,
FileType::Socket => &t.icons_by_filetype.socket, FileType::Socket => &t.filetype.socket,
FileType::Pipe => &t.icons_by_filetype.pipe, FileType::Pipe => &t.filetype.pipe,
FileType::CharDevice => &t.icons_by_filetype.device_char, FileType::CharDevice => &t.filetype.device_char,
FileType::BlockDevice => &t.icons_by_filetype.device_block, FileType::BlockDevice => &t.filetype.device_block,
FileType::Special => &t.icons_by_filetype.special, FileType::Special => &t.filetype.special,
_ => { _ => {
if let Some(icon) = t if let Some(icon) = t.name.get(name.file_name().to_lowercase().as_str()) {
.icons_by_name
.get(name.file_name().to_lowercase().as_str())
{
icon icon
} else if let Some(icon) = name } else if let Some(icon) = name
.extension() .extension()
.and_then(|ext| t.icons_by_extension.get(ext.to_lowercase().as_str())) .and_then(|ext| t.extension.get(ext.to_lowercase().as_str()))
{ {
icon icon
} else { } else {
match file_type { match file_type {
FileType::Directory { .. } => &t.icons_by_filetype.dir, FileType::Directory { .. } => &t.filetype.dir,
// If a file has no extension and is executable, show an icon. // If a file has no extension and is executable, show an icon.
// Except for Windows, it marks everything as an executable. // Except for Windows, it marks everything as an executable.
#[cfg(not(windows))] #[cfg(not(windows))]
FileType::File { exec: true, .. } => { FileType::File { exec: true, .. } => &t.filetype.executable,
&t.icons_by_filetype.executable _ => &t.filetype.file,
}
_ => &t.icons_by_filetype.file,
} }
} }
} }

View file

@ -38,7 +38,10 @@ impl Theme {
/// This read theme from file, /// This read theme from file,
/// use the file path if it is absolute /// use the file path if it is absolute
/// prefix the config_file dir to it if it is not /// prefix the config_file dir to it if it is not
pub fn from_path<D: DeserializeOwned>(file: &str) -> Result<D, Error> { pub fn from_path<D>(file: &str) -> Result<D, Error>
where
D: DeserializeOwned + Default,
{
let real = if let Some(path) = config_file::Config::expand_home(file) { let real = if let Some(path) = config_file::Config::expand_home(file) {
path path
} else { } else {
@ -49,7 +52,7 @@ impl Theme {
real real
} else { } else {
match config_file::Config::config_file_path() { match config_file::Config::config_file_path() {
Some(p) => p.join("themes").join(real), Some(p) => p.join(real),
None => return Err(Error::InvalidPath("config home not existed".into())), None => return Err(Error::InvalidPath("config home not existed".into())),
} }
}; };
@ -72,7 +75,13 @@ impl Theme {
} }
/// This constructs a Theme struct with a passed [Yaml] str. /// This constructs a Theme struct with a passed [Yaml] str.
fn with_yaml<D: DeserializeOwned>(yaml: &str) -> Result<D, serde_yaml::Error> { fn with_yaml<D>(yaml: &str) -> Result<D, serde_yaml::Error>
where
D: DeserializeOwned + Default,
{
if yaml.trim() == "" {
return Ok(D::default());
}
serde_yaml::from_str::<D>(yaml) serde_yaml::from_str::<D>(yaml)
} }
} }

View file

@ -6,16 +6,16 @@ use std::collections::HashMap;
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
#[serde(default)] #[serde(default)]
pub struct IconTheme { pub struct IconTheme {
pub icons_by_name: HashMap<String, String>, pub name: HashMap<String, String>,
pub icons_by_extension: HashMap<String, String>, pub extension: HashMap<String, String>,
pub icons_by_filetype: IconByType, pub filetype: ByType,
} }
#[derive(Debug, Deserialize, PartialEq, Eq)] #[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
#[serde(default)] #[serde(default)]
pub struct IconByType { pub struct ByType {
pub dir: String, pub dir: String,
pub file: String, pub file: String,
pub pipe: String, pub pipe: String,
@ -31,16 +31,16 @@ pub struct IconByType {
impl Default for IconTheme { impl Default for IconTheme {
fn default() -> Self { fn default() -> Self {
IconTheme { IconTheme {
icons_by_name: Self::get_default_icons_by_name(), name: Self::get_default_icons_by_name(),
icons_by_extension: Self::get_default_icons_by_extension(), extension: Self::get_default_icons_by_extension(),
icons_by_filetype: IconByType::default(), filetype: ByType::default(),
} }
} }
} }
impl Default for IconByType { impl Default for ByType {
fn default() -> IconByType { fn default() -> ByType {
IconByType { ByType {
dir: "\u{f115}".into(), //  dir: "\u{f115}".into(), // 
file: "\u{f016}".into(), //  file: "\u{f016}".into(), // 
pipe: "\u{f731}".into(), //  pipe: "\u{f731}".into(), // 
@ -55,9 +55,9 @@ impl Default for IconByType {
} }
} }
impl IconByType { impl ByType {
pub fn unicode() -> Self { pub fn unicode() -> Self {
IconByType { ByType {
dir: "\u{1f4c2}".into(), dir: "\u{1f4c2}".into(),
file: "\u{1f4c4}".into(), file: "\u{1f4c4}".into(),
pipe: "\u{1f4e9}".into(), pipe: "\u{1f4e9}".into(),
@ -75,9 +75,9 @@ impl IconByType {
impl IconTheme { impl IconTheme {
pub fn unicode() -> Self { pub fn unicode() -> Self {
IconTheme { IconTheme {
icons_by_name: HashMap::new(), name: HashMap::new(),
icons_by_extension: HashMap::new(), extension: HashMap::new(),
icons_by_filetype: IconByType::unicode(), filetype: ByType::unicode(),
} }
} }
@ -482,16 +482,16 @@ mod tests {
fn partial_default_yaml() -> &'static str { fn partial_default_yaml() -> &'static str {
r#"--- r#"---
icons-by-name: name:
.trash: .trash:
.cargo: .cargo:
.emacs.d: .emacs.d:
a.out: a.out:
icons-by-extension: extension:
go: go:
hs: hs:
rs: rs:
icons-by-filetype: filetype:
dir: dir:
file: file:
pipe: pipe:
@ -506,7 +506,7 @@ icons-by-filetype:
} }
fn check_partial_yaml(def: &IconTheme, yaml: &IconTheme) { fn check_partial_yaml(def: &IconTheme, yaml: &IconTheme) {
assert_eq!(def.icons_by_filetype.dir, yaml.icons_by_filetype.dir,); assert_eq!(def.filetype.dir, yaml.filetype.dir,);
} }
#[test] #[test]
@ -533,7 +533,16 @@ icons-by-filetype:
fn test_empty_theme_return_default() { fn test_empty_theme_return_default() {
// Must contain one field at least // Must contain one field at least
// ref https://github.com/dtolnay/serde-yaml/issues/86 // ref https://github.com/dtolnay/serde-yaml/issues/86
let empty: IconTheme = Theme::with_yaml("icons-by-filetype:\n dir: ").unwrap(); //  is the default value let empty: IconTheme = Theme::with_yaml(" ").unwrap();
let default = IconTheme::default();
check_partial_yaml(&empty, &default);
}
#[test]
fn test_partial_theme_return_default() {
// Must contain one field at least
// ref https://github.com/dtolnay/serde-yaml/issues/86
let empty: IconTheme = Theme::with_yaml("filetype:\n dir: ").unwrap(); //  is the default value
let default = IconTheme::default(); let default = IconTheme::default();
check_partial_yaml(&empty, &default); check_partial_yaml(&empty, &default);
} }
@ -542,7 +551,7 @@ icons-by-filetype:
fn test_serde_dir_from_yaml() { fn test_serde_dir_from_yaml() {
// Must contain one field at least // Must contain one field at least
// ref https://github.com/dtolnay/serde-yaml/issues/86 // ref https://github.com/dtolnay/serde-yaml/issues/86
let empty: IconTheme = Theme::with_yaml("icons-by-filetype:\n dir: ").unwrap(); let empty: IconTheme = Theme::with_yaml("filetype:\n dir: ").unwrap();
assert_eq!(empty.icons_by_filetype.dir, ""); assert_eq!(empty.filetype.dir, "");
} }
} }