From 463047c5c749317676bbeb2ccf742d224e9027e3 Mon Sep 17 00:00:00 2001 From: Dino Date: Tue, 28 Apr 2026 17:09:23 +0100 Subject: [PATCH] git_panel: Add support for vertically expanding the commit editor (#55043) Using the existing commit editor in the Git Panel to type out longer commit messages has been somewhat hard. I believe this happens because it takes a very small portion of the UI which, unfortunately, when `git: expand commit editor` is used, a modal ends up taking the center of the editor, making it possible to have the commit editor open on the side, while the `git: diff` view is open. As such, this Pull Request introduces a new `git::ToggleFillCommitEditor` action that allows users to update the commit editor's height so as to take as much vertical space as possible, hiding the entries status and simply rendering the Git Panel's footer. This makes it easier to be able to write longer commit messages while still having the `git: branch diff` on the side, something that's very complicated with the default number of lines in the commit editor and impossible using the `CommitModal`. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Added a `git::ToggleFillCommitEditor` action that expands the commit editor to fill the git panel's available vertical space. --------- Co-authored-by: Danilo Leal Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> --- assets/keymaps/default-linux.json | 2 + assets/keymaps/default-macos.json | 1 + assets/keymaps/default-windows.json | 2 + crates/git/src/git.rs | 3 + crates/git_ui/src/git_panel.rs | 177 +++++++++++++++++++++++----- 5 files changed, 157 insertions(+), 28 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index a72cd74359..ba7f514766 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -1071,6 +1071,8 @@ "ctrl-shift-enter": "git::Amend", "alt-up": "git_panel::FocusChanges", "alt-l": "git::GenerateCommitMessage", + "shift-escape": "git::ExpandCommitEditor", + "alt-shift-escape": "git::ToggleFillCommitEditor", }, }, { diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 4f26eeeb8b..11750aa741 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -1102,6 +1102,7 @@ "shift-tab": "git_panel::FocusChanges", "alt-up": "git_panel::FocusChanges", "shift-escape": "git::ExpandCommitEditor", + "alt-shift-escape": "git::ToggleFillCommitEditor", "alt-tab": "git::GenerateCommitMessage", }, }, diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json index 8d60a68dbc..2526a9412c 100644 --- a/assets/keymaps/default-windows.json +++ b/assets/keymaps/default-windows.json @@ -1069,6 +1069,8 @@ "ctrl-shift-enter": "git::Amend", "alt-up": "git_panel::FocusChanges", "alt-l": "git::GenerateCommitMessage", + "shift-escape": "git::ExpandCommitEditor", + "alt-shift-escape": "git::ToggleFillCommitEditor", }, }, { diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs index debb7649ab..d5274d24b5 100644 --- a/crates/git/src/git.rs +++ b/crates/git/src/git.rs @@ -94,6 +94,9 @@ actions!( Cancel, /// Expands the commit message editor. ExpandCommitEditor, + /// Toggles whether the commit message editor fills all the available + /// vertical space within the git panel. + ToggleFillCommitEditor, /// Generates a commit message using AI. GenerateCommitMessage, /// Initializes a new git repository. diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 62f8edcfbd..f0900b56bc 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -16,7 +16,7 @@ use askpass::AskPassDelegate; use collections::{BTreeMap, HashMap, HashSet}; use db::kvp::KeyValueStore; use editor::{ - Direction, Editor, EditorElement, EditorMode, MultiBuffer, MultiBufferOffset, + Direction, Editor, EditorElement, EditorMode, MultiBuffer, MultiBufferOffset, SizingBehavior, actions::ExpandAllDiffHunks, }; use editor::{EditorStyle, RewrapOptions}; @@ -34,7 +34,7 @@ use git::status::{DiffStat, StageStatus}; use git::{Amend, Signoff, ToggleStaged, repository::RepoPath, status::FileStatus}; use git::{ ExpandCommitEditor, GitHostingProviderRegistry, RestoreTrackedFiles, StageAll, StashAll, - StashApply, StashPop, TrashUntrackedFiles, UnstageAll, + StashApply, StashPop, ToggleFillCommitEditor, TrashUntrackedFiles, UnstageAll, }; use gpui::{ Action, Anchor, AsyncApp, AsyncWindowContext, Bounds, ClickEvent, DismissEvent, Empty, Entity, @@ -247,6 +247,13 @@ pub fn register(workspace: &mut Workspace) { workspace.register_action(|workspace, _: &ExpandCommitEditor, window, cx| { CommitModal::toggle(workspace, None, window, cx) }); + workspace.register_action(|workspace, _: &ToggleFillCommitEditor, window, cx| { + if let Some(panel) = workspace.panel::(cx) { + panel.update(cx, |panel, cx| { + panel.toggle_fill_commit_editor(&Default::default(), window, cx) + }); + } + }); workspace.register_action(|workspace, _: &git::Init, window, cx| { if let Some(panel) = workspace.panel::(cx) { panel.update(cx, |panel, cx| panel.git_init(window, cx)); @@ -619,6 +626,8 @@ impl TruncatedPatch { pub struct GitPanel { pub(crate) active_repository: Option>, pub(crate) commit_editor: Entity, + /// Whether the commit editor should fill the vertical height of the panel. + commit_editor_expanded: bool, conflicted_count: usize, conflicted_staged_count: usize, add_coauthors: bool, @@ -809,6 +818,7 @@ impl GitPanel { let mut this = Self { active_repository, commit_editor, + commit_editor_expanded: false, conflicted_count: 0, conflicted_staged_count: 0, add_coauthors: true, @@ -4259,9 +4269,34 @@ impl GitPanel { } } + fn toggle_fill_commit_editor( + &mut self, + _: &ToggleFillCommitEditor, + _window: &mut Window, + cx: &mut Context, + ) { + self.commit_editor_expanded = !self.commit_editor_expanded; + self.commit_editor.update(cx, |editor, _cx| { + if self.commit_editor_expanded { + editor.set_mode(EditorMode::Full { + scale_ui_elements_with_buffer_font_size: false, + show_active_line_background: false, + sizing_behavior: SizingBehavior::ExcludeOverscrollMargin, + }) + } else { + editor.set_mode(EditorMode::AutoHeight { + min_lines: MAX_PANEL_EDITOR_LINES, + max_lines: Some(MAX_PANEL_EDITOR_LINES), + }) + } + }); + + cx.notify(); + } + fn expand_commit_editor( &mut self, - _: &git::ExpandCommitEditor, + _: &ExpandCommitEditor, window: &mut Window, cx: &mut Context, ) { @@ -4375,10 +4410,7 @@ impl GitPanel { let active_repository = self.active_repository.clone()?; let panel_editor_style = panel_editor_style(true, window, cx); let enable_coauthors = self.render_co_authors(cx); - let editor_focus_handle = self.commit_editor.focus_handle(cx); - let expand_tooltip_focus_handle = editor_focus_handle; - let branch = active_repository.read(cx).branch.clone(); let head_commit = active_repository.read(cx).head_commit.clone(); @@ -4414,6 +4446,7 @@ impl GitPanel { }; let footer = v_flex() + .when(self.commit_editor_expanded, |this| this.flex_1().min_h_0()) .child(PanelRepoFooter::new( display_name, branch, @@ -4448,7 +4481,10 @@ impl GitPanel { .cursor_text() .relative() .w_full() - .h(max_height + footer_size) + .when(self.commit_editor_expanded, |this| this.flex_1().min_h_0()) + .when(!self.commit_editor_expanded, |this| { + this.h(max_height + footer_size) + }) .border_t_1() .border_color(if title_exceeds_limit { cx.theme().status().warning_border @@ -4486,6 +4522,9 @@ impl GitPanel { ) .child( div() + .when(self.commit_editor_expanded, |this| { + this.flex_1().min_h_0().pb(footer_size) + }) .pr_2p5() .on_action(|&zed_actions::editor::MoveUp, _, cx| { cx.stop_propagation(); @@ -4500,19 +4539,21 @@ impl GitPanel { .absolute() .top_2() .right_2() - .opacity(0.5) - .hover(|this| this.opacity(1.0)) + .gap_px() + .opacity(0.6) + .hover(|s| s.opacity(1.0)) .child( - panel_icon_button("expand-commit-editor", IconName::Maximize) + panel_icon_button("expand-commit-editor", IconName::MaximizeAlt) .icon_size(IconSize::Small) - .size(ui::ButtonSize::Default) - .tooltip(move |_window, cx| { - Tooltip::for_action_in( - "Open Commit Modal", - &git::ExpandCommitEditor, - &expand_tooltip_focus_handle, - cx, - ) + .tooltip({ + move |_window, cx| { + Tooltip::for_action_in( + "Open Commit Modal", + &git::ExpandCommitEditor, + &editor_focus_handle, + cx, + ) + } }) .on_click(cx.listener({ move |_, _, window, cx| { @@ -4522,7 +4563,36 @@ impl GitPanel { ) } })), - ), + ) + .child({ + let (icon, label) = if self.commit_editor_expanded { + (IconName::Minimize, "Collapse Commit Editor") + } else { + (IconName::Maximize, "Expand Commit Editor") + }; + let focus_handle = self.focus_handle.clone(); + + panel_icon_button("fill-commit-editor", icon) + .icon_size(IconSize::Small) + .tooltip({ + move |_window, cx| { + Tooltip::for_action_in( + label, + &git::ToggleFillCommitEditor, + &focus_handle, + cx, + ) + } + }) + .on_click(cx.listener({ + move |_, _, window, cx| { + window.dispatch_action( + git::ToggleFillCommitEditor.boxed_clone(), + cx, + ) + } + })) + }), ), ); @@ -5932,15 +6002,22 @@ impl Render for GitPanel { .child( v_flex() .size_full() - .children(self.render_panel_header(window, cx)) - .map(|this| { - if let Some(repo) = self.active_repository.clone() - && has_entries - { - this.child(self.render_entries(has_write_access, repo, window, cx)) - } else { - this.child(self.render_empty_state(cx).into_any_element()) - } + .when(!self.commit_editor_expanded, |this| { + this.children(self.render_panel_header(window, cx)) + .map(|this| { + if let Some(repo) = self.active_repository.clone() + && has_entries + { + this.child(self.render_entries( + has_write_access, + repo, + window, + cx, + )) + } else { + this.child(self.render_empty_state(cx).into_any_element()) + } + }) }) .children(self.render_footer(window, cx)) .when(self.amend_pending, |this| { @@ -8275,4 +8352,48 @@ mod tests { ); }); } + + #[gpui::test] + async fn test_fill_commit_editor_toggle(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.background_executor.clone()); + fs.insert_tree( + "/root", + json!({ "project": { ".git": {}, "src": { "main.rs": "fn main() {}" } } }), + ) + .await; + + let project = Project::test(fs.clone(), [Path::new(path!("/root/project"))], cx).await; + let window_handle = + cx.add_window(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx)); + let workspace = window_handle + .read_with(cx, |mw, _| mw.workspace().clone()) + .unwrap(); + let cx = &mut VisualTestContext::from_window(window_handle.into(), cx); + cx.executor().run_until_parked(); + + let panel = workspace.update_in(cx, GitPanel::new); + + panel.update_in(cx, |panel, window, cx| { + assert!(!panel.commit_editor_expanded); + assert!(matches!( + panel.commit_editor.read(cx).mode().clone(), + EditorMode::AutoHeight { .. } + )); + + panel.toggle_fill_commit_editor(&ToggleFillCommitEditor, window, cx); + assert!(panel.commit_editor_expanded); + assert!(matches!( + panel.commit_editor.read(cx).mode().clone(), + EditorMode::Full { .. } + )); + + panel.toggle_fill_commit_editor(&ToggleFillCommitEditor, window, cx); + assert!(!panel.commit_editor_expanded); + assert!(matches!( + panel.commit_editor.read(cx).mode().clone(), + EditorMode::AutoHeight { .. } + )); + }); + } }