From 3b56897a510dc98c9bf82903ed796f76e8e3b4b2 Mon Sep 17 00:00:00 2001 From: Evan Vinciguerra <7633300+evanvin@users.noreply.github.com> Date: Sun, 2 Aug 2026 21:46:00 -0400 Subject: [PATCH] editor: Add configurable git gutter width setting (#61304) ## Summary Adds a `gutter.git_gutter_width` setting that lets users pin the width, in pixels, of the git diff hunk indicators in the editor gutter. Previously the width was always derived from the buffer font size (`floor(0.275 * line_height)`), which can render too thin or too thick depending on font family and display pixel density. This adds an optional override: ```json { "gutter": { "git_gutter_width": 6 } } ``` When the setting is unset (`null`, the default), the width continues to scale with the buffer font size, so existing behavior is unchanged. ## Motivation Requested in [discussion #27799](https://github.com/zed-industries/zed/discussions/27799). Beyond the width itself, participants noted the git hunk hit target is extremely narrow and hard to click. Because the hunk hitbox reuses the painted strip bounds, setting a wider `git_gutter_width` also widens the clickable area, addressing that complaint with the same setting. ## Changes - `settings_content`: new `git_gutter_width: Option` on `GutterContent`. - `editor`: mirror field on the resolved `Gutter`; `gutter_strip_width` now consults the setting, and the value flows through `diff_hunk_bounds` (including the deleted-hunk marker) and the gutter layout anchors for line numbers, folds, and expand toggles. - `settings` (VS Code import): pass through the new field. - Docs + `default.json`: document the new setting. ## Notes - `Eq` was dropped from `GutterContent`/`Gutter` because `f32` is not `Eq`; this matches the existing `EditorSettingsContent` convention for float-bearing settings. Release Notes: - Added a `gutter.git_gutter_width` setting to configure the width of git diff indicators in the editor gutter --------- Co-authored-by: Claude Opus 4.8 (1M context) --- assets/settings/default.json | 3 +++ crates/editor/src/editor_settings.rs | 4 +++- crates/editor/src/element.rs | 23 ++++++++++++++++------- crates/settings/src/vscode_import.rs | 1 + crates/settings_content/src/editor.rs | 7 ++++++- crates/settings_ui/src/page_data.rs | 26 +++++++++++++++++++++++++- docs/src/reference/all-settings.md | 4 +++- 7 files changed, 57 insertions(+), 11 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 875f65650e..bbd1a33a0d 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -695,6 +695,9 @@ "folds": true, // Minimum number of characters to reserve space for in the gutter. "min_line_number_digits": 4, + // The width, in pixels, of the git diff hunk indicators in the gutter. + // When set to null, the width scales with the buffer font size. + "git_gutter_width": null, }, "indent_guides": { // Whether to show indent guides in the editor. diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 9e6cdcd44b..f6fdd6c16d 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -141,7 +141,7 @@ impl Minimap { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct Gutter { pub min_line_number_digits: usize, pub line_numbers: bool, @@ -149,6 +149,7 @@ pub struct Gutter { pub breakpoints: bool, pub bookmarks: bool, pub folds: bool, + pub git_gutter_width: Option, } /// Forcefully enable or disable the scrollbar for each axis @@ -267,6 +268,7 @@ impl Settings for EditorSettings { bookmarks: gutter.bookmarks.unwrap(), breakpoints: gutter.breakpoints.unwrap(), folds: gutter.folds.unwrap(), + git_gutter_width: gutter.git_gutter_width, }, scroll_beyond_last_line: editor.scroll_beyond_last_line.unwrap(), vertical_scroll_margin: editor.vertical_scroll_margin.unwrap() as f64, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index be01987333..a5ce7bdd54 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1676,6 +1676,7 @@ impl EditorElement { gutter_hitbox.bounds, hunk, snapshot, + cx, ); *hitbox = Some(window.insert_hitbox(hunk_bounds, HitboxBehavior::BlockMouse)); } @@ -2669,7 +2670,7 @@ impl EditorElement { .ilog10() + 1; - let git_gutter_width = Self::gutter_strip_width(line_height) + let git_gutter_width = Self::gutter_strip_width(line_height, cx) + gutter_dimensions .git_blame_entries_width .unwrap_or_default(); @@ -5228,6 +5229,7 @@ impl EditorElement { layout.gutter_hitbox.bounds, hunk, &layout.position_map.snapshot, + cx, ); Some(( hunk_bounds, @@ -5313,8 +5315,11 @@ impl EditorElement { }); } - fn gutter_strip_width(line_height: Pixels) -> Pixels { - (0.275 * line_height).floor() + fn gutter_strip_width(line_height: Pixels, cx: &App) -> Pixels { + match EditorSettings::get_global(cx).gutter.git_gutter_width { + Some(width) => px(width), + None => (0.275 * line_height).floor(), + } } fn diff_hunk_bounds( @@ -5323,9 +5328,10 @@ impl EditorElement { gutter_bounds: Bounds, hunk: &DisplayDiffHunk, snapshot: &EditorSnapshot, + cx: &App, ) -> Bounds { let scroll_top = scroll_position.y * ScrollPixelOffset::from(line_height); - let gutter_strip_width = Self::gutter_strip_width(line_height); + let gutter_strip_width = Self::gutter_strip_width(line_height, cx); match hunk { DisplayDiffHunk::Folded { display_row, .. } => { @@ -5351,7 +5357,10 @@ impl EditorElement { .into(); let end_y = start_y + line_height; - let width = (0.35 * line_height).floor(); + let width = match EditorSettings::get_global(cx).gutter.git_gutter_width { + Some(width) => px(width), + None => (0.35 * line_height).floor(), + }; let highlight_origin = gutter_bounds.origin + point(px(0.), start_y); let highlight_size = size(width, end_y - start_y); Bounds::new(highlight_origin, highlight_size) @@ -6720,7 +6729,7 @@ impl Gutter<'_> { AvailableSpace::Definite(self.line_height), ); let indicator_size = button.layout_as_root(available_space, window, cx); - let git_gutter_width = EditorElement::gutter_strip_width(self.line_height) + let git_gutter_width = EditorElement::gutter_strip_width(self.line_height, cx) + self.dimensions.git_blame_entries_width.unwrap_or_default(); let x = git_gutter_width + px(2.); @@ -9148,7 +9157,7 @@ impl Element for EditorElement { ); } - let git_gutter_width = Self::gutter_strip_width(line_height) + let git_gutter_width = Self::gutter_strip_width(line_height, cx) + gutter_dimensions .git_blame_entries_width .unwrap_or_default(); diff --git a/crates/settings/src/vscode_import.rs b/crates/settings/src/vscode_import.rs index 24072a291b..652748eda0 100644 --- a/crates/settings/src/vscode_import.rs +++ b/crates/settings/src/vscode_import.rs @@ -350,6 +350,7 @@ impl VsCodeSettings { "never" => Some(false), _ => None, }), + git_gutter_width: None, }) } diff --git a/crates/settings_content/src/editor.rs b/crates/settings_content/src/editor.rs index 14a594236a..52ff2fa10e 100644 --- a/crates/settings_content/src/editor.rs +++ b/crates/settings_content/src/editor.rs @@ -483,7 +483,7 @@ pub struct ScrollbarAxesContent { /// Gutter related settings #[with_fallible_options] -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)] pub struct GutterContent { /// Whether to show line numbers in the gutter. /// @@ -509,6 +509,11 @@ pub struct GutterContent { /// /// Default: true pub folds: Option, + /// The width, in pixels, of the git diff hunk indicators in the gutter. + /// When unset, the width scales with the buffer font size. + /// + /// Default: null + pub git_gutter_width: Option, } /// Whether to display code lenses from language servers above code elements. diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 5da60174a8..a1b905fe18 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -2182,7 +2182,7 @@ fn editor_page() -> SettingsPage { ] } - fn gutter_section() -> [SettingsPageItem; 9] { + fn gutter_section() -> [SettingsPageItem; 10] { [ SettingsPageItem::SectionHeader("Gutter"), SettingsPageItem::SettingItem(SettingItem { @@ -2339,6 +2339,30 @@ fn editor_page() -> SettingsPage { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Git Gutter Width", + description: "Width, in pixels, of the git diff indicators in the gutter. When unset, the width scales with the buffer font size.", + field: Box::new(SettingField { + organization_override: None, + json_path: Some("gutter.git_gutter_width"), + pick: |settings_content| { + settings_content + .editor + .gutter + .as_ref() + .and_then(|gutter| gutter.git_gutter_width.as_ref()) + }, + write: |settings_content, value, _| { + settings_content + .editor + .gutter + .get_or_insert_default() + .git_gutter_width = value; + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SettingItem(SettingItem { title: "Inline Code Actions", description: "Show code action button at start of buffer line.", diff --git a/docs/src/reference/all-settings.md b/docs/src/reference/all-settings.md index a141d1b3db..b0bf79dbe2 100644 --- a/docs/src/reference/all-settings.md +++ b/docs/src/reference/all-settings.md @@ -838,7 +838,8 @@ List of `string` values "runnables": true, "breakpoints": true, "folds": true, - "min_line_number_digits": 4 + "min_line_number_digits": 4, + "git_gutter_width": null } } ``` @@ -850,6 +851,7 @@ List of `string` values - `breakpoints`: Whether to show breakpoints in the gutter - `folds`: Whether to show fold buttons in the gutter - `min_line_number_digits`: Minimum number of characters to reserve space for in the gutter +- `git_gutter_width`: The width, in pixels, of the git diff hunk indicators in the gutter. When `null`, the width scales with the buffer font size ## Hide Mouse