Add `git.hunk_style` setting for gutter hollow hunk behavior (#26816)

Jakub Charvat created

This is a follow up to #26809, introducing `git.hunk_style` setting to
control whether staged or unstaged hunks are shown as hollow.

Reused `GitHunkStyleSetting` which was left over from #26504.

Release Notes:

- Added `git.hunk_style` setting to control whether staged or unstaged
hunks are hollow.

Change summary

assets/settings/default.json           | 10 ++++++++
crates/editor/src/element.rs           | 29 ++++++++++++++++++---------
crates/project/src/project_settings.rs | 21 +++++++------------
3 files changed, 36 insertions(+), 24 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -850,7 +850,15 @@
       //
       // The minimum column number to show the inline blame information at
       // "min_column": 0
-    }
+    },
+    // How git hunks are displayed visually in the editor.
+    // This setting can take two values:
+    //
+    // 1. Show unstaged hunks filled and staged hunks hollow:
+    //    "hunk_style": "staged_hollow"
+    // 2. Show unstaged hunks hollow and staged hunks filled:
+    //    "hunk_style": "unstaged_hollow"
+    "hunk_style": "staged_hollow"
   },
   // The list of custom Git hosting providers.
   "git_hosting_providers": [

crates/editor/src/element.rs 🔗

@@ -55,7 +55,7 @@ use multi_buffer::{
     Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint,
     MultiBufferRow, RowInfo,
 };
-use project::project_settings::{self, GitGutterSetting, ProjectSettings};
+use project::project_settings::{self, GitGutterSetting, GitHunkStyleSetting, ProjectSettings};
 use settings::Settings;
 use smallvec::{smallvec, SmallVec};
 use std::{
@@ -4379,8 +4379,6 @@ impl EditorElement {
                 };
 
                 if let Some((hunk_bounds, background_color, corner_radii, status)) = hunk_to_paint {
-                    let unstaged = status.has_secondary_hunk();
-
                     // Flatten the background color with the editor color to prevent
                     // elements below transparent hunks from showing through
                     let flattened_background_color = cx
@@ -4389,7 +4387,7 @@ impl EditorElement {
                         .editor_background
                         .blend(background_color);
 
-                    if unstaged {
+                    if !Self::diff_hunk_hollow(status, cx) {
                         window.paint_quad(quad(
                             hunk_bounds,
                             corner_radii,
@@ -5622,6 +5620,18 @@ impl EditorElement {
             &[run],
         )
     }
+
+    fn diff_hunk_hollow(status: DiffHunkStatus, cx: &mut App) -> bool {
+        let unstaged = status.has_secondary_hunk();
+        let unstaged_hollow = ProjectSettings::get_global(cx)
+            .git
+            .hunk_style
+            .map_or(false, |style| {
+                matches!(style, GitHunkStyleSetting::UnstagedHollow)
+            });
+
+        unstaged == unstaged_hollow
+    }
 }
 
 fn header_jump_data(
@@ -6773,10 +6783,9 @@ impl Element for EditorElement {
                             }
                         };
 
-                        let unstaged = diff_status.has_secondary_hunk();
                         let hunk_opacity = if is_light { 0.16 } else { 0.12 };
 
-                        let staged_highlight = LineHighlight {
+                        let hollow_highlight = LineHighlight {
                             background: (background_color.opacity(if is_light {
                                 0.08
                             } else {
@@ -6790,13 +6799,13 @@ impl Element for EditorElement {
                             }),
                         };
 
-                        let unstaged_highlight =
+                        let filled_highlight =
                             solid_background(background_color.opacity(hunk_opacity)).into();
 
-                        let background = if unstaged {
-                            unstaged_highlight
+                        let background = if Self::diff_hunk_hollow(diff_status, cx) {
+                            hollow_highlight
                         } else {
-                            staged_highlight
+                            filled_highlight
                         };
 
                         highlighted_rows

crates/project/src/project_settings.rs 🔗

@@ -168,6 +168,10 @@ pub struct GitSettings {
     ///
     /// Default: on
     pub inline_blame: Option<InlineBlameSettings>,
+    /// How hunks are displayed visually in the editor.
+    ///
+    /// Default: staged_hollow
+    pub hunk_style: Option<GitHunkStyleSetting>,
 }
 
 impl GitSettings {
@@ -203,20 +207,11 @@ impl GitSettings {
 #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "snake_case")]
 pub enum GitHunkStyleSetting {
-    /// Show unstaged hunks with a transparent background
+    /// Show unstaged hunks with a filled background and staged hunks hollow.
     #[default]
-    Transparent,
-    /// Show unstaged hunks with a pattern background
-    Pattern,
-    /// Show unstaged hunks with a border background
-    Border,
-
-    /// Show staged hunks with a pattern background
-    StagedPattern,
-    /// Show staged hunks with a pattern background
-    StagedTransparent,
-    /// Show staged hunks with a pattern background
-    StagedBorder,
+    StagedHollow,
+    /// Show unstaged hunks hollow and staged hunks with a filled background.
+    UnstagedHollow,
 }
 
 #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]