Display file icons in tabs (#14523)

Marshall Bowers created

This PR adds support for displaying file icons in tabs.

The `tabs.file_icons` setting controls whether the icons are displayed:

```json
{
  "tabs": {
    "file_icons": false
  }
}
```

This setting defaults to `true`.

<img width="1566" alt="Screenshot 2024-07-15 at 6 17 26 PM"
src="https://github.com/user-attachments/assets/86dfc8c9-764c-453d-95e4-2ec95d6fe715">

<img width="1566" alt="Screenshot 2024-07-15 at 6 24 26 PM"
src="https://github.com/user-attachments/assets/4b4e8489-49d3-41bf-b4cb-59365bdd3e9d">

Release Notes:

- Added file icons to buffer tabs
([#12138](https://github.com/zed-industries/zed/issues/12138)).
- If desired, these icons can be removed using `"tabs": { "file_icons":
false }`.

Change summary

Cargo.lock                   |  1 +
assets/settings/default.json |  4 +++-
crates/workspace/Cargo.toml  |  1 +
crates/workspace/src/item.rs |  5 +++++
crates/workspace/src/pane.rs | 22 +++++++++++++++++++++-
docs/src/configuring-zed.md  |  7 +++++++
6 files changed, 38 insertions(+), 2 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -13335,6 +13335,7 @@ dependencies = [
  "derive_more",
  "dev_server_projects",
  "env_logger",
+ "file_icons",
  "fs",
  "futures 0.3.28",
  "gpui",

assets/settings/default.json 🔗

@@ -431,7 +431,9 @@
     // Show git status colors in the editor tabs.
     "git_status": false,
     // Position of the close button on the editor tabs.
-    "close_position": "right"
+    "close_position": "right",
+    // Whether to show the file icon for a tab.
+    "file_icons": true
   },
   // Settings related to preview tabs.
   "preview_tabs": {

crates/workspace/Cargo.toml 🔗

@@ -36,6 +36,7 @@ clock.workspace = true
 collections.workspace = true
 db.workspace = true
 derive_more.workspace = true
+file_icons.workspace = true
 fs.workspace = true
 futures.workspace = true
 gpui.workspace = true

crates/workspace/src/item.rs 🔗

@@ -39,6 +39,7 @@ pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200);
 pub struct ItemSettings {
     pub git_status: bool,
     pub close_position: ClosePosition,
+    pub file_icons: bool,
 }
 
 #[derive(Deserialize)]
@@ -75,6 +76,10 @@ pub struct ItemSettingsContent {
     ///
     /// Default: right
     close_position: Option<ClosePosition>,
+    /// Whether to show the file icon for a tab.
+    ///
+    /// Default: true
+    file_icons: Option<bool>,
 }
 
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]

crates/workspace/src/pane.rs 🔗

@@ -10,6 +10,7 @@ use crate::{
 };
 use anyhow::Result;
 use collections::{BTreeSet, HashMap, HashSet, VecDeque};
+use file_icons::FileIcons;
 use futures::{stream::FuturesUnordered, StreamExt};
 use gpui::{
     actions, anchored, deferred, impl_actions, prelude::*, Action, AnchorCorner, AnyElement,
@@ -1595,6 +1596,14 @@ impl Pane {
         let is_last_item = ix == self.items.len() - 1;
         let position_relative_to_active_item = ix.cmp(&self.active_item_index);
 
+        let file_icon = ItemSettings::get_global(cx)
+            .file_icons
+            .then(|| {
+                item.project_path(cx)
+                    .and_then(|path| FileIcons::get_icon(path.path.as_ref(), cx))
+            })
+            .flatten();
+
         let tab = Tab::new(ix)
             .position(if is_first_item {
                 TabPosition::First
@@ -1663,7 +1672,18 @@ impl Pane {
             .when_some(item.tab_tooltip_text(cx), |tab, text| {
                 tab.tooltip(move |cx| Tooltip::text(text.clone(), cx))
             })
-            .start_slot::<Indicator>(indicator)
+            .map(|tab| match indicator {
+                Some(indicator) => tab.start_slot(indicator),
+                None => tab.start_slot::<Icon>(file_icon.map(|icon| {
+                    Icon::from_path(icon.to_string())
+                        .size(IconSize::XSmall)
+                        .color(if is_active {
+                            Color::Default
+                        } else {
+                            Color::Muted
+                        })
+                })),
+            })
             .end_slot(
                 IconButton::new("close tab", IconName::Close)
                     .shape(IconButtonShape::Square)

docs/src/configuring-zed.md 🔗

@@ -435,6 +435,7 @@ List of `string` values
 ```json
 "tabs": {
   "close_position": "right",
+  "file_icons": true,
   "git_status": false
 },
 ```
@@ -463,6 +464,12 @@ List of `string` values
 }
 ```
 
+### File Icons
+
+- Description: Whether to show the file icon for a tab.
+- Setting: `file_icons`
+- Default: `true`
+
 ### Git Status
 
 - Description: Whether or not to show Git file status in tab.