git: Fix project diff shortcuts (#26045)

Conrad Irwin created

Release Notes:

- git: Fix keyboard shortcut display in project diff view

Change summary

assets/keymaps/default-linux.json      |  24 +---
assets/keymaps/default-macos.json      |  12 +-
assets/keymaps/linux/jetbrains.json    |   4 
assets/keymaps/linux/sublime_text.json |   4 
assets/keymaps/macos/jetbrains.json    |   4 
assets/keymaps/macos/sublime_text.json |   4 
assets/keymaps/vim.json                |   9 +
crates/editor/src/actions.rs           |  18 ---
crates/editor/src/editor.rs            | 128 +++++----------------------
crates/editor/src/editor_tests.rs      |  18 +-
crates/editor/src/element.rs           |  17 ++
crates/git/src/git.rs                  |  14 --
crates/git_ui/src/project_diff.rs      |  52 ++---------
crates/zed/src/zed/quick_action_bar.rs |  14 --
docs/README.md                         |   6 +
docs/src/vim.md                        |  13 +
16 files changed, 108 insertions(+), 233 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -370,10 +370,10 @@
       "ctrl-shift-v": "markdown::OpenPreview",
       "ctrl-alt-shift-c": "editor::DisplayCursorNames",
       "ctrl-alt-y": "git::ToggleStaged",
-      "alt-y": ["git::StageAndNext", { "whole_excerpt": false }],
-      "alt-shift-y": ["git::UnstageAndNext", { "whole_excerpt": false }],
-      "alt-.": ["editor::GoToHunk", { "center_cursor": true }],
-      "alt-,": ["editor::GoToPreviousHunk", { "center_cursor": true }]
+      "alt-y": "git::StageAndNext",
+      "alt-shift-y": "git::UnstageAndNext",
+      "alt-.": "editor::GoToHunk",
+      "alt-,": "editor::GoToPreviousHunk"
     }
   },
   {
@@ -564,8 +564,8 @@
       "shift-enter": "editor::ExpandExcerpts",
       "ctrl-alt-enter": "editor::OpenExcerptsSplit",
       "ctrl-shift-e": "pane::RevealInProjectPanel",
-      "ctrl-f8": ["editor::GoToHunk", { "center_cursor": true }],
-      "ctrl-shift-f8": ["editor::GoToPreviousHunk", { "center_cursor": true }],
+      "ctrl-f8": "editor::GoToHunk",
+      "ctrl-shift-f8": "editor::GoToPreviousHunk",
       "ctrl-enter": "assistant::InlineAssist",
       "ctrl-:": "editor::ToggleInlayHints"
     }
@@ -739,7 +739,7 @@
       "tab": "git_panel::FocusEditor",
       "shift-tab": "git_panel::FocusEditor",
       "escape": "git_panel::ToggleFocus",
-      "ctrl-enter": "git::Commit",
+      "ctrl-enter": "git::ShowCommitEditor",
       "alt-enter": "menu::SecondaryConfirm"
     }
   },
@@ -753,7 +753,7 @@
   {
     "context": "GitDiff > Editor",
     "bindings": {
-      "ctrl-enter": "git::Commit"
+      "ctrl-enter": "git::ShowCommitEditor"
     }
   },
   {
@@ -766,14 +766,6 @@
       "alt-up": "git_panel::FocusChanges"
     }
   },
