Detailed changes
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
+<g id="surface1">
@@ -1,4 +1,9 @@
{
+ "stems": {
+ "Podfile": "ruby",
+ "Procfile": "heroku",
+ "Dockerfile": "docker"
+ },
"suffixes": {
"astro": "astro",
"Emakefile": "erlang",
@@ -86,6 +91,7 @@
"mdb": "storage",
"mdf": "storage",
"mdx": "document",
+ "metadata": "code",
"mkv": "video",
"mjs": "code",
"mka": "audio",
@@ -189,6 +195,9 @@
"default": {
"icon": "icons/file_icons/file.svg"
},
+ "docker": {
+ "icon": "icons/file_icons/docker.svg"
+ },
"document": {
"icon": "icons/file_icons/book.svg"
},
@@ -216,6 +225,9 @@
"haskell": {
"icon": "icons/file_icons/haskell.svg"
},
+ "heroku": {
+ "icon": "icons/file_icons/heroku.svg"
+ },
"go": {
"icon": "icons/file_icons/go.svg"
},
@@ -228,7 +240,7 @@
"java": {
"icon": "icons/file_icons/java.svg"
},
- "kotlin":{
+ "kotlin": {
"icon": "icons/file_icons/kotlin.svg"
},
"lock": {
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
+<g id="surface1">
@@ -13,6 +13,7 @@ struct TypeConfig {
#[derive(Deserialize, Debug)]
pub struct FileAssociations {
+ stems: HashMap<String, String>,
suffixes: HashMap<String, String>,
types: HashMap<String, TypeConfig>,
}
@@ -38,6 +39,7 @@ impl FileAssociations {
.map_err(Into::into)
})
.unwrap_or_else(|_| FileAssociations {
+ stems: HashMap::default(),
suffixes: HashMap::default(),
types: HashMap::default(),
})
@@ -49,7 +51,14 @@ impl FileAssociations {
// FIXME: Associate a type with the languages and have the file's language
// override these associations
maybe!({
- let suffix = path.icon_suffix()?;
+ let suffix = path.icon_stem_or_suffix()?;
+
+ if let Some(type_str) = this.stems.get(suffix) {
+ return this
+ .types
+ .get(type_str)
+ .map(|type_config| type_config.icon.clone());
+ }
this.suffixes
.get(suffix)
@@ -48,7 +48,7 @@ lazy_static::lazy_static! {
pub trait PathExt {
fn compact(&self) -> PathBuf;
- fn icon_suffix(&self) -> Option<&str>;
+ fn icon_stem_or_suffix(&self) -> Option<&str>;
fn extension_or_hidden_file_name(&self) -> Option<&str>;
fn try_from_bytes<'a>(bytes: &'a [u8]) -> anyhow::Result<Self>
where
@@ -100,17 +100,17 @@ impl<T: AsRef<Path>> PathExt for T {
}
}
- /// Returns a suffix of the path that is used to determine which file icon to use
- fn icon_suffix(&self) -> Option<&str> {
- let file_name = self.as_ref().file_name()?.to_str()?;
-
+ /// Returns either the suffix if available, or the file stem otherwise to determine which file icon to use
+ fn icon_stem_or_suffix(&self) -> Option<&str> {
+ let path = self.as_ref();
+ let file_name = path.file_name()?.to_str()?;
if file_name.starts_with('.') {
return file_name.strip_prefix('.');
}
- self.as_ref()
- .extension()
- .and_then(|extension| extension.to_str())
+ path.extension()
+ .and_then(|e| e.to_str())
+ .or_else(|| path.file_stem()?.to_str())
}
/// Returns a file's extension or, if the file is hidden, its name without the leading dot
@@ -403,26 +403,30 @@ mod tests {
}
#[test]
- fn test_icon_suffix() {
+ fn test_icon_stem_or_suffix() {
// No dots in name
let path = Path::new("/a/b/c/file_name.rs");
- assert_eq!(path.icon_suffix(), Some("rs"));
+ assert_eq!(path.icon_stem_or_suffix(), Some("rs"));
// Single dot in name
let path = Path::new("/a/b/c/file.name.rs");
- assert_eq!(path.icon_suffix(), Some("rs"));
+ assert_eq!(path.icon_stem_or_suffix(), Some("rs"));
+
+ // No suffix
+ let path = Path::new("/a/b/c/file");
+ assert_eq!(path.icon_stem_or_suffix(), Some("file"));
// Multiple dots in name
let path = Path::new("/a/b/c/long.file.name.rs");
- assert_eq!(path.icon_suffix(), Some("rs"));
+ assert_eq!(path.icon_stem_or_suffix(), Some("rs"));
// Hidden file, no extension
let path = Path::new("/a/b/c/.gitignore");
- assert_eq!(path.icon_suffix(), Some("gitignore"));
+ assert_eq!(path.icon_stem_or_suffix(), Some("gitignore"));
// Hidden file, with extension
let path = Path::new("/a/b/c/.eslintrc.js");
- assert_eq!(path.icon_suffix(), Some("eslintrc.js"));
+ assert_eq!(path.icon_stem_or_suffix(), Some("eslintrc.js"));
}
#[test]