assets/keymaps/default-linux.json π
@@ -982,6 +982,7 @@
"ctrl-shift-enter": "git::Amend",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll",
+ "ctrl-k ctrl-r": "git::RestoreAndNext",
},
},
{
Dino and Afonso created
Add a `git::RestoreAndNext` action that restores the diff hunk at the
cursor and advances to the next hunk. In the git diff view, the default
restore keybinding (`cmd-alt-z` on macOS, `ctrl-k ctrl-r` on
Linux/Windows) is remapped to this action so users can quickly restore
hunks in sequence. Also refactor `go_to_hunk_before_or_after_position`
to accept a `wrap_around` parameter, eliminating duplicated
hunk-navigation logic in `do_stage_or_unstage_and_next` and
`restore_and_next`.
Release Notes:
- Added a `git: restore and next` action that restores the diff hunk at
the cursor and moves to the next one. In the git diff view, the
default restore keybinding (`cmd-alt-z` on macOS, `ctrl-k ctrl-r` on
Linux/Windows) now triggers this action instead of `git: restore`.
---------
Co-authored-by: Afonso <4775087+afonsograca@users.noreply.github.com>
assets/keymaps/default-linux.json | 1
assets/keymaps/default-macos.json | 1
assets/keymaps/default-windows.json | 1
crates/agent_ui/src/agent_diff.rs | 2
crates/editor/src/editor.rs | 107 ++++++++++++++++++++----------
crates/editor/src/editor_tests.rs | 63 ++++++++++++++++++
crates/editor/src/element.rs | 1
crates/git/src/git.rs | 3
crates/git_ui/src/git_panel.rs | 1
9 files changed, 145 insertions(+), 35 deletions(-)
@@ -982,6 +982,7 @@
"ctrl-shift-enter": "git::Amend",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll",
+ "ctrl-k ctrl-r": "git::RestoreAndNext",
},
},
{
@@ -1033,6 +1033,7 @@
"cmd-shift-enter": "git::Amend",
"cmd-ctrl-y": "git::StageAll",
"cmd-ctrl-shift-y": "git::UnstageAll",
+ "cmd-alt-z": "git::RestoreAndNext",
},
},
{
@@ -983,6 +983,7 @@
"ctrl-shift-enter": "git::Amend",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll",
+ "ctrl-k ctrl-r": "git::RestoreAndNext",
},
},
{
@@ -831,6 +831,7 @@ fn render_diff_hunk_controls(
&snapshot,
position,
Direction::Next,
+ true,
window,
cx,
);
@@ -866,6 +867,7 @@ fn render_diff_hunk_controls(
&snapshot,
point,
Direction::Prev,
+ true,
window,
cx,
);
@@ -11683,6 +11683,43 @@ impl Editor {
self.restore_hunks_in_ranges(selections, window, cx);
}
+ /// Restores the diff hunks in the editor's selections and moves the cursor
+ /// to the next diff hunk. Wraps around to the beginning of the buffer if
+ /// not all diff hunks are expanded.
+ pub fn restore_and_next(
+ &mut self,
+ _: &::git::RestoreAndNext,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let selections = self
+ .selections
+ .all(&self.display_snapshot(cx))
+ .into_iter()
+ .map(|selection| selection.range())
+ .collect();
+
+ self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
+ self.restore_hunks_in_ranges(selections, window, cx);
+
+ let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
+ let wrap_around = !all_diff_hunks_expanded;
+ let snapshot = self.snapshot(window, cx);
+ let position = self
+ .selections
+ .newest::<Point>(&snapshot.display_snapshot)
+ .head();
+
+ self.go_to_hunk_before_or_after_position(
+ &snapshot,
+ position,
+ Direction::Next,
+ wrap_around,
+ window,
+ cx,
+ );
+ }
+
pub fn restore_hunks_in_ranges(
&mut self,
ranges: Vec<Range<Point>>,
@@ -17735,6 +17772,7 @@ impl Editor {
&snapshot,
selection.head(),
Direction::Next,
+ true,
window,
cx,
);
@@ -17745,14 +17783,15 @@ impl Editor {
snapshot: &EditorSnapshot,
position: Point,
direction: Direction,
+ wrap_around: bool,
window: &mut Window,
cx: &mut Context<Editor>,
) {
let row = if direction == Direction::Next {
- self.hunk_after_position(snapshot, position)
+ self.hunk_after_position(snapshot, position, wrap_around)
.map(|hunk| hunk.row_range.start)
} else {
- self.hunk_before_position(snapshot, position)
+ self.hunk_before_position(snapshot, position, wrap_around)
};
if let Some(row) = row {
@@ -17770,17 +17809,23 @@ impl Editor {
&mut self,
snapshot: &EditorSnapshot,
position: Point,
+ wrap_around: bool,
) -> Option<MultiBufferDiffHunk> {
- snapshot
+ let result = snapshot
.buffer_snapshot()
.diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
- .find(|hunk| hunk.row_range.start.0 > position.row)
- .or_else(|| {
+ .find(|hunk| hunk.row_range.start.0 > position.row);
+
+ if wrap_around {
+ result.or_else(|| {
snapshot
.buffer_snapshot()
.diff_hunks_in_range(Point::zero()..position)
.find(|hunk| hunk.row_range.end.0 < position.row)
})
+ } else {
+ result
+ }
}
fn go_to_prev_hunk(
@@ -17796,6 +17841,7 @@ impl Editor {
&snapshot,
selection.head(),
Direction::Prev,
+ true,
window,
cx,
);
@@ -17805,11 +17851,15 @@ impl Editor {
&mut self,
snapshot: &EditorSnapshot,
position: Point,
+ wrap_around: bool,
) -> Option<MultiBufferRow> {
- snapshot
- .buffer_snapshot()
- .diff_hunk_before(position)
- .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
+ let result = snapshot.buffer_snapshot().diff_hunk_before(position);
+
+ if wrap_around {
+ result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
+ } else {
+ result
+ }
}
fn go_to_next_change(
@@ -20793,38 +20843,23 @@ impl Editor {
}
self.stage_or_unstage_diff_hunks(stage, ranges, cx);
+
+ let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
+ let wrap_around = !all_diff_hunks_expanded;
let snapshot = self.snapshot(window, cx);
let position = self
.selections
.newest::<Point>(&snapshot.display_snapshot)
.head();
- let mut row = snapshot
- .buffer_snapshot()
- .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
- .find(|hunk| hunk.row_range.start.0 > position.row)
- .map(|hunk| hunk.row_range.start);
-
- let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
- // Outside of the project diff editor, wrap around to the beginning.
- if !all_diff_hunks_expanded {
- row = row.or_else(|| {
- snapshot
- .buffer_snapshot()
- .diff_hunks_in_range(Point::zero()..position)
- .find(|hunk| hunk.row_range.end.0 < position.row)
- .map(|hunk| hunk.row_range.start)
- });
- }
- if let Some(row) = row {
- let destination = Point::new(row.0, 0);
- let autoscroll = Autoscroll::center();
-
- self.unfold_ranges(&[destination..destination], false, false, cx);
- self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
- s.select_ranges([destination..destination]);
- });
- }
+ self.go_to_hunk_before_or_after_position(
+ &snapshot,
+ position,
+ Direction::Next,
+ wrap_around,
+ window,
+ cx,
+ );
}
pub(crate) fn do_stage_or_unstage(
@@ -29249,6 +29284,7 @@ fn render_diff_hunk_controls(
&snapshot,
position,
Direction::Next,
+ true,
window,
cx,
);
@@ -29284,6 +29320,7 @@ fn render_diff_hunk_controls(
&snapshot,
point,
Direction::Prev,
+ true,
window,
cx,
);
@@ -33557,3 +33557,66 @@ comment */ΛΒ»;"#},
assert_text_with_selections(editor, indoc! {r#"let arr = [Β«1, 2, 3]ΛΒ»;"#}, cx);
});
}
+
+#[gpui::test]
+async fn test_restore_and_next(cx: &mut TestAppContext) {
+ init_test(cx, |_| {});
+ let mut cx = EditorTestContext::new(cx).await;
+
+ let diff_base = r#"
+ one
+ two
+ three
+ four
+ five
+ "#
+ .unindent();
+
+ cx.set_state(
+ &r#"
+ ONE
+ two
+ ΛTHREE
+ four
+ FIVE
+ "#
+ .unindent(),
+ );
+ cx.set_head_text(&diff_base);
+
+ cx.update_editor(|editor, window, cx| {
+ editor.set_expand_all_diff_hunks(cx);
+ editor.restore_and_next(&Default::default(), window, cx);
+ });
+ cx.run_until_parked();
+
+ cx.assert_state_with_diff(
+ r#"
+ - one
+ + ONE
+ two
+ three
+ four
+ - Λfive
+ + FIVE
+ "#
+ .unindent(),
+ );
+
+ cx.update_editor(|editor, window, cx| {
+ editor.restore_and_next(&Default::default(), window, cx);
+ });
+ cx.run_until_parked();
+
+ cx.assert_state_with_diff(
+ r#"
+ - one
+ + ONE
+ two
+ three
+ four
+ Λfive
+ "#
+ .unindent(),
+ );
+}
@@ -637,6 +637,7 @@ impl EditorElement {
register_action(editor, window, Editor::accept_edit_prediction);
register_action(editor, window, Editor::restore_file);
register_action(editor, window, Editor::git_restore);
+ register_action(editor, window, Editor::restore_and_next);
register_action(editor, window, Editor::apply_all_diff_hunks);
register_action(editor, window, Editor::apply_selected_diff_hunks);
register_action(editor, window, Editor::open_active_item_in_terminal);
@@ -40,6 +40,9 @@ actions!(
/// Restores the selected hunks to their original state.
#[action(deprecated_aliases = ["editor::RevertSelectedHunks"])]
Restore,
+ /// Restores the selected hunks to their original state and moves to the
+ /// next one.
+ RestoreAndNext,
// per-file
/// Shows git blame information for the current file.
#[action(deprecated_aliases = ["editor::ToggleGitBlame"])]
@@ -1343,6 +1343,7 @@ impl GitPanel {
&snapshot,
language::Point::new(0, 0),
Direction::Next,
+ true,
window,
cx,
);