Skip to content

Commit

Permalink
Allow to toggle git hunk diffs (#11080)
Browse files Browse the repository at this point in the history
Part of #4523

Added two new actions with the default keybindings

```
"cmd-'": "editor::ToggleHunkDiff",
"cmd-\"": "editor::ExpandAllHunkDiffs",
```

that allow to browse git hunk diffs in Zed:


https://github.com/zed-industries/zed/assets/2690773/9a8a7d10-ed06-4960-b4ee-fe28fc5c4768


The hunks are dynamic and alter on user folds and modifications, or
toggle hidden, if the modifications were not adjacent to the expanded
hunk.


Release Notes:

- Added `editor::ToggleHunkDiff` (`cmd-'`) and
`editor::ExpandAllHunkDiffs` (`cmd-"`) actions to browse git hunk diffs
in Zed
  • Loading branch information
SomeoneToIgnore committed May 1, 2024
1 parent 5831d80 commit caa0d35
Show file tree
Hide file tree
Showing 24 changed files with 3,110 additions and 244 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions assets/keymaps/default-linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@
"ctrl-alt-space": "editor::ShowCharacterPalette",
"ctrl-;": "editor::ToggleLineNumbers",
"ctrl-k ctrl-r": "editor::RevertSelectedHunks",
"ctrl-'": "editor::ToggleHunkDiff",
"ctrl-\"": "editor::ExpandAllHunkDiffs",
"ctrl-alt-g b": "editor::ToggleGitBlame"
}
},
Expand Down
2 changes: 2 additions & 0 deletions assets/keymaps/default-macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@
"ctrl-cmd-space": "editor::ShowCharacterPalette",
"cmd-;": "editor::ToggleLineNumbers",
"cmd-alt-z": "editor::RevertSelectedHunks",
"cmd-'": "editor::ToggleHunkDiff",
"cmd-\"": "editor::ExpandAllHunkDiffs",
"cmd-alt-g b": "editor::ToggleGitBlame"
}
},
Expand Down
15 changes: 12 additions & 3 deletions assets/settings/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@
// The list of language servers to use (or disable) for all languages.
//
// This is typically customized on a per-language basis.
"language_servers": ["..."],
"language_servers": [
"..."
],
// When to automatically save edited buffers. This setting can
// take four values.
//
Expand Down Expand Up @@ -428,7 +430,9 @@
"copilot": {
// The set of glob patterns for which copilot should be disabled
// in any matching file.
"disabled_globs": [".env"]
"disabled_globs": [
".env"
]
},
// Settings specific to journaling
"journal": {
Expand Down Expand Up @@ -539,7 +543,12 @@
// Default directories to search for virtual environments, relative
// to the current working directory. We recommend overriding this
// in your project's settings, rather than globally.
"directories": [".env", "env", ".venv", "venv"],
"directories": [
".env",
"env",
".venv",
"venv"
],
// Can also be 'csh', 'fish', and `nushell`
"activate_script": "default"
}
Expand Down
108 changes: 104 additions & 4 deletions crates/collab/src/tests/editor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ use editor::{
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Redo, Rename, RevertSelectedHunks,
ToggleCodeActions, Undo,
},
test::editor_test_context::{AssertionContextManager, EditorTestContext},
test::{
editor_hunks,
editor_test_context::{AssertionContextManager, EditorTestContext},
expanded_hunks, expanded_hunks_background_highlights,
},
Editor,
};
use futures::StreamExt;
use git::diff::DiffHunkStatus;
use gpui::{BorrowAppContext, TestAppContext, VisualContext, VisualTestContext};
use indoc::indoc;
use language::{
Expand Down Expand Up @@ -1875,7 +1880,7 @@ async fn test_inlay_hint_refresh_is_forwarded(
}

#[gpui::test]
async fn test_multiple_types_reverts(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
async fn test_multiple_hunk_types_revert(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
let mut server = TestServer::start(cx_a.executor()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
Expand Down Expand Up @@ -1997,8 +2002,8 @@ struct Row10;"#};
cx_a.executor().run_until_parked();
cx_b.executor().run_until_parked();

// client, selects a range in the updated buffer, and reverts it
// both host and the client observe the reverted state (with one hunk left, not covered by client's selection)
// the client selects a range in the updated buffer, expands it to see the diff for each hunk in the selection
// the host does not see the diffs toggled
editor_cx_b.set_selections_state(indoc! {r#"«ˇstruct Row;
struct Row0.1;
struct Row0.2;
Expand All @@ -2010,11 +2015,106 @@ struct Row10;"#};
struct R»ow9;
struct Row1220;"#});
editor_cx_b
.update_editor(|editor, cx| editor.toggle_hunk_diff(&editor::actions::ToggleHunkDiff, cx));
cx_a.executor().run_until_parked();
cx_b.executor().run_until_parked();
editor_cx_a.update_editor(|editor, cx| {
let snapshot = editor.snapshot(cx);
let all_hunks = editor_hunks(editor, &snapshot, cx);
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
assert_eq!(
expanded_hunks_background_highlights(editor, &snapshot),
Vec::new(),
);
assert_eq!(
all_hunks,
vec![
("".to_string(), DiffHunkStatus::Added, 1..3),
("struct Row2;\n".to_string(), DiffHunkStatus::Removed, 4..4),
("struct Row5;\n".to_string(), DiffHunkStatus::Modified, 6..7),
("struct Row8;\n".to_string(), DiffHunkStatus::Removed, 9..9),
(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
10..10,
),
]
);
assert_eq!(all_expanded_hunks, Vec::new());
});
editor_cx_b.update_editor(|editor, cx| {
let snapshot = editor.snapshot(cx);
let all_hunks = editor_hunks(editor, &snapshot, cx);
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
assert_eq!(
expanded_hunks_background_highlights(editor, &snapshot),
vec![1..3, 8..9],
);
assert_eq!(
all_hunks,
vec![
("".to_string(), DiffHunkStatus::Added, 1..3),
("struct Row2;\n".to_string(), DiffHunkStatus::Removed, 5..5),
("struct Row5;\n".to_string(), DiffHunkStatus::Modified, 8..9),
(
"struct Row8;\n".to_string(),
DiffHunkStatus::Removed,
12..12
),
(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
13..13,
),
]
);
assert_eq!(all_expanded_hunks, &all_hunks[..all_hunks.len() - 1]);
});

// the client reverts the hunks, removing the expanded diffs too
// both host and the client observe the reverted state (with one hunk left, not covered by client's selection)
editor_cx_b.update_editor(|editor, cx| {
editor.revert_selected_hunks(&RevertSelectedHunks, cx);
});
cx_a.executor().run_until_parked();
cx_b.executor().run_until_parked();
editor_cx_a.update_editor(|editor, cx| {
let snapshot = editor.snapshot(cx);
let all_hunks = editor_hunks(editor, &snapshot, cx);
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
assert_eq!(
expanded_hunks_background_highlights(editor, &snapshot),
Vec::new(),
);
assert_eq!(
all_hunks,
vec![(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
10..10,
)]
);
assert_eq!(all_expanded_hunks, Vec::new());
});
editor_cx_b.update_editor(|editor, cx| {
let snapshot = editor.snapshot(cx);
let all_hunks = editor_hunks(editor, &snapshot, cx);
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
assert_eq!(
expanded_hunks_background_highlights(editor, &snapshot),
Vec::new(),
);
assert_eq!(
all_hunks,
vec![(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
10..10,
)]
);
assert_eq!(all_expanded_hunks, Vec::new());
});
editor_cx_a.assert_editor_state(indoc! {r#"struct Row;
struct Row1;
struct Row2;
Expand Down
2 changes: 2 additions & 0 deletions crates/editor/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ gpui::actions!(
TabPrev,
ToggleGitBlame,
ToggleGitBlameInline,
ToggleHunkDiff,
ExpandAllHunkDiffs,
ToggleInlayHints,
ToggleLineNumbers,
ToggleSoftWrap,
Expand Down
47 changes: 26 additions & 21 deletions crates/editor/src/display_map/block_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,28 +364,33 @@ impl BlockMap {
(position.row(), TransformBlock::Custom(block.clone()))
}),
);
blocks_in_edit.extend(
buffer
.excerpt_boundaries_in_range((start_bound, end_bound))
.map(|excerpt_boundary| {
(
wrap_snapshot
.make_wrap_point(Point::new(excerpt_boundary.row, 0), Bias::Left)
.row(),
TransformBlock::ExcerptHeader {
id: excerpt_boundary.id,
buffer: excerpt_boundary.buffer,
range: excerpt_boundary.range,
height: if excerpt_boundary.starts_new_buffer {
self.buffer_header_height
} else {
self.excerpt_header_height
if buffer.show_headers() {
blocks_in_edit.extend(
buffer
.excerpt_boundaries_in_range((start_bound, end_bound))
.map(|excerpt_boundary| {
(
wrap_snapshot
.make_wrap_point(
Point::new(excerpt_boundary.row, 0),
Bias::Left,
)
.row(),
TransformBlock::ExcerptHeader {
id: excerpt_boundary.id,
buffer: excerpt_boundary.buffer,
range: excerpt_boundary.range,
height: if excerpt_boundary.starts_new_buffer {
self.buffer_header_height
} else {
self.excerpt_header_height
},
starts_new_buffer: excerpt_boundary.starts_new_buffer,
},
starts_new_buffer: excerpt_boundary.starts_new_buffer,
},
)
}),
);
)
}),
);
}

// Place excerpt headers above custom blocks on the same row.
blocks_in_edit.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
Expand Down

0 comments on commit caa0d35

Please sign in to comment.