diff --git a/Cargo.lock b/Cargo.lock
index dca182a38f..fd99fe7ef7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5360,6 +5360,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
+ "collections",
"context_menu",
"db",
"drag_and_drop",
diff --git a/assets/icons/file_icons/file_types.json b/assets/icons/file_icons/file_types.json
new file mode 100644
index 0000000000..8803647857
--- /dev/null
+++ b/assets/icons/file_icons/file_types.json
@@ -0,0 +1,107 @@
+{
+ "suffixes": {
+ "jpg": "image",
+ "jpeg": "image",
+ "tiff": "image",
+ "svg": "image",
+ "psd": "image",
+ "png": "image",
+ "gif": "image",
+ "bmp": "image",
+ "ico": "image",
+ "mp4": "video",
+ "webm": "video",
+ "ogg": "video",
+ "mp3": "audio",
+ "wav": "audio",
+ "flac": "audio",
+ "aac": "audio",
+ "pdf": "document",
+ "doc": "document",
+ "docx": "document",
+ "xls": "document",
+ "xlsx": "document",
+ "ppt": "document",
+ "pptx": "document",
+ "odt": "document",
+ "ods": "document",
+ "odp": "document",
+ "txt": "document",
+ "rtf": "document",
+ "md": "document",
+ "html": "template",
+ "htm": "template",
+ "xml": "template",
+ "hbs": "template",
+ "handlebars": "template",
+ "js": "code",
+ "css": "code",
+ "php": "code",
+ "c": "code",
+ "cpp": "code",
+ "h": "code",
+ "hpp": "code",
+ "java": "code",
+ "py": "code",
+ "swift": "code",
+ "go": "code",
+ "rb": "code",
+ "rs": "code",
+ "rkt": "code",
+ "scm": "code",
+ "sql": "code",
+ "json": "settings",
+ "ini": "settings",
+ "yaml": "settings",
+ "yml": "settings",
+ "toml": "settings",
+ "conf": "settings",
+ "lock": "settings",
+ "gitignore": "vcs",
+ "gitattributes": "vcs",
+ "ps1": "terminal",
+ "sh": "terminal",
+ "bash": "terminal",
+ "zsh": "terminal",
+ "fish": "terminal",
+ "log": "log"
+ },
+ "types": {
+ "directory": {
+ "icon": "icons/file_icons/quill/folder.svg"
+ },
+ "expanded_directory": {
+ "icon": "icons/file_icons/quill/folder-open.svg"
+ },
+ "image": {
+ "icon": "icons/file_icons/quill/image.svg"
+ },
+ "video": {
+ "icon": "icons/file_icons/quill/file.svg"
+ },
+ "audio": {
+ "icon": "icons/file_icons/quill/file.svg"
+ },
+ "document": {
+ "icon": "icons/file_icons/quill/book.svg"
+ },
+ "template": {
+ "icon": "icons/file_icons/quill/html.svg"
+ },
+ "code": {
+ "icon": "icons/file_icons/quill/code.svg"
+ },
+ "settings": {
+ "icon": "icons/file_icons/quill/settings.svg"
+ },
+ "vcs": {
+ "icon": "icons/file_icons/quill/git.svg"
+ },
+ "terminal": {
+ "icon": "icons/file_icons/quill/terminal.svg"
+ },
+ "log": {
+ "icon": "icons/file_icons/quill/info.svg"
+ }
+ }
+}
diff --git a/assets/icons/file_icons/quill/anchor.svg b/assets/icons/file_icons/quill/anchor.svg
new file mode 100644
index 0000000000..4828578ee0
--- /dev/null
+++ b/assets/icons/file_icons/quill/anchor.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/archive.svg b/assets/icons/file_icons/quill/archive.svg
new file mode 100644
index 0000000000..c78ca0cff6
--- /dev/null
+++ b/assets/icons/file_icons/quill/archive.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/book.svg b/assets/icons/file_icons/quill/book.svg
new file mode 100644
index 0000000000..af918b5c61
--- /dev/null
+++ b/assets/icons/file_icons/quill/book.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/camera.svg b/assets/icons/file_icons/quill/camera.svg
new file mode 100644
index 0000000000..f861af607c
--- /dev/null
+++ b/assets/icons/file_icons/quill/camera.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/code.svg b/assets/icons/file_icons/quill/code.svg
new file mode 100644
index 0000000000..a844740f1a
--- /dev/null
+++ b/assets/icons/file_icons/quill/code.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/database.svg b/assets/icons/file_icons/quill/database.svg
new file mode 100644
index 0000000000..8c98d5ac16
--- /dev/null
+++ b/assets/icons/file_icons/quill/database.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/eslint.svg b/assets/icons/file_icons/quill/eslint.svg
new file mode 100644
index 0000000000..880689293a
--- /dev/null
+++ b/assets/icons/file_icons/quill/eslint.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/file.svg b/assets/icons/file_icons/quill/file.svg
new file mode 100644
index 0000000000..492c383ab6
--- /dev/null
+++ b/assets/icons/file_icons/quill/file.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/folder-open.svg b/assets/icons/file_icons/quill/folder-open.svg
new file mode 100644
index 0000000000..00a94c199f
--- /dev/null
+++ b/assets/icons/file_icons/quill/folder-open.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/folder.svg b/assets/icons/file_icons/quill/folder.svg
new file mode 100644
index 0000000000..9cc5b4a8c9
--- /dev/null
+++ b/assets/icons/file_icons/quill/folder.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/git.svg b/assets/icons/file_icons/quill/git.svg
new file mode 100644
index 0000000000..830a7f9565
--- /dev/null
+++ b/assets/icons/file_icons/quill/git.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/hash.svg b/assets/icons/file_icons/quill/hash.svg
new file mode 100644
index 0000000000..36366625fe
--- /dev/null
+++ b/assets/icons/file_icons/quill/hash.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/html.svg b/assets/icons/file_icons/quill/html.svg
new file mode 100644
index 0000000000..7704575f24
--- /dev/null
+++ b/assets/icons/file_icons/quill/html.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/image.svg b/assets/icons/file_icons/quill/image.svg
new file mode 100644
index 0000000000..0ec9583edd
--- /dev/null
+++ b/assets/icons/file_icons/quill/image.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/info.svg b/assets/icons/file_icons/quill/info.svg
new file mode 100644
index 0000000000..af3fa9d39d
--- /dev/null
+++ b/assets/icons/file_icons/quill/info.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/lock.svg b/assets/icons/file_icons/quill/lock.svg
new file mode 100644
index 0000000000..a1e36e6c12
--- /dev/null
+++ b/assets/icons/file_icons/quill/lock.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/package.svg b/assets/icons/file_icons/quill/package.svg
new file mode 100644
index 0000000000..9bda169cf5
--- /dev/null
+++ b/assets/icons/file_icons/quill/package.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/prettier.svg b/assets/icons/file_icons/quill/prettier.svg
new file mode 100644
index 0000000000..ba7b340654
--- /dev/null
+++ b/assets/icons/file_icons/quill/prettier.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/settings.svg b/assets/icons/file_icons/quill/settings.svg
new file mode 100644
index 0000000000..f0209bf3c2
--- /dev/null
+++ b/assets/icons/file_icons/quill/settings.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/assets/icons/file_icons/quill/terminal.svg b/assets/icons/file_icons/quill/terminal.svg
new file mode 100644
index 0000000000..964f44251f
--- /dev/null
+++ b/assets/icons/file_icons/quill/terminal.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs
index 60f14d9fa7..50a0b4b161 100644
--- a/crates/language/src/language.rs
+++ b/crates/language/src/language.rs
@@ -342,8 +342,6 @@ pub struct LanguageConfig {
pub block_comment: Option<(Arc, Arc)>,
#[serde(default)]
pub overrides: HashMap,
- #[serde(default)]
- pub icon_path: Option>,
}
#[derive(Debug, Default)]
@@ -410,7 +408,6 @@ impl Default for LanguageConfig {
line_comment: Default::default(),
block_comment: Default::default(),
overrides: Default::default(),
- icon_path: Default::default(),
}
}
}
@@ -755,16 +752,6 @@ impl LanguageRegistry {
self.get_or_load_language(|config| UniCase::new(config.name.as_ref()) == name)
}
- pub fn icon_for_suffix(self: &Arc, suffix: &str) -> Option> {
- let state = self.state.read();
- state
- .available_languages
- .iter()
- .find(|langauge| langauge.config.path_suffixes.iter().any(|s| s == suffix))
- .map(|language| language.config.icon_path.clone())
- .flatten()
- }
-
pub fn language_for_name_or_extension(
self: &Arc,
string: &str,
diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs
index 80c098baa6..5bb8af3f38 100644
--- a/crates/project/src/project.rs
+++ b/crates/project/src/project.rs
@@ -2474,10 +2474,6 @@ impl Project {
})
}
- pub fn icon_for_path(&self, path: &Path) -> Option> {
- self.languages.icon_for_suffix(path.extension()?.to_str()?)
- }
-
fn detect_language_for_buffer(
&mut self,
buffer_handle: &ModelHandle,
diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml
index 33606fccc4..4fe5372a51 100644
--- a/crates/project_panel/Cargo.toml
+++ b/crates/project_panel/Cargo.toml
@@ -10,6 +10,7 @@ doctest = false
[dependencies]
context_menu = { path = "../context_menu" }
+collections = { path = "../collections" }
db = { path = "../db" }
drag_and_drop = { path = "../drag_and_drop" }
editor = { path = "../editor" }
diff --git a/crates/project_panel/src/file_associations.rs b/crates/project_panel/src/file_associations.rs
new file mode 100644
index 0000000000..ea8e59685b
--- /dev/null
+++ b/crates/project_panel/src/file_associations.rs
@@ -0,0 +1,64 @@
+use std::{path::Path, str, sync::Arc};
+
+use collections::HashMap;
+
+use gpui::{AppContext, AssetSource};
+use serde_derive::Deserialize;
+
+#[derive(Deserialize, Debug)]
+struct TypeConfig {
+ icon: Arc,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct FileAssociations {
+ suffixes: HashMap,
+ types: HashMap,
+}
+
+pub const TEXT_FILE_ASSET: &'static str = "icons/file_icons/quill/file.svg";
+const DIRECTORY_TYPE: &'static str = "directory";
+const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_directory";
+
+pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
+ cx.set_global(FileAssociations::new(assets))
+}
+
+impl FileAssociations {
+ pub fn new(assets: impl AssetSource) -> Self {
+ let file = assets.load("icons/file_icons/file_types.json").unwrap();
+ serde_json::from_str::(str::from_utf8(&file).unwrap()).unwrap()
+ }
+
+ pub fn get_icon(path: &Path, cx: &AppContext) -> Option> {
+ if !cx.has_global::() {
+ return None;
+ }
+
+ let this = cx.global::();
+ let suffix = path.extension()?.to_str()?;
+
+ this.suffixes
+ .get(suffix)
+ .and_then(|type_str| this.types.get(type_str))
+ .map(|type_config| type_config.icon.clone())
+ }
+
+ pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Option> {
+ if !cx.has_global::() {
+ return None;
+ }
+
+ let this = cx.global::();
+
+ let key = if expanded {
+ EXPANDED_DIRECTORY_TYPE
+ } else {
+ DIRECTORY_TYPE
+ };
+
+ this.types
+ .get(key)
+ .map(|type_config| type_config.icon.clone())
+ }
+}
diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs
index 06c6c1540c..dd40fdd561 100644
--- a/crates/project_panel/src/project_panel.rs
+++ b/crates/project_panel/src/project_panel.rs
@@ -1,9 +1,11 @@
+mod file_associations;
mod project_panel_settings;
use context_menu::{ContextMenu, ContextMenuItem};
use db::kvp::KEY_VALUE_STORE;
use drag_and_drop::{DragAndDrop, Draggable};
use editor::{Cancel, Editor};
+use file_associations::{FileAssociations, TEXT_FILE_ASSET};
use futures::stream::StreamExt;
use gpui::{
actions,
@@ -15,8 +17,8 @@ use gpui::{
geometry::vector::Vector2F,
keymap_matcher::KeymapContext,
platform::{CursorStyle, MouseButton, PromptLevel},
- Action, AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, ModelHandle,
- Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
+ Action, AnyElement, AppContext, AssetSource, AsyncAppContext, ClipboardItem, Element, Entity,
+ ModelHandle, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::{
@@ -44,7 +46,6 @@ use workspace::{
const PROJECT_PANEL_KEY: &'static str = "ProjectPanel";
const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX;
-const TEXT_FILE_ASSET: &'static str = "icons/radix/file-text.svg";
pub struct ProjectPanel {
project: ModelHandle,
@@ -131,8 +132,9 @@ pub fn init_settings(cx: &mut AppContext) {
settings::register::(cx);
}
-pub fn init(cx: &mut AppContext) {
+pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
init_settings(cx);
+ file_associations::init(assets, cx);
cx.add_action(ProjectPanel::expand_selected_entry);
cx.add_action(ProjectPanel::collapse_selected_entry);
cx.add_action(ProjectPanel::select_prev);
@@ -1184,15 +1186,12 @@ impl ProjectPanel {
let entry_range = range.start.saturating_sub(ix)..end_ix - ix;
for entry in visible_worktree_entries[entry_range].iter() {
let status = git_status_setting.then(|| entry.git_status).flatten();
-
+ let is_expanded = expanded_entry_ids.binary_search(&entry.id).is_ok();
let icon = show_file_icons
.then(|| match entry.kind {
- EntryKind::File(_) => self
- .project
- .read(cx)
- .icon_for_path(&entry.path)
+ EntryKind::File(_) => FileAssociations::get_icon(&entry.path, cx)
.or_else(|| Some(TEXT_FILE_ASSET.into())),
- _ => None,
+ _ => FileAssociations::get_folder_icon(is_expanded, cx),
})
.flatten();
@@ -1208,7 +1207,7 @@ impl ProjectPanel {
depth: entry.path.components().count(),
kind: entry.kind,
is_ignored: entry.is_ignored,
- is_expanded: expanded_entry_ids.binary_search(&entry.id).is_ok(),
+ is_expanded,
is_selected: self.selection.map_or(false, |e| {
e.worktree_id == snapshot.id() && e.entry_id == entry.id
}),
@@ -1271,37 +1270,35 @@ impl ProjectPanel {
.unwrap_or(style.text.color);
Flex::row()
- .with_child(if kind.is_dir() {
+ .with_child(if let Some(icon) = &details.icon {
+ Svg::new(icon.to_string())
+ .with_color(style.icon_color)
+ .constrained()
+ .with_max_width(style.icon_size)
+ .with_max_height(style.icon_size)
+ .aligned()
+ .constrained()
+ .with_width(style.icon_size)
+ } else if kind.is_dir() {
if details.is_expanded {
- Svg::new("icons/chevron_down_8.svg").with_color(style.icon_color)
+ Svg::new("icons/chevron_down_8.svg").with_color(style.chevron_color)
} else {
- Svg::new("icons/chevron_right_8.svg").with_color(style.icon_color)
+ Svg::new("icons/chevron_right_8.svg").with_color(style.chevron_color)
}
.constrained()
- .with_max_width(style.directory_icon_size)
- .with_max_height(style.directory_icon_size)
+ .with_max_width(style.chevron_size)
+ .with_max_height(style.chevron_size)
.aligned()
.constrained()
- .with_width(style.directory_icon_size)
+ .with_width(style.chevron_size)
} else {
- if let Some(icon) = &details.icon {
- Svg::new(icon.to_string())
- .with_color(style.icon_color)
- .constrained()
- .with_max_width(style.file_icon_size)
- .with_max_height(style.file_icon_size)
- .aligned()
- .constrained()
- .with_width(style.file_icon_size)
- } else {
- Empty::new()
- .constrained()
- .with_max_width(style.directory_icon_size)
- .with_max_height(style.directory_icon_size)
- .aligned()
- .constrained()
- .with_width(style.directory_icon_size)
- }
+ Empty::new()
+ .constrained()
+ .with_max_width(style.chevron_size)
+ .with_max_height(style.chevron_size)
+ .aligned()
+ .constrained()
+ .with_width(style.chevron_size)
})
.with_child(if show_editor && editor.is_some() {
ChildView::new(editor.as_ref().unwrap(), cx)
@@ -2613,7 +2610,7 @@ mod tests {
theme::init((), cx);
language::init(cx);
editor::init_settings(cx);
- crate::init(cx);
+ crate::init((), cx);
workspace::init_settings(cx);
Project::init_settings(cx);
});
@@ -2628,7 +2625,7 @@ mod tests {
language::init(cx);
editor::init(cx);
pane::init(cx);
- crate::init(cx);
+ crate::init((), cx);
workspace::init(app_state.clone(), cx);
Project::init_settings(cx);
});
diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs
index 53a0629198..29c0d9ce8e 100644
--- a/crates/theme/src/theme.rs
+++ b/crates/theme/src/theme.rs
@@ -480,9 +480,10 @@ pub struct ProjectPanelEntry {
#[serde(flatten)]
pub container: ContainerStyle,
pub text: TextStyle,
+ pub icon_size: f32,
pub icon_color: Color,
- pub directory_icon_size: f32,
- pub file_icon_size: f32,
+ pub chevron_color: Color,
+ pub chevron_size: f32,
pub icon_spacing: f32,
pub status: EntryStatus,
}
diff --git a/crates/zed/src/languages/toml/config.toml b/crates/zed/src/languages/toml/config.toml
index 5a3fc9d8b8..4e89f5cabd 100644
--- a/crates/zed/src/languages/toml/config.toml
+++ b/crates/zed/src/languages/toml/config.toml
@@ -1,6 +1,5 @@
name = "TOML"
path_suffixes = ["toml"]
-icon_path = "icons/radix/gear.svg"
line_comment = "# "
autoclose_before = ",]}"
brackets = [
diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs
index ccf381b5b1..e85113d57d 100644
--- a/crates/zed/src/main.rs
+++ b/crates/zed/src/main.rs
@@ -154,7 +154,7 @@ fn main() {
file_finder::init(cx);
outline::init(cx);
project_symbols::init(cx);
- project_panel::init(cx);
+ project_panel::init(Assets, cx);
diagnostics::init(cx);
search::init(cx);
vector_store::init(fs.clone(), http.clone(), languages.clone(), cx);
diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs
index 9dffc644ae..6bbba0bd02 100644
--- a/crates/zed/src/zed.rs
+++ b/crates/zed/src/zed.rs
@@ -2334,7 +2334,7 @@ mod tests {
editor::init(cx);
project_panel::init_settings(cx);
pane::init(cx);
- project_panel::init(cx);
+ project_panel::init((), cx);
terminal_view::init(cx);
ai::init(cx);
app_state
diff --git a/styles/src/style_tree/project_panel.ts b/styles/src/style_tree/project_panel.ts
index c2719e935e..c3e82de8b0 100644
--- a/styles/src/style_tree/project_panel.ts
+++ b/styles/src/style_tree/project_panel.ts
@@ -46,9 +46,10 @@ export default function project_panel(): any {
const base_properties = {
height: 22,
background: background(theme.middle),
- icon_color: foreground(theme.middle, "variant"),
- directory_icon_size: 7,
- file_icon_size: 14,
+ chevron_color: foreground(theme.middle, "variant"),
+ icon_color: foreground(theme.middle, "active"),
+ chevron_size: 7,
+ icon_size: 14,
icon_spacing: 5,
text: text(theme.middle, "sans", "variant", { size: "sm" }),
status: {