From 8c202b3b09083e0b5430fce3631734de6112bbc0 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 13 Feb 2025 15:12:23 -0500 Subject: [PATCH] editor: Update git hunk indicators to show staging status when hunk is expanded (#24818) - Update git hunk indicators to show staging status when hunk is expanded - Updates uses of status colors to the new version control theme colors - Adds new version control theme colors to included themes Before: ![CleanShot 2025-02-13 at 14 42 48@2x](https://github.com/user-attachments/assets/ccca147e-0de2-4e69-9cd4-01b010bf06d0) After: ![CleanShot 2025-02-13 at 14 42 04@2x](https://github.com/user-attachments/assets/1ab49174-bde5-43b2-83c5-d217533df49a) (Colors here are from before theme colors were added) Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: cole-miller --- assets/themes/gruvbox/gruvbox.json | 60 +++++++++++++++ assets/themes/one/one.json | 10 +++ crates/editor/src/editor.rs | 20 ++--- crates/editor/src/element.rs | 117 ++++++++++++++++------------- crates/gpui/src/color.rs | 19 +++++ crates/theme/src/default_colors.rs | 6 +- 6 files changed, 167 insertions(+), 65 deletions(-) diff --git a/assets/themes/gruvbox/gruvbox.json b/assets/themes/gruvbox/gruvbox.json index 51085b61ca..fb6bef7dc2 100644 --- a/assets/themes/gruvbox/gruvbox.json +++ b/assets/themes/gruvbox/gruvbox.json @@ -105,6 +105,16 @@ "terminal.ansi.bright_white": "#fbf1c7ff", "terminal.ansi.dim_white": "#b0a189ff", "link_text.hover": "#83a598ff", + "version_control.added": "#b7bb26ff", + "version_control.added_background": "#b7bb2614", + "version_control.deleted": "#fb4a35ff", + "version_control.deleted_background": "#fb4a3514", + "version_control.modified": "#f9bd2fff", + "version_control.modified_background": "#f9bd2f14", + "version_control.renamed": "#83a598ff", + "version_control.conflict": "#f9bd2fff", + "version_control.conflict_background": "#f9bd2f14", + "version_control.ignored": "#998b78ff", "conflict": "#f9bd2fff", "conflict.background": "#572e10ff", "conflict.border": "#754916ff", @@ -490,6 +500,16 @@ "terminal.ansi.bright_white": "#fbf1c7ff", "terminal.ansi.dim_white": "#b0a189ff", "link_text.hover": "#83a598ff", + "version_control.added": "#b7bb26ff", + "version_control.added_background": "#b7bb2614", + "version_control.deleted": "#fb4a35ff", + "version_control.deleted_background": "#fb4a3514", + "version_control.modified": "#f9bd2fff", + "version_control.modified_background": "#f9bd2f14", + "version_control.renamed": "#83a598ff", + "version_control.conflict": "#f9bd2fff", + "version_control.conflict_background": "#f9bd2f14", + "version_control.ignored": "#998b78ff", "conflict": "#f9bd2fff", "conflict.background": "#572e10ff", "conflict.border": "#754916ff", @@ -875,6 +895,16 @@ "terminal.ansi.bright_white": "#fbf1c7ff", "terminal.ansi.dim_white": "#b0a189ff", "link_text.hover": "#83a598ff", + "version_control.added": "#b7bb26ff", + "version_control.added_background": "#b7bb2614", + "version_control.deleted": "#fb4a35ff", + "version_control.deleted_background": "#fb4a3514", + "version_control.modified": "#f9bd2fff", + "version_control.modified_background": "#f9bd2f14", + "version_control.renamed": "#83a598ff", + "version_control.conflict": "#f9bd2fff", + "version_control.conflict_background": "#f9bd2f14", + "version_control.ignored": "#998b78ff", "conflict": "#f9bd2fff", "conflict.background": "#572e10ff", "conflict.border": "#754916ff", @@ -1260,6 +1290,16 @@ "terminal.ansi.bright_white": "#282828ff", "terminal.ansi.dim_white": "#73675eff", "link_text.hover": "#0b6678ff", + "version_control.added": "#79740eff", + "version_control.added_background": "#79740e14", + "version_control.deleted": "#9d0006ff", + "version_control.deleted_background": "#9d000614", + "version_control.modified": "#b57614ff", + "version_control.modified_background": "#b5761414", + "version_control.renamed": "#076678ff", + "version_control.conflict": "#b57614ff", + "version_control.conflict_background": "#b5761414", + "version_control.ignored": "#928374ff", "conflict": "#b57615ff", "conflict.background": "#f5e2d0ff", "conflict.border": "#ebccabff", @@ -1645,6 +1685,16 @@ "terminal.ansi.bright_white": "#282828ff", "terminal.ansi.dim_white": "#73675eff", "link_text.hover": "#0b6678ff", + "version_control.added": "#79740eff", + "version_control.added_background": "#79740e14", + "version_control.deleted": "#9d0006ff", + "version_control.deleted_background": "#9d000614", + "version_control.modified": "#b57614ff", + "version_control.modified_background": "#b5761414", + "version_control.renamed": "#076678ff", + "version_control.conflict": "#b57614ff", + "version_control.conflict_background": "#b5761414", + "version_control.ignored": "#928374ff", "conflict": "#b57615ff", "conflict.background": "#f5e2d0ff", "conflict.border": "#ebccabff", @@ -2030,6 +2080,16 @@ "terminal.ansi.bright_white": "#282828ff", "terminal.ansi.dim_white": "#73675eff", "link_text.hover": "#0b6678ff", + "version_control.added": "#79740eff", + "version_control.added_background": "#79740e14", + "version_control.deleted": "#9d0006ff", + "version_control.deleted_background": "#9d000614", + "version_control.modified": "#b57614ff", + "version_control.modified_background": "#b5761414", + "version_control.renamed": "#076678ff", + "version_control.conflict": "#b57614ff", + "version_control.conflict_background": "#b5761414", + "version_control.ignored": "#928374ff", "conflict": "#b57615ff", "conflict.background": "#f5e2d0ff", "conflict.border": "#ebccabff", diff --git a/assets/themes/one/one.json b/assets/themes/one/one.json index 1cac0db14b..83423840f0 100644 --- a/assets/themes/one/one.json +++ b/assets/themes/one/one.json @@ -96,6 +96,16 @@ "terminal.ansi.bright_white": "#dce0e5ff", "terminal.ansi.dim_white": "#575d65ff", "link_text.hover": "#74ade8ff", + "version_control.added": "#a1c181ff", + "version_control.added_background": "#a1c18114", + "version_control.deleted": "#d07277ff", + "version_control.deleted_background": "#d0727714", + "version_control.modified": "#dec184ff", + "version_control.modified_background": "#dec18414", + "version_control.renamed": "#74ade8ff", + "version_control.conflict": "#dec184ff", + "version_control.conflict_background": "#dec18414", + "version_control.ignored": "#878a98ff", "conflict": "#dec184ff", "conflict.background": "#dec1841a", "conflict.border": "#5d4c2fff", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a61ffbcf30..d719ee0073 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -80,13 +80,13 @@ use code_context_menus::{ use git::blame::GitBlame; use gpui::{ div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation, - AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Background, Bounds, - ClipboardEntry, ClipboardItem, Context, DispatchPhase, ElementId, Entity, EntityInputHandler, - EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global, - HighlightStyle, Hsla, InteractiveText, KeyContext, Modifiers, MouseButton, MouseDownEvent, - PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText, Subscription, - Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, - WeakEntity, WeakFocusHandle, Window, + AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, + ClipboardItem, Context, DispatchPhase, ElementId, Entity, EntityInputHandler, EventEmitter, + FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, + InteractiveText, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad, ParentElement, + Pixels, Render, SharedString, Size, Styled, StyledText, Subscription, Task, TextStyle, + TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, WeakEntity, + WeakFocusHandle, Window, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -13645,14 +13645,14 @@ impl Editor { &self, window: &mut Window, cx: &mut App, - ) -> BTreeMap { + ) -> BTreeMap { let snapshot = self.snapshot(window, cx); let mut used_highlight_orders = HashMap::default(); self.highlighted_rows .iter() .flat_map(|(_, highlighted_rows)| highlighted_rows.iter()) .fold( - BTreeMap::::new(), + BTreeMap::::new(), |mut unique_rows, highlight| { let start = highlight.range.start.to_display_point(&snapshot); let end = highlight.range.end.to_display_point(&snapshot); @@ -13669,7 +13669,7 @@ impl Editor { used_highlight_orders.entry(row).or_insert(highlight.index); if highlight.index >= *used_index { *used_index = highlight.index; - unique_rows.insert(DisplayRow(row), highlight.color.into()); + unique_rows.insert(DisplayRow(row), highlight.color); } } unique_rows diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 9c00adff34..a6993315c7 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -31,7 +31,7 @@ use file_icons::FileIcons; use git::{blame::BlameEntry, Oid}; use gpui::{ anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, pattern_slash, - point, px, quad, relative, size, svg, transparent_black, Action, AnyElement, App, + point, px, quad, relative, size, solid_color, svg, transparent_black, Action, AnyElement, App, AvailableSpace, Axis, Bounds, ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Focusable, FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Keystroke, Length, @@ -89,6 +89,7 @@ enum DisplayDiffHunk { display_row_range: Range, multi_buffer_range: Range, status: DiffHunkStatus, + contains_expanded: bool, }, } @@ -1567,6 +1568,11 @@ impl EditorElement { if hunk_display_end.column() > 0 { end_row.0 += 1; } + let start_row = hunk_display_start.row(); + let contains_expanded = snapshot + .row_infos(start_row) + .take(end_row.0 as usize - start_row.0 as usize) + .any(|row_info| row_info.diff_status.is_some()); DisplayDiffHunk::Unfolded { status: hunk.status(), diff_base_byte_range: hunk.diff_base_byte_range, @@ -1576,6 +1582,7 @@ impl EditorElement { hunk.buffer_id, hunk.buffer_range, ), + contains_expanded, } }; @@ -4341,7 +4348,7 @@ impl EditorElement { window.paint_quad(fill(Bounds { origin, size }, color)); }; - let mut current_paint: Option<(gpui::Background, Range)> = None; + let mut current_paint: Option<(Hsla, Range)> = None; for (&new_row, &new_background) in &layout.highlighted_rows { match &mut current_paint { Some((current_background, current_range)) => { @@ -4539,11 +4546,17 @@ impl EditorElement { } } - fn paint_diff_hunks(layout: &mut EditorLayout, window: &mut Window, cx: &mut App) { + fn paint_diff_hunk_gutter_indicators( + layout: &mut EditorLayout, + window: &mut Window, + cx: &mut App, + ) { if layout.display_hunks.is_empty() { return; } + let corners = Corners::all(px(0.)); + let line_height = layout.position_map.line_height; window.paint_layer(layout.gutter_hitbox.bounds, |window| { for (hunk, hitbox) in &layout.display_hunks { @@ -4557,36 +4570,41 @@ impl EditorElement { ); Some(( hunk_bounds, - cx.theme().status().modified, - Corners::all(px(0.)), + cx.theme().colors().version_control_modified.opacity(0.7), + corners, &DiffHunkSecondaryStatus::None, + false, )) } DisplayDiffHunk::Unfolded { status, display_row_range, + contains_expanded, .. } => hitbox.as_ref().map(|hunk_hitbox| match status { DiffHunkStatus::Added(secondary_status) => ( hunk_hitbox.bounds, - cx.theme().status().created, - Corners::all(px(0.)), + cx.theme().colors().version_control_added.opacity(0.7), + corners, secondary_status, + *contains_expanded, ), DiffHunkStatus::Modified(secondary_status) => ( hunk_hitbox.bounds, - cx.theme().status().modified, - Corners::all(px(0.)), + cx.theme().colors().version_control_modified.opacity(0.7), + corners, secondary_status, + *contains_expanded, ), DiffHunkStatus::Removed(secondary_status) if !display_row_range.is_empty() => { ( hunk_hitbox.bounds, - cx.theme().status().deleted, - Corners::all(px(0.)), + cx.theme().colors().version_control_deleted.opacity(0.7), + corners, secondary_status, + *contains_expanded, ) } DiffHunkStatus::Removed(secondary_status) => ( @@ -4597,23 +4615,34 @@ impl EditorElement { ), size(hunk_hitbox.size.width * px(2.), hunk_hitbox.size.height), ), - cx.theme().status().deleted, + cx.theme().colors().version_control_deleted.opacity(0.7), Corners::all(1. * line_height), secondary_status, + *contains_expanded, ), }), }; - if let Some((hunk_bounds, mut background_color, corner_radii, secondary_status)) = - hunk_to_paint + if let Some(( + hunk_bounds, + background_color, + corner_radii, + secondary_status, + contains_expanded, + )) = hunk_to_paint { - if *secondary_status != DiffHunkSecondaryStatus::None { - background_color.a *= 0.6; - } + let background = if *secondary_status != DiffHunkSecondaryStatus::None + && contains_expanded + { + pattern_slash(background_color, line_height.0 / 2.5) + } else { + solid_color(background_color) + }; + window.paint_quad(quad( hunk_bounds, corner_radii, - background_color, + background, Edges::default(), transparent_black(), )); @@ -4733,7 +4762,7 @@ impl EditorElement { ) }); if show_git_gutter { - Self::paint_diff_hunks(layout, window, cx) + Self::paint_diff_hunk_gutter_indicators(layout, window, cx) } let highlight_width = 0.275 * layout.position_map.line_height; @@ -5292,9 +5321,15 @@ impl EditorElement { end_display_row.0 -= 1; } let color = match &hunk.status() { - DiffHunkStatus::Added(_) => theme.status().created, - DiffHunkStatus::Modified(_) => theme.status().modified, - DiffHunkStatus::Removed(_) => theme.status().deleted, + DiffHunkStatus::Added(_) => { + theme.colors().version_control_added + } + DiffHunkStatus::Modified(_) => { + theme.colors().version_control_modified + } + DiffHunkStatus::Removed(_) => { + theme.colors().version_control_deleted + } }; ColoredRange { start: start_display_row, @@ -6875,39 +6910,17 @@ impl Element for EditorElement { ) }; - let (mut highlighted_rows, distinguish_unstaged_hunks) = - self.editor.update(cx, |editor, cx| { - ( - editor.highlighted_display_rows(window, cx), - editor.distinguish_unstaged_diff_hunks, - ) - }); + let mut highlighted_rows = self + .editor + .update(cx, |editor, cx| editor.highlighted_display_rows(window, cx)); for (ix, row_info) in row_infos.iter().enumerate() { let background = match row_info.diff_status { - Some(DiffHunkStatus::Added(secondary_status)) => { - let color = style.status.created_background; - match secondary_status { - DiffHunkSecondaryStatus::HasSecondaryHunk - | DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk - if distinguish_unstaged_hunks => - { - pattern_slash(color, line_height.0 / 4.0) - } - _ => color.into(), - } + Some(DiffHunkStatus::Added(_)) => { + cx.theme().colors().version_control_added_background } - Some(DiffHunkStatus::Removed(secondary_status)) => { - let color = style.status.deleted_background; - match secondary_status { - DiffHunkSecondaryStatus::HasSecondaryHunk - | DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk - if distinguish_unstaged_hunks => - { - pattern_slash(color, line_height.0 / 4.0) - } - _ => color.into(), - } + Some(DiffHunkStatus::Removed(_)) => { + cx.theme().colors().version_control_deleted_background } _ => continue, }; @@ -7755,7 +7768,7 @@ pub struct EditorLayout { indent_guides: Option>, visible_display_row_range: Range, active_rows: BTreeMap, - highlighted_rows: BTreeMap, + highlighted_rows: BTreeMap, line_elements: SmallVec<[AnyElement; 1]>, line_numbers: Arc>, display_hunks: Vec<(DisplayDiffHunk, Option)>, diff --git a/crates/gpui/src/color.rs b/crates/gpui/src/color.rs index daec9440a4..e3adc8ded9 100644 --- a/crates/gpui/src/color.rs +++ b/crates/gpui/src/color.rs @@ -607,6 +607,25 @@ impl Default for Background { } } +impl Background { + /// Gets the color of the background if there is one. + pub fn color(&self) -> Option { + match self.tag { + BackgroundTag::Solid => Some(self.solid), + BackgroundTag::LinearGradient => None, + BackgroundTag::PatternSlash => Some(self.solid), + } + } +} + +/// Creates a background with a solid color +pub fn solid_color(color: impl Into) -> Background { + Background { + solid: color.into(), + ..Default::default() + } +} + /// Creates a hash pattern background pub fn pattern_slash(color: Hsla, thickness: f32) -> Background { Background { diff --git a/crates/theme/src/default_colors.rs b/crates/theme/src/default_colors.rs index 45c70cb7cf..df480cbe89 100644 --- a/crates/theme/src/default_colors.rs +++ b/crates/theme/src/default_colors.rs @@ -136,11 +136,11 @@ impl ThemeColors { terminal_ansi_dim_white: neutral().light().step_11(), link_text_hover: orange().light().step_10(), version_control_added: ADDED_COLOR, - version_control_added_background: ADDED_COLOR.opacity(0.1), + version_control_added_background: ADDED_COLOR.opacity(0.08), version_control_deleted: REMOVED_COLOR, - version_control_deleted_background: REMOVED_COLOR.opacity(0.1), + version_control_deleted_background: REMOVED_COLOR.opacity(0.08), version_control_modified: MODIFIED_COLOR, - version_control_modified_background: MODIFIED_COLOR.opacity(0.1), + version_control_modified_background: MODIFIED_COLOR.opacity(0.08), version_control_renamed: MODIFIED_COLOR, version_control_conflict: orange().light().step_12(), version_control_conflict_background: orange().light().step_12().opacity(0.1),