-  {
-    "context": "GitCommit > Editor",
-    "use_key_equivalents": true,
-    "bindings": {
-      "enter": "editor::Newline",
-      "ctrl-enter": "git::Commit"
-    }
-  },
   {
     "context": "CollabPanel && not_editing",
     "bindings": {

assets/keymaps/default-macos.json 🔗

@@ -142,8 +142,8 @@
       "cmd-;": "editor::ToggleLineNumbers",
       "cmd-alt-z": "git::Restore",
       "cmd-alt-y": "git::ToggleStaged",
-      "cmd-y": ["git::StageAndNext", { "whole_excerpt": false }],
-      "cmd-shift-y": ["git::UnstageAndNext", { "whole_excerpt": false }],
+      "cmd-y": "git::StageAndNext",
+      "cmd-shift-y": "git::UnstageAndNext",
       "cmd-'": "editor::ToggleSelectedDiffHunks",
       "cmd-\"": "editor::ExpandAllDiffHunks",
       "cmd-alt-g b": "editor::ToggleGitBlame",
@@ -659,8 +659,8 @@
       "shift-enter": "editor::ExpandExcerpts",
       "cmd-alt-enter": "editor::OpenExcerptsSplit",
       "cmd-shift-e": "pane::RevealInProjectPanel",
-      "cmd-f8": ["editor::GoToHunk", { "center_cursor": true }],
-      "cmd-shift-f8": ["editor::GoToPreviousHunk", { "center_cursor": true }],
+      "cmd-f8": "editor::GoToHunk",
+      "cmd-shift-f8": "editor::GoToPreviousHunk",
       "ctrl-enter": "assistant::InlineAssist",
       "ctrl-:": "editor::ToggleInlayHints"
     }
@@ -760,14 +760,14 @@
       "tab": "git_panel::FocusEditor",
       "shift-tab": "git_panel::FocusEditor",
       "escape": "git_panel::ToggleFocus",
-      "cmd-enter": "git::Commit"
+      "cmd-enter": "git::ShowCommitEditor"
     }
   },
   {
     "context": "GitDiff > Editor",
     "use_key_equivalents": true,
     "bindings": {
-      "cmd-enter": "git::Commit"
+      "cmd-enter": "git::ShowCommitEditor"
     }
   },
   {

assets/keymaps/linux/jetbrains.json 🔗

@@ -42,8 +42,8 @@
       "ctrl-alt-shift-b": "editor::GoToTypeDefinitionSplit",
       "f2": "editor::GoToDiagnostic",
       "shift-f2": "editor::GoToPreviousDiagnostic",
-      "ctrl-alt-shift-down": ["editor::GoToHunk", { "center_cursor": true }],
-      "ctrl-alt-shift-up": ["editor::GoToPreviousHunk", { "center_cursor": true }],
+      "ctrl-alt-shift-down": "editor::GoToHunk",
+      "ctrl-alt-shift-up": "editor::GoToPreviousHunk",
       "ctrl-alt-z": "git::Restore",
       "ctrl-home": "editor::MoveToBeginning",
       "ctrl-end": "editor::MoveToEnd",

assets/keymaps/linux/sublime_text.json 🔗

@@ -43,8 +43,8 @@
       "ctrl-f12": "editor::GoToDefinitionSplit",
       "shift-f12": "editor::FindAllReferences",
       "ctrl-shift-f12": "editor::FindAllReferences",
-      "ctrl-.": ["editor::GoToHunk", { "center_cursor": true }],
-      "ctrl-,": ["editor::GoToPreviousHunk", { "center_cursor": true }],
+      "ctrl-.": "editor::GoToHunk",
+      "ctrl-,": "editor::GoToPreviousHunk",
       "ctrl-k ctrl-u": "editor::ConvertToUpperCase",
       "ctrl-k ctrl-l": "editor::ConvertToLowerCase",
       "shift-alt-m": "markdown::OpenPreviewToTheSide",

assets/keymaps/macos/jetbrains.json 🔗

@@ -40,8 +40,8 @@
       "cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit",
       "f2": "editor::GoToDiagnostic",
       "shift-f2": "editor::GoToPreviousDiagnostic",
-      "ctrl-alt-shift-down": ["editor::GoToHunk", { "center_cursor": true }],
-      "ctrl-alt-shift-up": ["editor::GoToPreviousHunk", { "center_cursor": true }],
+      "ctrl-alt-shift-down": "editor::GoToHunk",
+      "ctrl-alt-shift-up": "editor::GoToPreviousHunk",
       "cmd-home": "editor::MoveToBeginning",
       "cmd-end": "editor::MoveToEnd",
       "cmd-shift-home": "editor::SelectToBeginning",

assets/keymaps/macos/sublime_text.json 🔗

@@ -44,8 +44,8 @@
       "alt-cmd-down": "editor::GoToDefinition",
       "ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
       "alt-shift-cmd-down": "editor::FindAllReferences",
-      "ctrl-.": ["editor::GoToHunk", { "center_cursor": true }],
-      "ctrl-,": ["editor::GoToPreviousHunk", { "center_cursor": true }],
+      "ctrl-.": "editor::GoToHunk",
+      "ctrl-,": "editor::GoToPreviousHunk",
       "cmd-k cmd-u": "editor::ConvertToUpperCase",
       "cmd-k cmd-l": "editor::ConvertToLowerCase",
       "cmd-shift-j": "editor::JoinLines",

assets/keymaps/vim.json 🔗

@@ -238,8 +238,8 @@
       "] x": "vim::SelectSmallerSyntaxNode",
       "] d": "editor::GoToDiagnostic",
       "[ d": "editor::GoToPreviousDiagnostic",
-      "] c": ["editor::GoToHunk", { "center_cursor": true }],
-      "[ c": ["editor::GoToPreviousHunk", { "center_cursor": true }],
+      "] c": "editor::GoToHunk",
+      "[ c": "editor::GoToPreviousHunk",
       "g c": "vim::PushToggleComments"
     }
   },
