Cargo.lock 🔗
@@ -7356,6 +7356,7 @@ dependencies = [
"db",
"editor",
"feature_flags",
+ "file_icons",
"futures 0.3.31",
"fuzzy",
"git",
Jongchan and Danilo Leal created
Closes https://github.com/zed-industries/zed/discussions/49740
Adds optional file and folder icons to the Git panel so its file list is
easier to scan, especially in larger repositories.
- add folder icons for Git panel entries
- add Git panel settings for file and folder icon visibility
---
Release Notes:
- Made the Git Panel aware of icon themes.
- Added the ability to render file type icons in the Git panel.
---------
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Cargo.lock | 1
assets/settings/default.json | 8 +
crates/git_ui/Cargo.toml | 1
crates/git_ui/src/git_panel.rs | 81 ++++++++++++++++--
crates/git_ui/src/git_panel_settings.rs | 4
crates/settings_content/src/settings_content.rs | 11 ++
crates/settings_ui/src/page_data.rs | 38 ++++++++
7 files changed, 132 insertions(+), 12 deletions(-)
@@ -7356,6 +7356,7 @@ dependencies = [
"db",
"editor",
"feature_flags",
+ "file_icons",
"futures 0.3.31",
"fuzzy",
"git",
@@ -898,6 +898,14 @@
// Choices: label_color, icon
// Default: icon
"status_style": "icon",
+ // Whether to show file icons in the git panel.
+ //
+ // Default: false
+ "file_icons": false,
+ // Whether to show folder icons or chevrons for directories in the git panel.
+ //
+ // Default: true
+ "folder_icons": true,
// What branch name to use if `init.defaultBranch` is not set
//
// Default: main
@@ -26,6 +26,7 @@ collections.workspace = true
component.workspace = true
db.workspace = true
editor.workspace = true
+file_icons.workspace = true
futures.workspace = true
feature_flags.workspace = true
fuzzy.workspace = true
@@ -20,6 +20,7 @@ use editor::{
actions::ExpandAllDiffHunks,
};
use editor::{EditorStyle, RewrapOptions};
+use file_icons::FileIcons;
use futures::StreamExt as _;
use git::commit::ParsedCommitMessage;
use git::repository::{
@@ -714,11 +715,16 @@ impl GitPanel {
let mut was_sort_by_path = GitPanelSettings::get_global(cx).sort_by_path;
let mut was_tree_view = GitPanelSettings::get_global(cx).tree_view;
+ let mut was_file_icons = GitPanelSettings::get_global(cx).file_icons;
+ let mut was_folder_icons = GitPanelSettings::get_global(cx).folder_icons;
let mut was_diff_stats = GitPanelSettings::get_global(cx).diff_stats;
cx.observe_global_in::<SettingsStore>(window, move |this, window, cx| {
- let sort_by_path = GitPanelSettings::get_global(cx).sort_by_path;
- let tree_view = GitPanelSettings::get_global(cx).tree_view;
- let diff_stats = GitPanelSettings::get_global(cx).diff_stats;
+ let settings = GitPanelSettings::get_global(cx);
+ let sort_by_path = settings.sort_by_path;
+ let tree_view = settings.tree_view;
+ let file_icons = settings.file_icons;
+ let folder_icons = settings.folder_icons;
+ let diff_stats = settings.diff_stats;
if tree_view != was_tree_view {
this.view_mode = GitPanelViewMode::from_settings(cx);
}
@@ -731,12 +737,22 @@ impl GitPanel {
if (diff_stats != was_diff_stats) || update_entries {
this.update_visible_entries(window, cx);
}
+ if file_icons != was_file_icons || folder_icons != was_folder_icons {
+ cx.notify();
+ }
was_sort_by_path = sort_by_path;
was_tree_view = tree_view;
+ was_file_icons = file_icons;
+ was_folder_icons = folder_icons;
was_diff_stats = diff_stats;
})
.detach();
+ cx.observe_global::<FileIcons>(|_, cx| {
+ cx.notify();
+ })
+ .detach();
+
// just to let us render a placeholder editor.
// Once the active git repo is set, this buffer will be replaced.
let temporary_buffer = cx.new(|cx| Buffer::local("", cx));
@@ -5020,15 +5036,21 @@ impl GitPanel {
window: &Window,
cx: &Context<Self>,
) -> AnyElement {
- let tree_view = GitPanelSettings::get_global(cx).tree_view;
+ let settings = GitPanelSettings::get_global(cx);
+ let tree_view = settings.tree_view;
let path_style = self.project.read(cx).path_style(cx);
let git_path_style = ProjectSettings::get_global(cx).git.path_style;
let display_name = entry.display_name(path_style);
let selected = self.selected_entry == Some(ix);
let marked = self.marked_entries.contains(&ix);
- let status_style = GitPanelSettings::get_global(cx).status_style;
+ let status_style = settings.status_style;
let status = entry.status;
+ let file_icon = if settings.file_icons {
+ FileIcons::get_icon(entry.repo_path.as_std_path(), cx)
+ } else {
+ None
+ };
let has_conflict = status.is_conflicted();
let is_modified = status.is_modified();
@@ -5105,6 +5127,21 @@ impl GitPanel {
.min_w_0()
.flex_1()
.gap_1()
+ .when(settings.file_icons, |this| {
+ this.child(
+ file_icon
+ .map(|file_icon| {
+ Icon::from_path(file_icon)
+ .size(IconSize::Small)
+ .color(Color::Muted)
+ })
+ .unwrap_or_else(|| {
+ Icon::new(IconName::File)
+ .size(IconSize::Small)
+ .color(Color::Muted)
+ }),
+ )
+ })
.child(git_status_icon(status))
.map(|this| {
if tree_view {
@@ -5273,10 +5310,24 @@ impl GitPanel {
)
};
- let folder_icon = if entry.expanded {
- IconName::FolderOpen
+ let settings = GitPanelSettings::get_global(cx);
+ let folder_icon = if settings.folder_icons {
+ FileIcons::get_folder_icon(entry.expanded, entry.key.path.as_std_path(), cx)
+ } else {
+ FileIcons::get_chevron_icon(entry.expanded, cx)
+ };
+ let fallback_folder_icon = if settings.folder_icons {
+ if entry.expanded {
+ IconName::FolderOpen
+ } else {
+ IconName::Folder
+ }
} else {
- IconName::Folder
+ if entry.expanded {
+ IconName::ChevronDown
+ } else {
+ IconName::ChevronRight
+ }
};
let stage_status = if let Some(repo) = &self.active_repository {
@@ -5299,9 +5350,17 @@ impl GitPanel {
.gap_1()
.pl(px(entry.depth as f32 * TREE_INDENT))
.child(
- Icon::new(folder_icon)
- .size(IconSize::Small)
- .color(Color::Muted),
+ folder_icon
+ .map(|folder_icon| {
+ Icon::from_path(folder_icon)
+ .size(IconSize::Small)
+ .color(Color::Muted)
+ })
+ .unwrap_or_else(|| {
+ Icon::new(fallback_folder_icon)
+ .size(IconSize::Small)
+ .color(Color::Muted)
+ }),
)
.child(self.entry_label(entry.name.clone(), label_color).truncate());
@@ -20,6 +20,8 @@ pub struct GitPanelSettings {
pub dock: DockPosition,
pub default_width: Pixels,
pub status_style: StatusStyle,
+ pub file_icons: bool,
+ pub folder_icons: bool,
pub scrollbar: ScrollbarSettings,
pub fallback_branch_name: String,
pub sort_by_path: bool,
@@ -52,6 +54,8 @@ impl Settings for GitPanelSettings {
dock: git_panel.dock.unwrap().into(),
default_width: px(git_panel.default_width.unwrap()),
status_style: git_panel.status_style.unwrap(),
+ file_icons: git_panel.file_icons.unwrap(),
+ folder_icons: git_panel.folder_icons.unwrap(),
scrollbar: ScrollbarSettings {
show: git_panel.scrollbar.unwrap().show.map(Into::into),
},
@@ -593,6 +593,17 @@ pub struct GitPanelSettingsContent {
///
/// Default: icon
pub status_style: Option<StatusStyle>,
+
+ /// Whether to show file icons in the git panel.
+ ///
+ /// Default: false
+ pub file_icons: Option<bool>,
+
+ /// Whether to show folder icons or chevrons for directories in the git panel.
+ ///
+ /// Default: true
+ pub folder_icons: Option<bool>,
+
/// How and when the scrollbar should be displayed.
///
/// Default: inherits editor scrollbar settings
@@ -5048,7 +5048,7 @@ fn panels_page() -> SettingsPage {
]
}
- fn git_panel_section() -> [SettingsPageItem; 11] {
+ fn git_panel_section() -> [SettingsPageItem; 13] {
[
SettingsPageItem::SectionHeader("Git Panel"),
SettingsPageItem::SettingItem(SettingItem {
@@ -5190,6 +5190,42 @@ fn panels_page() -> SettingsPage {
metadata: None,
files: USER,
}),
+ SettingsPageItem::SettingItem(SettingItem {
+ title: "File Icons",
+ description: "Show file icons next to the Git status icon.",
+ field: Box::new(SettingField {
+ json_path: Some("git_panel.file_icons"),
+ pick: |settings_content| {
+ settings_content.git_panel.as_ref()?.file_icons.as_ref()
+ },
+ write: |settings_content, value| {
+ settings_content
+ .git_panel
+ .get_or_insert_default()
+ .file_icons = value;
+ },
+ }),
+ metadata: None,
+ files: USER,
+ }),
+ SettingsPageItem::SettingItem(SettingItem {
+ title: "Folder Icons",
+ description: "Whether to show folder icons or chevrons for directories in the git panel.",
+ field: Box::new(SettingField {
+ json_path: Some("git_panel.folder_icons"),
+ pick: |settings_content| {
+ settings_content.git_panel.as_ref()?.folder_icons.as_ref()
+ },
+ write: |settings_content, value| {
+ settings_content
+ .git_panel
+ .get_or_insert_default()
+ .folder_icons = value;
+ },
+ }),
+ metadata: None,
+ files: USER,
+ }),
SettingsPageItem::SettingItem(SettingItem {
title: "Diff Stats",
description: "Whether to show the addition/deletion change count next to each file in the Git panel.",