From 0a504f5f301a9d45c00fa13aa8673bfdb1c4a134 Mon Sep 17 00:00:00 2001 From: nilskch Date: Sun, 1 Dec 2024 13:33:07 +0000 Subject: [PATCH 1/2] show error and warning indicators in tabs --- assets/settings/default.json | 12 ++- crates/workspace/src/item.rs | 15 ++++ crates/workspace/src/pane.rs | 140 ++++++++++++++++++++++++++--------- 3 files changed, 131 insertions(+), 36 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index b844be7fa24262..5516299ae6e61c 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -565,7 +565,17 @@ // "History" // 2. Activate the neighbour tab (prefers the right one, if present) // "Neighbour" - "activate_on_close": "history" + "activate_on_close": "history", + /// Which files containing diagnostic errors/warnings to mark in the tabs. + /// This setting can take the following three values: + /// + /// 1. Do not mark any files: + /// "off" + /// 2. Only mark files with errors: + /// "errors" + /// 3. Mark files with errors and warnings: + /// "all" + "show_diagnostics": "all" }, // Settings related to preview tabs. "preview_tabs": { diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 40d92666a0bc3a..45949a8a8537f0 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -42,6 +42,7 @@ pub struct ItemSettings { pub close_position: ClosePosition, pub activate_on_close: ActivateOnClose, pub file_icons: bool, + pub show_diagnostics: ShowDiagnostics, } #[derive(Deserialize)] @@ -59,6 +60,15 @@ pub enum ClosePosition { Right, } +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ShowDiagnostics { + Off, + Errors, + #[default] + All, +} + #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum ActivateOnClose { @@ -85,6 +95,11 @@ pub struct ItemSettingsContent { /// /// Default: history pub activate_on_close: Option, + /// Which files containing diagnostic errors/warnings to mark in the tabs. + /// This setting can take the following three values: + /// + /// Default: all + show_diagnostics: Option, } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 66db71553f084d..c88fe467f49a5a 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1,7 +1,7 @@ use crate::{ item::{ ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings, - TabContentParams, WeakItemHandle, + ShowDiagnostics, TabContentParams, WeakItemHandle, }, move_item, notifications::NotifyResultExt, @@ -13,7 +13,6 @@ use crate::{ use anyhow::Result; use collections::{BTreeSet, HashMap, HashSet, VecDeque}; use futures::{stream::FuturesUnordered, StreamExt}; -use git::repository::GitFileStatus; use gpui::{ actions, anchored, deferred, impl_actions, prelude::*, Action, AnchorCorner, AnyElement, AppContext, AsyncWindowContext, ClickEvent, ClipboardItem, Div, DragMoveEvent, EntityId, @@ -23,6 +22,7 @@ use gpui::{ WindowContext, }; use itertools::Itertools; +use language::DiagnosticSeverity; use parking_lot::Mutex; use project::{Project, ProjectEntryId, ProjectPath, WorktreeId}; use serde::Deserialize; @@ -39,10 +39,10 @@ use std::{ }, }; use theme::ThemeSettings; - use ui::{ - prelude::*, right_click_menu, ButtonSize, Color, IconButton, IconButtonShape, IconName, - IconSize, Indicator, Label, PopoverMenu, PopoverMenuHandle, Tab, TabBar, TabPosition, Tooltip, + prelude::*, right_click_menu, ButtonSize, Color, DecoratedIcon, IconButton, IconButtonShape, + IconDecoration, IconDecorationKind, IconName, IconSize, Indicator, Label, PopoverMenu, + PopoverMenuHandle, Tab, TabBar, TabPosition, Tooltip, }; use ui::{v_flex, ContextMenu}; use util::{debug_panic, maybe, truncate_and_remove_front, ResultExt}; @@ -305,6 +305,7 @@ pub struct Pane { pub new_item_context_menu_handle: PopoverMenuHandle, pub split_item_context_menu_handle: PopoverMenuHandle, pinned_tab_count: usize, + diagnostics: HashMap, } pub struct ActivationHistoryEntry { @@ -381,6 +382,7 @@ impl Pane { cx.on_focus_in(&focus_handle, Pane::focus_in), cx.on_focus_out(&focus_handle, Pane::focus_out), cx.observe_global::(Self::settings_changed), + cx.subscribe(&project, Self::project_events), ]; let handle = cx.view().downgrade(); @@ -504,6 +506,7 @@ impl Pane { split_item_context_menu_handle: Default::default(), new_item_context_menu_handle: Default::default(), pinned_tab_count: 0, + diagnostics: Default::default(), } } @@ -598,6 +601,47 @@ impl Pane { cx.notify(); } + fn project_events( + this: &mut Pane, + _project: Model, + event: &project::Event, + cx: &mut ViewContext, + ) { + match event { + project::Event::DiskBasedDiagnosticsFinished { .. } + | project::Event::DiagnosticsUpdated { .. } => { + if ItemSettings::get_global(cx).show_diagnostics != ShowDiagnostics::Off { + this.update_diagnostics(cx); + cx.notify(); + } + } + _ => {} + } + } + + fn update_diagnostics(&mut self, cx: &mut ViewContext) { + let show_diagnostics = ItemSettings::get_global(cx).show_diagnostics; + self.diagnostics = if show_diagnostics != ShowDiagnostics::Off { + self.project + .read(cx) + .diagnostic_summaries(false, cx) + .filter_map(|(project_path, _, diagnostic_summary)| { + if diagnostic_summary.error_count > 0 { + Some((project_path, DiagnosticSeverity::ERROR)) + } else if diagnostic_summary.warning_count > 0 + && show_diagnostics != ShowDiagnostics::Errors + { + Some((project_path, DiagnosticSeverity::WARNING)) + } else { + None + } + }) + .collect::>() + } else { + Default::default() + } + } + fn settings_changed(&mut self, cx: &mut ViewContext) { if let Some(display_nav_history_buttons) = self.display_nav_history_buttons.as_mut() { *display_nav_history_buttons = TabBarSettings::get_global(cx).show_nav_history_buttons; @@ -605,6 +649,7 @@ impl Pane { if !PreviewTabsSettings::get_global(cx).enabled { self.preview_item_id = None; } + self.update_diagnostics(cx); cx.notify(); } @@ -1841,23 +1886,6 @@ impl Pane { } } - pub fn git_aware_icon_color( - git_status: Option, - ignored: bool, - selected: bool, - ) -> Color { - if ignored { - Color::Ignored - } else { - match git_status { - Some(GitFileStatus::Added) => Color::Created, - Some(GitFileStatus::Modified) => Color::Modified, - Some(GitFileStatus::Conflict) => Color::Conflict, - None => Self::icon_color(selected), - } - } - } - fn toggle_pin_tab(&mut self, _: &TogglePinTab, cx: &mut ViewContext<'_, Self>) { if self.items.is_empty() { return; @@ -1921,8 +1949,6 @@ impl Pane { focus_handle: &FocusHandle, cx: &mut ViewContext<'_, Pane>, ) -> impl IntoElement { - let project_path = item.project_path(cx); - let is_active = ix == self.active_item_index; let is_preview = self .preview_item_id @@ -1938,19 +1964,57 @@ impl Pane { cx, ); - let icon_color = if ItemSettings::get_global(cx).git_status { - project_path - .as_ref() - .and_then(|path| self.project.read(cx).entry_for_path(path, cx)) - .map(|entry| { - Self::git_aware_icon_color(entry.git_status, entry.is_ignored, is_active) - }) - .unwrap_or_else(|| Self::icon_color(is_active)) + let item_diagnostic = item + .project_path(cx) + .map_or(None, |project_path| self.diagnostics.get(&project_path)); + + let decorated_icon = item_diagnostic.map_or(None, |diagnostic| { + let icon = match item.tab_icon(cx) { + Some(icon) => icon, + None => return None, + }; + + let knockout_item_color = if is_active { + cx.theme().colors().tab_active_background + } else { + cx.theme().colors().tab_bar_background + }; + + let (icon_decoration, icon_color) = if matches!(diagnostic, &DiagnosticSeverity::ERROR) + { + (IconDecorationKind::X, Color::Error) + } else { + (IconDecorationKind::Triangle, Color::Warning) + }; + + Some(DecoratedIcon::new( + icon.size(IconSize::Small).color(Color::Muted), + Some( + IconDecoration::new(icon_decoration, knockout_item_color, cx) + .color(icon_color.color(cx)) + .position(Point { + x: px(-2.), + y: px(-2.), + }), + ), + )) + }); + + let icon = if decorated_icon.is_none() { + match item_diagnostic { + Some(&DiagnosticSeverity::ERROR) => { + Some(Icon::new(IconName::X).color(Color::Error)) + } + Some(&DiagnosticSeverity::WARNING) => { + Some(Icon::new(IconName::Triangle).color(Color::Warning)) + } + _ => item.tab_icon(cx).map(|icon| icon.color(Color::Muted)), + } + .map(|icon| icon.size(IconSize::Small)) } else { - Self::icon_color(is_active) + None }; - let icon = item.tab_icon(cx); let close_side = &ItemSettings::get_global(cx).close_position; let indicator = render_item_indicator(item.boxed_clone(), cx); let item_id = item.item_id(); @@ -2076,7 +2140,13 @@ impl Pane { .child( h_flex() .gap_1() - .children(icon.map(|icon| icon.size(IconSize::Small).color(icon_color))) + .child(if let Some(decorated_icon) = decorated_icon { + div().child(decorated_icon.into_any_element()) + } else if let Some(icon) = icon { + div().child(icon.into_any_element()) + } else { + div() + }) .child(label), ); From b1eb9c6574e1d80310bbd2aa6cc3e639f382a86f Mon Sep 17 00:00:00 2001 From: nilskch Date: Sun, 1 Dec 2024 13:43:51 +0000 Subject: [PATCH 2/2] fix cargo machete --- Cargo.lock | 1 - crates/workspace/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7768dac710feff..43adac76252e40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15283,7 +15283,6 @@ dependencies = [ "env_logger 0.11.5", "fs", "futures 0.3.31", - "git", "gpui", "http_client", "itertools 0.13.0", diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 1fa4db2af8a744..3b17ed8dabd387 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -38,7 +38,6 @@ db.workspace = true derive_more.workspace = true fs.workspace = true futures.workspace = true -git.workspace = true gpui.workspace = true http_client.workspace = true itertools.workspace = true