git_ui: Force commit modal mode from command palette (#28745)

Smit Barmase created

Depending on `git::commit` or `git::amend` action triggered, commit
modal opens up in appropriate mode, handling edge cases like if you are
already in amend mode, etc.

Release Notes:

- N/A

Change summary

crates/git_ui/src/commit_modal.rs | 87 ++++++++++++--------------------
crates/git_ui/src/git_panel.rs    | 46 +++++++++-------
2 files changed, 59 insertions(+), 74 deletions(-)

Detailed changes

crates/git_ui/src/commit_modal.rs 🔗

@@ -2,7 +2,6 @@ use crate::branch_picker::{self, BranchList};
 use crate::git_panel::{GitPanel, commit_message_editor};
 use git::repository::CommitOptions;
 use git::{Amend, Commit, GenerateCommitMessage};
-use language::Buffer;
 use panel::{panel_button, panel_editor_style, panel_filled_button};
 use ui::{
     ContextMenu, KeybindingHint, PopoverMenu, PopoverMenuHandle, SplitButton, Tooltip, prelude::*,
@@ -100,22 +99,47 @@ struct RestoreDock {
     active_index: Option<usize>,
 }
 
+pub enum ForceMode {
+    Amend,
+    Commit,
+}
+
 impl CommitModal {
     pub fn register(workspace: &mut Workspace) {
         workspace.register_action(|workspace, _: &Commit, window, cx| {
-            CommitModal::toggle(workspace, window, cx);
+            CommitModal::toggle(workspace, Some(ForceMode::Commit), window, cx);
         });
         workspace.register_action(|workspace, _: &Amend, window, cx| {
-            CommitModal::toggle(workspace, window, cx);
+            CommitModal::toggle(workspace, Some(ForceMode::Amend), window, cx);
         });
     }
 
-    pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
+    pub fn toggle(
+        workspace: &mut Workspace,
+        force_mode: Option<ForceMode>,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
         let Some(git_panel) = workspace.panel::<GitPanel>(cx) else {
             return;
         };
 
         git_panel.update(cx, |git_panel, cx| {
+            if let Some(force_mode) = force_mode {
+                match force_mode {
+                    ForceMode::Amend => {
+                        if !git_panel.amend_pending() {
+                            git_panel.set_amend_pending(true, cx);
+                            git_panel.load_last_commit_message_if_empty(cx);
+                        }
+                    }
+                    ForceMode::Commit => {
+                        if git_panel.amend_pending() {
+                            git_panel.set_amend_pending(false, cx);
+                        }
+                    }
+                }
+            }
             git_panel.set_modal_open(true, cx);
         });
 
@@ -461,23 +485,12 @@ impl CommitModal {
     fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
         if self.git_panel.read(cx).amend_pending() {
             self.git_panel
-                .update(cx, |git_panel, _| git_panel.set_amend_pending(false));
-            cx.notify();
+                .update(cx, |git_panel, cx| git_panel.set_amend_pending(false, cx));
         } else {
             cx.emit(DismissEvent);
         }
     }
 
-    pub fn commit_message_buffer(&self, cx: &App) -> Entity<Buffer> {
-        self.commit_editor
-            .read(cx)
-            .buffer()
-            .read(cx)
-            .as_singleton()
-            .unwrap()
-            .clone()
-    }
-
     fn commit(&mut self, _: &git::Commit, window: &mut Window, cx: &mut Context<Self>) {
         if self.git_panel.read(cx).amend_pending() {
             return;
@@ -490,54 +503,20 @@ impl CommitModal {
     }
 
     fn amend(&mut self, _: &git::Amend, window: &mut Window, cx: &mut Context<Self>) {
-        let Some(active_repository) = self.git_panel.read(cx).active_repository.as_ref() else {
-            return;
-        };
-        let Some(branch) = active_repository.read(cx).branch.as_ref() else {
-            return;
-        };
-        let Some(recent_sha) = branch
-            .most_recent_commit
-            .as_ref()
-            .map(|commit| commit.sha.to_string())
-        else {
-            return;
-        };
         if self
             .commit_editor
             .focus_handle(cx)
             .contains_focused(window, cx)
         {
             if !self.git_panel.read(cx).amend_pending() {
-                self.git_panel.update(cx, |git_panel, _| {
-                    git_panel.set_amend_pending(true);
+                self.git_panel.update(cx, |git_panel, cx| {
+                    git_panel.set_amend_pending(true, cx);
+                    git_panel.load_last_commit_message_if_empty(cx);
                 });
-                cx.notify();
-                if self.commit_editor.read(cx).is_empty(cx) {
-                    let detail_task = self.git_panel.update(cx, |git_panel, cx| {
-                        git_panel.load_commit_details(recent_sha, cx)
-                    });
-                    cx.spawn(async move |this, cx| {
-                        if let Ok(message) = detail_task.await.map(|detail| detail.message) {
-                            this.update(cx, |this, cx| {
-                                this.commit_message_buffer(cx).update(cx, |buffer, cx| {
-                                    let insert_position = buffer.anchor_before(buffer.len());
-                                    buffer.edit(
-                                        [(insert_position..insert_position, message)],
-                                        None,
-                                        cx,
-                                    );
-                                });
-                            })
-                            .log_err();
-                        }
-                    })
-                    .detach();
-                }
             } else {
                 telemetry::event!("Git Amended", source = "Git Panel");
                 self.git_panel.update(cx, |git_panel, cx| {
-                    git_panel.set_amend_pending(false);
+                    git_panel.set_amend_pending(false, cx);
                     git_panel.commit_changes(CommitOptions { amend: true }, window, cx);
                 });
                 cx.emit(DismissEvent);

crates/git_ui/src/git_panel.rs 🔗

@@ -167,7 +167,7 @@ pub fn register(workspace: &mut Workspace) {
         workspace.toggle_panel_focus::<GitPanel>(window, cx);
     });
     workspace.register_action(|workspace, _: &ExpandCommitEditor, window, cx| {
-        CommitModal::toggle(workspace, window, cx)
+        CommitModal::toggle(workspace, None, window, cx)
     });
 }
 
@@ -1434,7 +1434,10 @@ impl GitPanel {
         }
     }
 
-    fn amend(&mut self, _: &git::Amend, window: &mut Window, cx: &mut Context<Self>) {
+    pub fn load_last_commit_message_if_empty(&mut self, cx: &mut Context<Self>) {
+        if !self.commit_editor.read(cx).is_empty(cx) {
+            return;
+        }
         let Some(active_repository) = self.active_repository.as_ref() else {
             return;
         };
@@ -1448,6 +1451,23 @@ impl GitPanel {
         else {
             return;
         };
+        let detail_task = self.load_commit_details(recent_sha, cx);
+        cx.spawn(async move |this, cx| {
+            if let Ok(message) = detail_task.await.map(|detail| detail.message) {
+                this.update(cx, |this, cx| {
+                    this.commit_message_buffer(cx).update(cx, |buffer, cx| {
+                        let start = buffer.anchor_before(0);
+                        let end = buffer.anchor_after(buffer.len());
+                        buffer.edit([(start..end, message)], None, cx);
+                    });
+                })
+                .log_err();
+            }
+        })
+        .detach();
+    }
+
+    fn amend(&mut self, _: &git::Amend, window: &mut Window, cx: &mut Context<Self>) {
         if self
             .commit_editor
             .focus_handle(cx)
@@ -1456,22 +1476,7 @@ impl GitPanel {
             if !self.amend_pending {
                 self.amend_pending = true;
                 cx.notify();
-                if self.commit_editor.read(cx).is_empty(cx) {
-                    let detail_task = self.load_commit_details(recent_sha, cx);
-                    cx.spawn(async move |this, cx| {
-                        if let Ok(message) = detail_task.await.map(|detail| detail.message) {
-                            this.update(cx, |this, cx| {
-                                this.commit_message_buffer(cx).update(cx, |buffer, cx| {
-                                    let start = buffer.anchor_before(0);
-                                    let end = buffer.anchor_after(buffer.len());
-                                    buffer.edit([(start..end, message)], None, cx);
-                                });
-                            })
-                            .log_err();
-                        }
-                    })
-                    .detach();
-                }
+                self.load_last_commit_message_if_empty(cx);
             } else {
                 telemetry::event!("Git Amended", source = "Git Panel");
                 self.amend_pending = false;
@@ -2859,7 +2864,7 @@ impl GitPanel {
         window.defer(cx, move |window, cx| {
             workspace
                 .update(cx, |workspace, cx| {
-                    CommitModal::toggle(workspace, window, cx)
+                    CommitModal::toggle(workspace, None, window, cx)
                 })
                 .ok();
         })
@@ -3997,8 +4002,9 @@ impl GitPanel {
         self.amend_pending
     }
 
-    pub fn set_amend_pending(&mut self, value: bool) {
+    pub fn set_amend_pending(&mut self, value: bool, cx: &mut Context<Self>) {
         self.amend_pending = value;
+        cx.notify();
     }
 }