diff --git a/assets/settings/default.json b/assets/settings/default.json index 2e6361ce7e..9b8dce10d8 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -128,6 +128,10 @@ // 4. Save when idle for a certain amount of time: // "autosave": { "after_delay": {"milliseconds": 500} }, "autosave": "off", + // Color tab titles based on the git status of the buffer. + "tabs": { + "git_status": false + }, // Whether or not to remove any trailing whitespace from lines of a buffer // before saving it. "remove_trailing_whitespace_on_save": true, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index cdf3cadf59..191fe6332a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -350,6 +350,7 @@ pub struct Tab { pub icon_close_active: Color, pub icon_dirty: Color, pub icon_conflict: Color, + pub git: GitProjectStatus, } #[derive(Clone, Deserialize, Default, JsonSchema)] @@ -722,12 +723,12 @@ pub struct Scrollbar { pub thumb: ContainerStyle, pub width: f32, pub min_height_factor: f32, - pub git: GitDiffColors, + pub git: FileGitDiffColors, pub selections: Color, } #[derive(Clone, Deserialize, Default, JsonSchema)] -pub struct GitDiffColors { +pub struct FileGitDiffColors { pub inserted: Color, pub modified: Color, pub deleted: Color, diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 0c7a478e31..c4b9d8e879 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -10,6 +10,9 @@ use gpui::{ ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use project::{Project, ProjectEntryId, ProjectPath}; +use schemars::JsonSchema; +use serde_derive::{Deserialize, Serialize}; +use settings::Setting; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -27,6 +30,30 @@ use std::{ }; use theme::Theme; +#[derive(Deserialize)] +pub struct ItemSettings { + pub git_status: bool, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct ItemSettingsContent { + git_status: Option, +} + +impl Setting for ItemSettings { + const KEY: Option<&'static str> = Some("tabs"); + + type FileContent = ItemSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result { + Self::load_via_json_merge(default_value, user_values) + } +} + #[derive(Eq, PartialEq, Hash, Debug)] pub enum ItemEvent { CloseItem, diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 8e6e107488..373c30fb52 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -3,14 +3,16 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; pub use crate::toolbar::Toolbar; use crate::{ - item::WeakItemHandle, notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile, - NewSearch, ToggleZoom, Workspace, WorkspaceSettings, + item::{ItemSettings, WeakItemHandle}, + notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile, NewSearch, ToggleZoom, + Workspace, WorkspaceSettings, }; use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use context_menu::{ContextMenu, ContextMenuItem}; use drag_and_drop::{DragAndDrop, Draggable}; use dragged_item_receiver::dragged_item_receiver; +use fs::repository::GitFileStatus; use futures::StreamExt; use gpui::{ actions, @@ -866,6 +868,7 @@ impl Pane { .paths_by_item .get(&item.id()) .and_then(|(_, abs_path)| abs_path.clone()); + self.nav_history .0 .borrow_mut() @@ -1157,6 +1160,11 @@ impl Pane { .zip(self.tab_details(cx)) .enumerate() { + let git_status = item + .project_path(cx) + .and_then(|path| self.project.read(cx).entry_for_path(&path, cx)) + .and_then(|entry| entry.git_status()); + let detail = if detail == 0 { None } else { Some(detail) }; let tab_active = ix == self.active_item_index; @@ -1174,9 +1182,21 @@ impl Pane { let tab_tooltip_text = item.tab_tooltip_text(cx).map(|text| text.into_owned()); + let mut tab_style = theme + .workspace + .tab_bar + .tab_style(pane_active, tab_active) + .clone(); + let should_show_status = settings::get::(cx).git_status; + if should_show_status && git_status != None { + tab_style.label.text.color = match git_status.unwrap() { + GitFileStatus::Added => tab_style.git.inserted, + GitFileStatus::Modified => tab_style.git.modified, + GitFileStatus::Conflict => tab_style.git.conflict, + }; + } + move |mouse_state, cx| { - let tab_style = - theme.workspace.tab_bar.tab_style(pane_active, tab_active); let hovered = mouse_state.hovered(); enum Tab {} @@ -1188,7 +1208,7 @@ impl Pane { ix == 0, detail, hovered, - tab_style, + &tab_style, cx, ) }) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a23efdf628..3e62af8ea6 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -203,6 +203,7 @@ pub type WorkspaceId = i64; pub fn init_settings(cx: &mut AppContext) { settings::register::(cx); + settings::register::(cx); } pub fn init(app_state: Arc, cx: &mut AppContext) { diff --git a/styles/src/style_tree/tab_bar.ts b/styles/src/style_tree/tab_bar.ts index 29769f9bae..e7b04246c4 100644 --- a/styles/src/style_tree/tab_bar.ts +++ b/styles/src/style_tree/tab_bar.ts @@ -6,6 +6,8 @@ import { useTheme } from "../common" export default function tab_bar(): any { const theme = useTheme() + const { is_light } = theme + const height = 32 const active_layer = theme.highest @@ -38,6 +40,18 @@ export default function tab_bar(): any { icon_conflict: foreground(layer, "warning"), icon_dirty: foreground(layer, "accent"), + git: { + modified: is_light + ? theme.ramps.yellow(0.6).hex() + : theme.ramps.yellow(0.5).hex(), + inserted: is_light + ? theme.ramps.green(0.45).hex() + : theme.ramps.green(0.5).hex(), + conflict: is_light + ? theme.ramps.red(0.6).hex() + : theme.ramps.red(0.5).hex(), + }, + // When two tabs of the same name are open, a label appears next to them description: { margin: { left: 8 },