mirror of
https://github.com/lsd-rs/lsd
synced 2025-01-05 16:38:50 +00:00
🔨 simplify icon theme and updates due to review
Signed-off-by: Wei Zhang <kweizh@gmail.com>
This commit is contained in:
parent
9a7a3eaf13
commit
acae1201a8
4 changed files with 68 additions and 58 deletions
29
README.md
29
README.md
|
@ -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
|
||||||
|
|
29
src/icon.rs
29
src/icon.rs
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/theme.rs
15
src/theme.rs
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue