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 @@ + + + + anchor_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + archive_dark + Created with Sketch. + + + + + + + \ 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 @@ + + + + book_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + camera_dark + Created with Sketch. + + + + + + + \ 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 @@ + + + + code_dark + Created with Sketch. + + + + + + + \ 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 @@ + + + + database_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + eslint_dark + Created with Sketch. + + + + + \ 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 @@ + + + + file_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + folder-open_dark + Created with Sketch. + + + + + + + \ 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 @@ + + + + folder_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + git_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + hash_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + html_dark + Created with Sketch. + + + + + + + \ 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 @@ + + + + image_dark + Created with Sketch. + + + + + + + \ 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 @@ + + + + info_dark + Created with Sketch. + + + + + + + \ 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 @@ + + + + lock_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + package_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + prettier_dark + Created with Sketch. + + + + + + + + + + + + + + + + + \ 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 @@ + + + + settings_dark + Created with Sketch. + + + + + + \ 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 @@ + + + + terminal_dark + Created with Sketch. + + + + \ 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: {