@@ -448,7 +448,10 @@
       "d": "vim::CurrentLine",
       "s": "vim::PushDeleteSurrounds",
       "o": "editor::ToggleSelectedDiffHunks", // "d o"
-      "p": "git::Restore" // "d p"
+      "shift-o": "git::ToggleStaged",
+      "p": "git::Restore", // "d p"
+      "u": "git::StageAndNext", // "d u"
+      "shift-u": "git::UnstageAndNext" // "d shift-u"
     }
   },
   {

crates/editor/src/actions.rs 🔗

@@ -196,20 +196,6 @@ pub struct DeleteToPreviousWordStart {
     pub ignore_newlines: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
-#[serde(deny_unknown_fields)]
-pub struct GoToHunk {
-    #[serde(default)]
-    pub center_cursor: bool,
-}
-
-#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
-#[serde(deny_unknown_fields)]
-pub struct GoToPreviousHunk {
-    #[serde(default)]
-    pub center_cursor: bool,
-}
-
 #[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
 pub struct FoldAtLevel(pub u32);
 
@@ -240,8 +226,6 @@ impl_actions!(
         ExpandExcerptsDown,
         ExpandExcerptsUp,
         FoldAt,
-        GoToHunk,
-        GoToPreviousHunk,
         HandleInput,
         MoveDownByLines,
         MovePageDown,
@@ -323,6 +307,8 @@ gpui::actions!(
         GoToDefinition,
         GoToDefinitionSplit,
         GoToDiagnostic,
+        GoToHunk,
+        GoToPreviousHunk,
         GoToImplementation,
         GoToImplementationSplit,
         GoToPreviousDiagnostic,

crates/editor/src/editor.rs 🔗

@@ -73,7 +73,7 @@ use futures::{
 };
 use fuzzy::StringMatchCandidate;
 
-use ::git::{status::FileStatus, Restore};
+use ::git::Restore;
 use code_context_menus::{
     AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
     CompletionsMenu, ContextMenuOrigin,
@@ -11488,14 +11488,13 @@ impl Editor {
         }
     }
 
-    fn go_to_next_hunk(&mut self, action: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
+    fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
         let snapshot = self.snapshot(window, cx);
         let selection = self.selections.newest::<Point>(cx);
         self.go_to_hunk_after_or_before_position(
             &snapshot,
             selection.head(),
-            true,
-            action.center_cursor,
+            Direction::Next,
             window,
             cx,
         );
@@ -11505,12 +11504,11 @@ impl Editor {
         &mut self,
         snapshot: &EditorSnapshot,
         position: Point,
-        after: bool,
-        scroll_center: bool,
+        direction: Direction,
         window: &mut Window,
         cx: &mut Context<Editor>,
     ) -> Option<MultiBufferDiffHunk> {
-        let hunk = if after {
+        let hunk = if direction == Direction::Next {
             self.hunk_after_position(snapshot, position)
         } else {
             self.hunk_before_position(snapshot, position)
@@ -11518,11 +11516,7 @@ impl Editor {
 
         if let Some(hunk) = &hunk {
             let destination = Point::new(hunk.row_range.start.0, 0);
-            let autoscroll = if scroll_center {
-                Autoscroll::center()
-            } else {
-                Autoscroll::fit()
-            };
+            let autoscroll = Autoscroll::center();
 
             self.unfold_ranges(&[destination..destination], false, false, cx);
             self.change_selections(Some(autoscroll), window, cx, |s| {
@@ -11552,7 +11546,7 @@ impl Editor {
 
     fn go_to_prev_hunk(
         &mut self,
-        action: &GoToPreviousHunk,
+        _: &GoToPreviousHunk,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
@@ -11561,8 +11555,7 @@ impl Editor {
         self.go_to_hunk_after_or_before_position(
             &snapshot,
             selection.head(),
-            false,
-            action.center_cursor,
+            Direction::Prev,
             window,
             cx,
         );
@@ -13639,20 +13632,20 @@ impl Editor {
 
     pub fn stage_and_next(
         &mut self,
-        action: &::git::StageAndNext,
+        _: &::git::StageAndNext,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        self.do_stage_or_unstage_and_next(true, action.whole_excerpt, window, cx);
+        self.do_stage_or_unstage_and_next(true, window, cx);
     }
 
     pub fn unstage_and_next(
         &mut self,
-        action: &::git::UnstageAndNext,
+        _: &::git::UnstageAndNext,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        self.do_stage_or_unstage_and_next(false, action.whole_excerpt, window, cx);
+        self.do_stage_or_unstage_and_next(false, window, cx);
     }
 
     pub fn stage_or_unstage_diff_hunks(
@@ -13674,102 +13667,33 @@ impl Editor {
     fn do_stage_or_unstage_and_next(
         &mut self,
         stage: bool,
-        whole_excerpt: bool,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        let mut ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
+        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
 
         if ranges.iter().any(|range| range.start != range.end) {
             self.stage_or_unstage_diff_hunks(stage, &ranges[..], window, cx);
             return;
         }
 
-        if !whole_excerpt {
-            let snapshot = self.snapshot(window, cx);
-            let newest_range = self.selections.newest::<Point>(cx).range();
-
-            let run_twice = snapshot
-                .hunks_for_ranges([newest_range])
-                .first()
-                .is_some_and(|hunk| {
-                    let next_line = Point::new(hunk.row_range.end.0 + 1, 0);
-                    self.hunk_after_position(&snapshot, next_line)
-                        .is_some_and(|other| other.row_range == hunk.row_range)
-                });
-
-            if run_twice {
-                self.go_to_next_hunk(
-                    &GoToHunk {
-                        center_cursor: true,
-                    },
-                    window,
-                    cx,
-                );
-            }
-        } else if !self.buffer().read(cx).is_singleton() {
-            self.stage_or_unstage_diff_hunks(stage, &ranges[..], window, cx);
-
-            if let Some((excerpt_id, buffer, range)) = self.active_excerpt(cx) {
-                if buffer.read(cx).is_empty() {
-                    let buffer = buffer.read(cx);
-                    let Some(file) = buffer.file() else {
-                        return;
-                    };
-                    let project_path = project::ProjectPath {
-                        worktree_id: file.worktree_id(cx),
-                        path: file.path().clone(),
-                    };
-                    let Some(project) = self.project.as_ref() else {
-                        return;
-                    };
+        let snapshot = self.snapshot(window, cx);
+        let newest_range = self.selections.newest::<Point>(cx).range();
 
-                    let Some(repo) = project.read(cx).git_store().read(cx).active_repository()
-                    else {
-                        return;
-                    };
+        let run_twice = snapshot
+            .hunks_for_ranges([newest_range])
+            .first()
+            .is_some_and(|hunk| {
+                let next_line = Point::new(hunk.row_range.end.0 + 1, 0);
+                self.hunk_after_position(&snapshot, next_line)
+                    .is_some_and(|other| other.row_range == hunk.row_range)
+            });
 
-                    repo.update(cx, |repo, cx| {
-                        let Some(repo_path) = repo.project_path_to_repo_path(&project_path) else {
-                            return;
-                        };
-                        let Some(status) = repo.repository_entry.status_for_path(&repo_path) else {
-                            return;
-                        };
-                        if stage && status.status == FileStatus::Untracked {
-                            repo.stage_entries(vec![repo_path], cx)
-                                .detach_and_log_err(cx);
-                            return;
-                        }
-                    })
-                }
-                ranges = vec![multi_buffer::Anchor::range_in_buffer(
-                    excerpt_id,
-                    buffer.read(cx).remote_id(),
-                    range,
-                )];
-                self.stage_or_unstage_diff_hunks(stage, &ranges[..], window, cx);
-                let snapshot = self.buffer().read(cx).snapshot(cx);
-                let mut point = ranges.last().unwrap().end.to_point(&snapshot);
-                if point.row < snapshot.max_row().0 {
-                    point.row += 1;
-                    point.column = 0;
-                    point = snapshot.clip_point(point, Bias::Right);
-                    self.change_selections(Some(Autoscroll::top_relative(6)), window, cx, |s| {
-                        s.select_ranges([point..point]);
-                    });
-                }
-                return;
-            }
+        if run_twice {
+            self.go_to_next_hunk(&GoToHunk, window, cx);
         }
         self.stage_or_unstage_diff_hunks(stage, &ranges[..], window, cx);
-        self.go_to_next_hunk(
-            &GoToHunk {
-                center_cursor: true,
-            },
-            window,
-            cx,
-        );
+        self.go_to_next_hunk(&GoToHunk, window, cx);
     }
 
     fn do_stage_or_unstage(

crates/editor/src/editor_tests.rs 🔗

@@ -11413,7 +11413,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
     cx.update_editor(|editor, window, cx| {
         //Wrap around the bottom of the buffer
         for _ in 0..3 {
-            editor.go_to_next_hunk(&GoToHunk::default(), window, cx);
+            editor.go_to_next_hunk(&GoToHunk, window, cx);
         }
     });
 
@@ -11435,7 +11435,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
     cx.update_editor(|editor, window, cx| {
         //Wrap around the top of the buffer
         for _ in 0..2 {
-            editor.go_to_prev_hunk(&GoToPreviousHunk::default(), window, cx);
+            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
         }
     });
 
@@ -11455,7 +11455,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
     );
 
     cx.update_editor(|editor, window, cx| {
-        editor.go_to_prev_hunk(&GoToPreviousHunk::default(), window, cx);
+        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
     });
 
     cx.assert_editor_state(
@@ -11474,7 +11474,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
     );
 
     cx.update_editor(|editor, window, cx| {
-        editor.go_to_prev_hunk(&GoToPreviousHunk::default(), window, cx);
+        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
     });
 
     cx.assert_editor_state(
@@ -11494,7 +11494,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
 
     cx.update_editor(|editor, window, cx| {
         for _ in 0..2 {
-            editor.go_to_prev_hunk(&GoToPreviousHunk::default(), window, cx);
+            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
         }
     });
 
@@ -11518,7 +11518,7 @@ async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext)
     });
 
     cx.update_editor(|editor, window, cx| {
-        editor.go_to_next_hunk(&GoToHunk::default(), window, cx);
+        editor.go_to_next_hunk(&GoToHunk, window, cx);
     });
 
     cx.assert_editor_state(
@@ -13525,7 +13525,7 @@ async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut
     executor.run_until_parked();
 
     cx.update_editor(|editor, window, cx| {
-        editor.go_to_next_hunk(&GoToHunk::default(), window, cx);
+        editor.go_to_next_hunk(&GoToHunk, window, cx);
         editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
     });
     executor.run_until_parked();
@@ -13547,7 +13547,7 @@ async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut
 
     cx.update_editor(|editor, window, cx| {
         for _ in 0..2 {
-            editor.go_to_next_hunk(&GoToHunk::default(), window, cx);
+            editor.go_to_next_hunk(&GoToHunk, window, cx);
             editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
         }
     });
@@ -13570,7 +13570,7 @@ async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut
     );
 
     cx.update_editor(|editor, window, cx| {
-        editor.go_to_next_hunk(&GoToHunk::default(), window, cx);
+        editor.go_to_next_hunk(&GoToHunk, window, cx);
         editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
     });
     executor.run_until_parked();

crates/editor/src/element.rs 🔗

@@ -42,6 +42,7 @@ use gpui::{
     SharedString, Size, StatefulInteractiveElement, Style, Styled, Subscription, TextRun,
     TextStyleRefinement, Window,
 };
+use inline_completion::Direction;
 use itertools::Itertools;
 use language::{
     language_settings::{
@@ -8906,7 +8907,7 @@ fn diff_hunk_controls(
                             move |window, cx| {
                                 Tooltip::for_action_in(
                                     "Next Hunk",
-                                    &GoToHunk::default(),
+                                    &GoToHunk,
                                     &focus_handle,
                                     window,
                                     cx,
@@ -8921,7 +8922,11 @@ fn diff_hunk_controls(
                                     let position =
                                         hunk_range.end.to_point(&snapshot.buffer_snapshot);
                                     editor.go_to_hunk_after_or_before_position(
-                                        &snapshot, position, true, true, window, cx,
+                                        &snapshot,
+                                        position,
+                                        Direction::Next,
+                                        window,
+                                        cx,
                                     );
                                     editor.expand_selected_diff_hunks(cx);
                                 });
@@ -8938,7 +8943,7 @@ fn diff_hunk_controls(
                             move |window, cx| {
                                 Tooltip::for_action_in(
                                     "Previous Hunk",
-                                    &GoToPreviousHunk::default(),
+                                    &GoToPreviousHunk,
                                     &focus_handle,
                                     window,
                                     cx,
@@ -8953,7 +8958,11 @@ fn diff_hunk_controls(
                                     let point =
                                         hunk_range.start.to_point(&snapshot.buffer_snapshot);
                                     editor.go_to_hunk_after_or_before_position(
-                                        &snapshot, point, false, true, window, cx,
+                                        &snapshot,
+                                        point,
+                                        Direction::Prev,
+                                        window,
+                                        cx,
                                     );
                                     editor.expand_selected_diff_hunks(cx);
                                 });

crates/git/src/git.rs 🔗

@@ -36,23 +36,15 @@ pub struct Push {
     pub options: Option<PushOptions>,
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Deserialize, JsonSchema)]
-pub struct StageAndNext {
-    pub whole_excerpt: bool,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Deserialize, JsonSchema)]
-pub struct UnstageAndNext {
-    pub whole_excerpt: bool,
-}
-
-impl_actions!(git, [Push, StageAndNext, UnstageAndNext]);
+impl_actions!(git, [Push]);
 
 actions!(
     git,
     [
         // per-hunk
         ToggleStaged,
+        StageAndNext,
+        UnstageAndNext,
         // per-file
         StageFile,
         UnstageFile,

crates/git_ui/src/project_diff.rs 🔗

@@ -812,10 +812,8 @@ impl Render for ProjectDiffToolbar {
                         el.child(
                             Button::new("stage", "Stage")
                                 .tooltip(Tooltip::for_action_title_in(
-                                    "Stage",
-                                    &StageAndNext {
-                                        whole_excerpt: false,
-                                    },
+                                    "Stage and go to next hunk",
+                                    &StageAndNext,
                                     &focus_handle,
                                 ))
                                 // don't actually disable the button so it's mashable
@@ -825,22 +823,14 @@ impl Render for ProjectDiffToolbar {
                                     Color::Disabled
                                 })
                                 .on_click(cx.listener(|this, _, window, cx| {
-                                    this.dispatch_action(
-                                        &StageAndNext {
-                                            whole_excerpt: false,
-                                        },
-                                        window,
-                                        cx,
-                                    )
+                                    this.dispatch_action(&StageAndNext, window, cx)
                                 })),
                         )
                         .child(
                             Button::new("unstage", "Unstage")
                                 .tooltip(Tooltip::for_action_title_in(
-                                    "Unstage",
-                                    &UnstageAndNext {
-                                        whole_excerpt: false,
-                                    },
+                                    "Unstage and go to next hunk",
+                                    &UnstageAndNext,
                                     &focus_handle,
                                 ))
                                 .color(if button_states.unstage {
@@ -849,13 +839,7 @@ impl Render for ProjectDiffToolbar {
                                     Color::Disabled
                                 })
                                 .on_click(cx.listener(|this, _, window, cx| {
-                                    this.dispatch_action(
-                                        &UnstageAndNext {
-                                            whole_excerpt: false,
-                                        },
-                                        window,
-                                        cx,
-                                    )
+                                    this.dispatch_action(&UnstageAndNext, window, cx)
                                 })),
                         )
                     }),
@@ -869,20 +853,12 @@ impl Render for ProjectDiffToolbar {
                             .shape(ui::IconButtonShape::Square)
                             .tooltip(Tooltip::for_action_title_in(
                                 "Go to previous hunk",
-                                &GoToPreviousHunk {
-                                    center_cursor: false,
-                                },
+                                &GoToPreviousHunk,
                                 &focus_handle,
                             ))
                             .disabled(!button_states.prev_next)
                             .on_click(cx.listener(|this, _, window, cx| {
-                                this.dispatch_action(
-                                    &GoToPreviousHunk {
-                                        center_cursor: true,
-                                    },
-                                    window,
-                                    cx,
-                                )
+                                this.dispatch_action(&GoToPreviousHunk, window, cx)
                             })),
                     )
                     .child(
@@ -890,20 +866,12 @@ impl Render for ProjectDiffToolbar {
                             .shape(ui::IconButtonShape::Square)
                             .tooltip(Tooltip::for_action_title_in(
                                 "Go to next hunk",
-                                &GoToHunk {
-                                    center_cursor: false,
-                                },
+                                &GoToHunk,
                                 &focus_handle,
                             ))
                             .disabled(!button_states.prev_next)
                             .on_click(cx.listener(|this, _, window, cx| {
-                                this.dispatch_action(
-                                    &GoToHunk {
-                                        center_cursor: true,
-                                    },
-                                    window,
-                                    cx,
-                                )
+                                this.dispatch_action(&GoToHunk, window, cx)
                             })),
                     ),
             )

crates/zed/src/zed/quick_action_bar.rs 🔗

@@ -182,18 +182,8 @@ impl Render for QuickActionBar {
                             .action("Next Problem", Box::new(GoToDiagnostic))
                             .action("Previous Problem", Box::new(GoToPreviousDiagnostic))
                             .separator()
-                            .action(
-                                "Next Hunk",
-                                Box::new(GoToHunk {
-                                    center_cursor: true,
-                                }),
-                            )
-                            .action(
-                                "Previous Hunk",
-                                Box::new(GoToPreviousHunk {
-                                    center_cursor: true,
-                                }),
-                            )
+                            .action("Next Hunk", Box::new(GoToHunk))
+                            .action("Previous Hunk", Box::new(GoToPreviousHunk))
                             .separator()
                             .action("Move Line Up", Box::new(MoveLineUp))
                             .action("Move Line Down", Box::new(MoveLineDown))

docs/README.md 🔗

@@ -10,6 +10,12 @@ To preview the docs locally you will need to install [mdBook](https://rust-lang.
 mdbook serve docs
 ```
 
+Before committing, verify that the docs are formatted in the way prettier expects with:
+
+```
+cd docs && pnpm dlx prettier@3.5.0 . --write && cd ..
+```
+
 ## Preprocessor
 
 We have a custom mdbook preprocessor for interfacing with our crates (`crates/docs_preprocessor`).

docs/src/vim.md 🔗

@@ -72,10 +72,15 @@ The following commands use the language server to help you navigate and refactor
 
 ### Git
 
-| Command                   | Default Shortcut |
-| ------------------------- | ---------------- |
-| Go to next git change     | `] c`            |
-| Go to previous git change | `[ c`            |
+| Command                         | Default Shortcut |
+| ------------------------------- | ---------------- |
+| Go to next git change           | `] c`            |
+| Go to previous git change       | `[ c`            |
+| Expand diff hunk                | `d o`            |
+| Toggle staged                   | `d O`            |
+| Stage and next (in diff view)   | `d u`            |
+| Unstage and next (in diff view) | `d U`            |
+| Restore change                  | `d p`            |
 
 ### Treesitter