Expand git diffs when clicking the gutter strip, display their controls in a block above (#18313)
Todo:
* [x] Tooltips for hunk buttons
* [x] Buttons to go to next and previous hunk
* [x] Ellipsis button that opens a context menu with `Revert all`
/cc @iamnbutler @danilo-leal for design 👀
Release Notes:
- Changed the behavior of the git gutter so that diff hunk are expanded
immediately when clicking the gutter, and hunk controls are displayed
above the hunk.
---------
Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
parent
ae6a3d15af
commit
21a023980d
5 changed files with 580 additions and 758 deletions
|
@ -7,18 +7,12 @@ use collections::HashMap;
|
|||
use editor::{
|
||||
actions::{
|
||||
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst, Redo, Rename,
|
||||
RevertSelectedHunks, ToggleCodeActions, Undo,
|
||||
},
|
||||
display_map::DisplayRow,
|
||||
test::{
|
||||
editor_hunks,
|
||||
editor_test_context::{AssertionContextManager, EditorTestContext},
|
||||
expanded_hunks, expanded_hunks_background_highlights,
|
||||
ToggleCodeActions, Undo,
|
||||
},
|
||||
test::editor_test_context::{AssertionContextManager, EditorTestContext},
|
||||
Editor,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use git::diff::DiffHunkStatus;
|
||||
use gpui::{TestAppContext, UpdateGlobal, VisualContext, VisualTestContext};
|
||||
use indoc::indoc;
|
||||
use language::{
|
||||
|
@ -1970,285 +1964,6 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
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;
|
||||
server
|
||||
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
.await;
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
cx_a.update(editor::init);
|
||||
cx_b.update(editor::init);
|
||||
|
||||
client_a.language_registry().add(rust_lang());
|
||||
client_b.language_registry().add(rust_lang());
|
||||
|
||||
let base_text = indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
struct Row2;
|
||||
|
||||
struct Row4;
|
||||
struct Row5;
|
||||
struct Row6;
|
||||
|
||||
struct Row8;
|
||||
struct Row9;
|
||||
struct Row10;"#};
|
||||
|
||||
client_a
|
||||
.fs()
|
||||
.insert_tree(
|
||||
"/a",
|
||||
json!({
|
||||
"main.rs": base_text,
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
active_call_a
|
||||
.update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let project_b = client_b.build_dev_server_project(project_id, cx_b).await;
|
||||
active_call_b
|
||||
.update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let (workspace_a, cx_a) = client_a.build_workspace(&project_a, cx_a);
|
||||
let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
|
||||
|
||||
let editor_a = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
|
||||
let mut editor_cx_a = EditorTestContext {
|
||||
cx: cx_a.clone(),
|
||||
window: cx_a.handle(),
|
||||
editor: editor_a,
|
||||
assertion_cx: AssertionContextManager::new(),
|
||||
};
|
||||
let mut editor_cx_b = EditorTestContext {
|
||||
cx: cx_b.clone(),
|
||||
window: cx_b.handle(),
|
||||
editor: editor_b,
|
||||
assertion_cx: AssertionContextManager::new(),
|
||||
};
|
||||
|
||||
// host edits the file, that differs from the base text, producing diff hunks
|
||||
editor_cx_a.set_state(indoc! {r#"struct Row;
|
||||
struct Row0.1;
|
||||
struct Row0.2;
|
||||
struct Row1;
|
||||
|
||||
struct Row4;
|
||||
struct Row5444;
|
||||
struct Row6;
|
||||
|
||||
struct Row9;
|
||||
struct Row1220;ˇ"#});
|
||||
editor_cx_a.update_editor(|editor, cx| {
|
||||
editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.unwrap()
|
||||
.update(cx, |buffer, cx| {
|
||||
buffer.set_diff_base(Some(base_text.into()), cx);
|
||||
});
|
||||
});
|
||||
editor_cx_b.update_editor(|editor, cx| {
|
||||
editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.unwrap()
|
||||
.update(cx, |buffer, cx| {
|
||||
buffer.set_diff_base(Some(base_text.into()), cx);
|
||||
});
|
||||
});
|
||||
cx_a.executor().run_until_parked();
|
||||
cx_b.executor().run_until_parked();
|
||||
|
||||
// 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;
|
||||
struct Row1;
|
||||
|
||||
struct Row4;
|
||||
struct Row5444;
|
||||
struct Row6;
|
||||
|
||||
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, cx), Vec::new());
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(1)..DisplayRow(3)
|
||||
),
|
||||
(
|
||||
"struct Row2;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(4)..DisplayRow(4)
|
||||
),
|
||||
(
|
||||
"struct Row5;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"struct Row8;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(9)..DisplayRow(9)
|
||||
),
|
||||
(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(10)..DisplayRow(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, cx),
|
||||
vec![DisplayRow(1)..=DisplayRow(2), DisplayRow(8)..=DisplayRow(8)],
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(1)..DisplayRow(3)
|
||||
),
|
||||
(
|
||||
"struct Row2;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(5)..DisplayRow(5)
|
||||
),
|
||||
(
|
||||
"struct Row5;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(8)..DisplayRow(9)
|
||||
),
|
||||
(
|
||||
"struct Row8;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(12)..DisplayRow(12)
|
||||
),
|
||||
(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(13)..DisplayRow(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, cx), Vec::new());
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(10)..DisplayRow(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, cx), Vec::new());
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![(
|
||||
"struct Row10;".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(10)..DisplayRow(10),
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_expanded_hunks, Vec::new());
|
||||
});
|
||||
editor_cx_a.assert_editor_state(indoc! {r#"struct Row;
|
||||
struct Row1;
|
||||
struct Row2;
|
||||
|
||||
struct Row4;
|
||||
struct Row5;
|
||||
struct Row6;
|
||||
|
||||
struct Row8;
|
||||
struct Row9;
|
||||
struct Row1220;ˇ"#});
|
||||
editor_cx_b.assert_editor_state(indoc! {r#"«ˇstruct Row;
|
||||
struct Row1;
|
||||
struct Row2;
|
||||
|
||||
struct Row4;
|
||||
struct Row5;
|
||||
struct Row6;
|
||||
|
||||
struct Row8;
|
||||
struct R»ow9;
|
||||
struct Row1220;"#});
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
let mut server = TestServer::start(cx_a.executor()).await;
|
||||
|
|
|
@ -154,7 +154,7 @@ use theme::{
|
|||
};
|
||||
use ui::{
|
||||
h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
|
||||
ListItem, Popover, Tooltip,
|
||||
ListItem, Popover, PopoverMenuHandle, Tooltip,
|
||||
};
|
||||
use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
|
||||
use workspace::item::{ItemHandle, PreviewTabsSettings};
|
||||
|
@ -562,6 +562,7 @@ pub struct Editor {
|
|||
nav_history: Option<ItemNavHistory>,
|
||||
context_menu: RwLock<Option<ContextMenu>>,
|
||||
mouse_context_menu: Option<MouseContextMenu>,
|
||||
hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
|
||||
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
|
||||
signature_help_state: SignatureHelpState,
|
||||
auto_signature_help: Option<bool>,
|
||||
|
@ -1938,6 +1939,7 @@ impl Editor {
|
|||
nav_history: None,
|
||||
context_menu: RwLock::new(None),
|
||||
mouse_context_menu: None,
|
||||
hunk_controls_menu_handle: PopoverMenuHandle::default(),
|
||||
completion_tasks: Default::default(),
|
||||
signature_help_state: SignatureHelpState::default(),
|
||||
auto_signature_help: None,
|
||||
|
@ -5383,23 +5385,6 @@ impl Editor {
|
|||
}))
|
||||
}
|
||||
|
||||
fn close_hunk_diff_button(
|
||||
&self,
|
||||
hunk: HoveredHunk,
|
||||
row: DisplayRow,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> IconButton {
|
||||
IconButton::new(
|
||||
("close_hunk_diff_indicator", row.0 as usize),
|
||||
ui::IconName::Close,
|
||||
)
|
||||
.shape(ui::IconButtonShape::Square)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Muted)
|
||||
.tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
|
||||
.on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
|
||||
}
|
||||
|
||||
pub fn context_menu_visible(&self) -> bool {
|
||||
self.context_menu
|
||||
.read()
|
||||
|
@ -9335,32 +9320,42 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
|
||||
fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
|
||||
let snapshot = self
|
||||
.display_map
|
||||
.update(cx, |display_map, cx| display_map.snapshot(cx));
|
||||
let selection = self.selections.newest::<Point>(cx);
|
||||
self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
|
||||
}
|
||||
|
||||
if !self.seek_in_direction(
|
||||
&snapshot,
|
||||
selection.head(),
|
||||
fn go_to_hunk_after_position(
|
||||
&mut self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
position: Point,
|
||||
cx: &mut ViewContext<'_, Editor>,
|
||||
) -> Option<MultiBufferDiffHunk> {
|
||||
if let Some(hunk) = self.go_to_next_hunk_in_direction(
|
||||
snapshot,
|
||||
position,
|
||||
false,
|
||||
snapshot.buffer_snapshot.git_diff_hunks_in_range(
|
||||
MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
|
||||
),
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(MultiBufferRow(position.row + 1)..MultiBufferRow::MAX),
|
||||
cx,
|
||||
) {
|
||||
let wrapped_point = Point::zero();
|
||||
self.seek_in_direction(
|
||||
&snapshot,
|
||||
wrapped_point,
|
||||
true,
|
||||
snapshot.buffer_snapshot.git_diff_hunks_in_range(
|
||||
MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
|
||||
),
|
||||
cx,
|
||||
);
|
||||
return Some(hunk);
|
||||
}
|
||||
|
||||
let wrapped_point = Point::zero();
|
||||
self.go_to_next_hunk_in_direction(
|
||||
snapshot,
|
||||
wrapped_point,
|
||||
true,
|
||||
snapshot.buffer_snapshot.git_diff_hunks_in_range(
|
||||
MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
|
||||
|
@ -9369,52 +9364,65 @@ impl Editor {
|
|||
.update(cx, |display_map, cx| display_map.snapshot(cx));
|
||||
let selection = self.selections.newest::<Point>(cx);
|
||||
|
||||
if !self.seek_in_direction(
|
||||
&snapshot,
|
||||
selection.head(),
|
||||
false,
|
||||
snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
|
||||
MultiBufferRow(0)..MultiBufferRow(selection.head().row),
|
||||
),
|
||||
cx,
|
||||
) {
|
||||
let wrapped_point = snapshot.buffer_snapshot.max_point();
|
||||
self.seek_in_direction(
|
||||
&snapshot,
|
||||
wrapped_point,
|
||||
true,
|
||||
snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
|
||||
MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
|
||||
),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
|
||||
}
|
||||
|
||||
fn seek_in_direction(
|
||||
fn go_to_hunk_before_position(
|
||||
&mut self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
position: Point,
|
||||
cx: &mut ViewContext<'_, Editor>,
|
||||
) -> Option<MultiBufferDiffHunk> {
|
||||
if let Some(hunk) = self.go_to_next_hunk_in_direction(
|
||||
snapshot,
|
||||
position,
|
||||
false,
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(position.row)),
|
||||
cx,
|
||||
) {
|
||||
return Some(hunk);
|
||||
}
|
||||
|
||||
let wrapped_point = snapshot.buffer_snapshot.max_point();
|
||||
self.go_to_next_hunk_in_direction(
|
||||
snapshot,
|
||||
wrapped_point,
|
||||
true,
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(wrapped_point.row)),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
fn go_to_next_hunk_in_direction(
|
||||
&mut self,
|
||||
snapshot: &DisplaySnapshot,
|
||||
initial_point: Point,
|
||||
is_wrapped: bool,
|
||||
hunks: impl Iterator<Item = MultiBufferDiffHunk>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
) -> Option<MultiBufferDiffHunk> {
|
||||
let display_point = initial_point.to_display_point(snapshot);
|
||||
let mut hunks = hunks
|
||||
.map(|hunk| diff_hunk_to_display(&hunk, snapshot))
|
||||
.filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
|
||||
.map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
|
||||
.filter(|(display_hunk, _)| {
|
||||
is_wrapped || !display_hunk.contains_display_row(display_point.row())
|
||||
})
|
||||
.dedup();
|
||||
|
||||
if let Some(hunk) = hunks.next() {
|
||||
if let Some((display_hunk, hunk)) = hunks.next() {
|
||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
let row = hunk.start_display_row();
|
||||
let row = display_hunk.start_display_row();
|
||||
let point = DisplayPoint::new(row, 0);
|
||||
s.select_display_ranges([point..point]);
|
||||
});
|
||||
|
||||
true
|
||||
Some(hunk)
|
||||
} else {
|
||||
false
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9623,7 +9623,7 @@ async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext)
|
|||
cx.update_editor(|editor, cx| {
|
||||
//Wrap around the bottom of the buffer
|
||||
for _ in 0..3 {
|
||||
editor.go_to_hunk(&GoToHunk, cx);
|
||||
editor.go_to_next_hunk(&GoToHunk, cx);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9709,7 +9709,7 @@ async fn go_to_hunk(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext)
|
|||
|
||||
//Make sure that the fold only gets one hunk
|
||||
for _ in 0..4 {
|
||||
editor.go_to_hunk(&GoToHunk, cx);
|
||||
editor.go_to_next_hunk(&GoToHunk, cx);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -11226,7 +11226,7 @@ async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
|
||||
cx.update_editor(|editor, cx| {
|
||||
for _ in 0..4 {
|
||||
editor.go_to_hunk(&GoToHunk, cx);
|
||||
editor.go_to_next_hunk(&GoToHunk, cx);
|
||||
editor.toggle_hunk_diff(&ToggleHunkDiff, cx);
|
||||
}
|
||||
});
|
||||
|
@ -11249,18 +11249,13 @@ async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
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, cx),
|
||||
vec![DisplayRow(1)..=DisplayRow(1), DisplayRow(7)..=DisplayRow(7), DisplayRow(9)..=DisplayRow(9)],
|
||||
"After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(1)..DisplayRow(2)),
|
||||
("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(4)..DisplayRow(4)),
|
||||
(" println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(7)..DisplayRow(8)),
|
||||
("".to_string(), DiffHunkStatus::Added, DisplayRow(9)..DisplayRow(10)),
|
||||
("use some::mod;\n".to_string(), DiffHunkStatus::Modified, DisplayRow(2)..DisplayRow(3)),
|
||||
("const A: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(6)..DisplayRow(6)),
|
||||
(" println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(10)..DisplayRow(11)),
|
||||
("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(14)),
|
||||
],
|
||||
"After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
|
||||
(from modified and removed hunks)"
|
||||
|
@ -11269,6 +11264,11 @@ async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
all_hunks, all_expanded_hunks,
|
||||
"Editor hunks should not change and all be expanded"
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(2)..=DisplayRow(2), DisplayRow(10)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(13)],
|
||||
"After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
|
||||
);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, cx| {
|
||||
|
@ -11311,7 +11311,7 @@ async fn test_toggled_diff_base_change(
|
|||
const B: u32 = 42;
|
||||
const C: u32 = 42;
|
||||
|
||||
fn main(ˇ) {
|
||||
fn main() {
|
||||
println!("hello");
|
||||
|
||||
println!("world");
|
||||
|
@ -11356,9 +11356,9 @@ async fn test_toggled_diff_base_change(
|
|||
DisplayRow(3)..DisplayRow(3)
|
||||
),
|
||||
(
|
||||
"fn main(ˇ) {\n println!(\"hello\");\n".to_string(),
|
||||
" println!(\"hello\");\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(5)..DisplayRow(7)
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"".to_string(),
|
||||
|
@ -11390,22 +11390,18 @@ async fn test_toggled_diff_base_change(
|
|||
"#
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
cx.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, cx),
|
||||
vec![DisplayRow(9)..=DisplayRow(10), DisplayRow(13)..=DisplayRow(14)],
|
||||
"After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(1)..DisplayRow(1)),
|
||||
("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(5)..DisplayRow(5)),
|
||||
("fn main(ˇ) {\n println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(9)..DisplayRow(11)),
|
||||
("".to_string(), DiffHunkStatus::Added, DisplayRow(13)..DisplayRow(15)),
|
||||
("use some::mod1;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(2)..DisplayRow(2)),
|
||||
("const B: u32 = 42;\n".to_string(), DiffHunkStatus::Removed, DisplayRow(7)..DisplayRow(7)),
|
||||
(" println!(\"hello\");\n".to_string(), DiffHunkStatus::Modified, DisplayRow(12)..DisplayRow(13)),
|
||||
("".to_string(), DiffHunkStatus::Added, DisplayRow(16)..DisplayRow(18)),
|
||||
],
|
||||
"After expanding, all hunks' display rows should have shifted by the amount of deleted lines added \
|
||||
(from modified and removed hunks)"
|
||||
|
@ -11414,6 +11410,11 @@ async fn test_toggled_diff_base_change(
|
|||
all_hunks, all_expanded_hunks,
|
||||
"Editor hunks should not change and all be expanded"
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(12)..=DisplayRow(12), DisplayRow(16)..=DisplayRow(17)],
|
||||
"After expanding, all git additions should be highlighted for Modified (split into added and removed) and Added hunks"
|
||||
);
|
||||
});
|
||||
|
||||
cx.set_diff_base(Some("new diff base!"));
|
||||
|
@ -11459,7 +11460,7 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
const B: u32 = 42;
|
||||
const C: u32 = 42;
|
||||
|
||||
fn main(ˇ) {
|
||||
fn main() {
|
||||
println!("hello");
|
||||
|
||||
println!("world");
|
||||
|
@ -11520,9 +11521,9 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
DisplayRow(3)..DisplayRow(3)
|
||||
),
|
||||
(
|
||||
"fn main(ˇ) {\n println!(\"hello\");\n".to_string(),
|
||||
" println!(\"hello\");\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(5)..DisplayRow(7)
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"".to_string(),
|
||||
|
@ -11576,50 +11577,50 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
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, cx),
|
||||
vec![
|
||||
DisplayRow(9)..=DisplayRow(10),
|
||||
DisplayRow(13)..=DisplayRow(14),
|
||||
DisplayRow(19)..=DisplayRow(19)
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
(
|
||||
"use some::mod1;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(1)..DisplayRow(1)
|
||||
DisplayRow(2)..DisplayRow(2)
|
||||
),
|
||||
(
|
||||
"const B: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(5)..DisplayRow(5)
|
||||
DisplayRow(7)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"fn main(ˇ) {\n println!(\"hello\");\n".to_string(),
|
||||
" println!(\"hello\");\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(9)..DisplayRow(11)
|
||||
DisplayRow(12)..DisplayRow(13)
|
||||
),
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(13)..DisplayRow(15)
|
||||
DisplayRow(16)..DisplayRow(18)
|
||||
),
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(19)..DisplayRow(20)
|
||||
DisplayRow(23)..DisplayRow(24)
|
||||
),
|
||||
(
|
||||
"fn another2() {\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(23)..DisplayRow(23)
|
||||
DisplayRow(28)..DisplayRow(28)
|
||||
),
|
||||
],
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![
|
||||
DisplayRow(12)..=DisplayRow(12),
|
||||
DisplayRow(16)..=DisplayRow(17),
|
||||
DisplayRow(23)..=DisplayRow(23)
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, cx| editor.fold_selected_ranges(&FoldSelectedRanges, cx));
|
||||
|
@ -11653,11 +11654,6 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
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, cx),
|
||||
vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(5)..=DisplayRow(5)],
|
||||
"Only one hunk is left not folded, its highlight should be visible"
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
|
@ -11672,7 +11668,7 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
DisplayRow(0)..DisplayRow(0)
|
||||
),
|
||||
(
|
||||
"fn main(ˇ) {\n println!(\"hello\");\n".to_string(),
|
||||
" println!(\"hello\");\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(0)..DisplayRow(0)
|
||||
),
|
||||
|
@ -11684,12 +11680,12 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(5)..DisplayRow(6)
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"fn another2() {\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(9)..DisplayRow(9)
|
||||
DisplayRow(11)..DisplayRow(11)
|
||||
),
|
||||
],
|
||||
"Hunk list should still return shifted folded hunks"
|
||||
|
@ -11700,16 +11696,21 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(5)..DisplayRow(6)
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"fn another2() {\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(9)..DisplayRow(9)
|
||||
DisplayRow(11)..DisplayRow(11)
|
||||
),
|
||||
],
|
||||
"Only non-folded hunks should be left expanded"
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(0)..=DisplayRow(0), DisplayRow(6)..=DisplayRow(6)],
|
||||
"Only one hunk is left not folded, its highlight should be visible"
|
||||
);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, cx| {
|
||||
|
@ -11746,51 +11747,51 @@ async fn test_fold_unfold_diff(executor: BackgroundExecutor, cx: &mut gpui::Test
|
|||
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, cx),
|
||||
vec![
|
||||
DisplayRow(9)..=DisplayRow(10),
|
||||
DisplayRow(13)..=DisplayRow(14),
|
||||
DisplayRow(19)..=DisplayRow(19)
|
||||
],
|
||||
"After unfolding, all hunk diffs should be visible again"
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![
|
||||
(
|
||||
"use some::mod1;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(1)..DisplayRow(1)
|
||||
DisplayRow(2)..DisplayRow(2)
|
||||
),
|
||||
(
|
||||
"const B: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(5)..DisplayRow(5)
|
||||
DisplayRow(7)..DisplayRow(7)
|
||||
),
|
||||
(
|
||||
"fn main(ˇ) {\n println!(\"hello\");\n".to_string(),
|
||||
" println!(\"hello\");\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(9)..DisplayRow(11)
|
||||
DisplayRow(12)..DisplayRow(13)
|
||||
),
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(13)..DisplayRow(15)
|
||||
DisplayRow(16)..DisplayRow(18)
|
||||
),
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(19)..DisplayRow(20)
|
||||
DisplayRow(23)..DisplayRow(24)
|
||||
),
|
||||
(
|
||||
"fn another2() {\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(23)..DisplayRow(23)
|
||||
DisplayRow(28)..DisplayRow(28)
|
||||
),
|
||||
],
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![
|
||||
DisplayRow(12)..=DisplayRow(12),
|
||||
DisplayRow(16)..=DisplayRow(17),
|
||||
DisplayRow(23)..=DisplayRow(23)
|
||||
],
|
||||
"After unfolding, all hunk diffs should be visible again"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -11940,17 +11941,17 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
|||
(
|
||||
"bbbb\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(5)..DisplayRow(5),
|
||||
DisplayRow(6)..DisplayRow(6),
|
||||
),
|
||||
(
|
||||
"nnnn\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(23)..DisplayRow(24),
|
||||
DisplayRow(25)..DisplayRow(26),
|
||||
),
|
||||
(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(43)..DisplayRow(44),
|
||||
DisplayRow(46)..DisplayRow(47),
|
||||
),
|
||||
];
|
||||
|
||||
|
@ -11975,8 +11976,8 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
|||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![
|
||||
DisplayRow(23)..=DisplayRow(23),
|
||||
DisplayRow(43)..=DisplayRow(43)
|
||||
DisplayRow(25)..=DisplayRow(25),
|
||||
DisplayRow(46)..=DisplayRow(46)
|
||||
],
|
||||
);
|
||||
assert_eq!(all_hunks, expected_all_hunks_shifted);
|
||||
|
@ -12007,8 +12008,8 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut gpui::TestAppContext)
|
|||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![
|
||||
DisplayRow(23)..=DisplayRow(23),
|
||||
DisplayRow(43)..=DisplayRow(43)
|
||||
DisplayRow(25)..=DisplayRow(25),
|
||||
DisplayRow(46)..=DisplayRow(46)
|
||||
],
|
||||
);
|
||||
assert_eq!(all_hunks, expected_all_hunks_shifted);
|
||||
|
@ -12116,12 +12117,12 @@ async fn test_edits_around_toggled_additions(
|
|||
vec![(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(4)..DisplayRow(7)
|
||||
DisplayRow(5)..DisplayRow(8)
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(4)..=DisplayRow(6)]
|
||||
vec![DisplayRow(5)..=DisplayRow(7)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
});
|
||||
|
@ -12156,12 +12157,12 @@ async fn test_edits_around_toggled_additions(
|
|||
vec![(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(4)..DisplayRow(8)
|
||||
DisplayRow(5)..DisplayRow(9)
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(4)..=DisplayRow(6)],
|
||||
vec![DisplayRow(5)..=DisplayRow(7)],
|
||||
"Edited hunk should have one more line added"
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -12201,12 +12202,12 @@ async fn test_edits_around_toggled_additions(
|
|||
vec![(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(4)..DisplayRow(9)
|
||||
DisplayRow(5)..DisplayRow(10)
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(4)..=DisplayRow(6)],
|
||||
vec![DisplayRow(5)..=DisplayRow(7)],
|
||||
"Edited hunk should have one more line added"
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12245,12 +12246,12 @@ async fn test_edits_around_toggled_additions(
|
|||
vec![(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(4)..DisplayRow(8)
|
||||
DisplayRow(5)..DisplayRow(9)
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(4)..=DisplayRow(6)],
|
||||
vec![DisplayRow(5)..=DisplayRow(7)],
|
||||
"Deleting a line should shrint the hunk"
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -12293,12 +12294,12 @@ async fn test_edits_around_toggled_additions(
|
|||
vec![(
|
||||
"".to_string(),
|
||||
DiffHunkStatus::Added,
|
||||
DisplayRow(5)..DisplayRow(6)
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(5)..=DisplayRow(5)]
|
||||
vec![DisplayRow(6)..=DisplayRow(6)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
});
|
||||
|
@ -12335,7 +12336,7 @@ async fn test_edits_around_toggled_additions(
|
|||
(
|
||||
"const A: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(2)..DisplayRow(2)
|
||||
DisplayRow(3)..DisplayRow(3)
|
||||
)
|
||||
]
|
||||
);
|
||||
|
@ -12349,7 +12350,7 @@ async fn test_edits_around_toggled_additions(
|
|||
vec![(
|
||||
"const A: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(2)..DisplayRow(2)
|
||||
DisplayRow(3)..DisplayRow(3)
|
||||
)],
|
||||
"Should open hunks that were adjacent to the stale addition one"
|
||||
);
|
||||
|
@ -12445,7 +12446,7 @@ async fn test_edits_around_toggled_deletions(
|
|||
vec![(
|
||||
"const A: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(4)..DisplayRow(4)
|
||||
DisplayRow(5)..DisplayRow(5)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12485,7 +12486,7 @@ async fn test_edits_around_toggled_deletions(
|
|||
vec![(
|
||||
"const A: u32 = 42;\nconst B: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(5)..DisplayRow(5)
|
||||
DisplayRow(6)..DisplayRow(6)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12520,7 +12521,7 @@ async fn test_edits_around_toggled_deletions(
|
|||
vec![(
|
||||
"const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(6)..DisplayRow(6)
|
||||
DisplayRow(7)..DisplayRow(7)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12554,12 +12555,12 @@ async fn test_edits_around_toggled_deletions(
|
|||
vec![(
|
||||
"const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(7)..DisplayRow(8)
|
||||
DisplayRow(8)..DisplayRow(9)
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(7)..=DisplayRow(7)],
|
||||
vec![DisplayRow(8)..=DisplayRow(8)],
|
||||
"Modified expanded hunks should display additions and highlight their background"
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12653,14 +12654,14 @@ async fn test_edits_around_toggled_modifications(
|
|||
let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(6)..=DisplayRow(6)],
|
||||
vec![DisplayRow(7)..=DisplayRow(7)],
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![(
|
||||
"const C: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
DisplayRow(7)..DisplayRow(8)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12696,7 +12697,7 @@ async fn test_edits_around_toggled_modifications(
|
|||
let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(6)..=DisplayRow(6)],
|
||||
vec![DisplayRow(7)..=DisplayRow(7)],
|
||||
"Modified hunk should grow highlighted lines on more text additions"
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -12704,7 +12705,7 @@ async fn test_edits_around_toggled_modifications(
|
|||
vec![(
|
||||
"const C: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(9)
|
||||
DisplayRow(7)..DisplayRow(10)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12742,14 +12743,14 @@ async fn test_edits_around_toggled_modifications(
|
|||
let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(6)..=DisplayRow(8)],
|
||||
vec![DisplayRow(7)..=DisplayRow(9)],
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![(
|
||||
"const B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(9)
|
||||
DisplayRow(7)..DisplayRow(10)
|
||||
)],
|
||||
"Modified hunk should grow deleted lines on text deletions above"
|
||||
);
|
||||
|
@ -12786,7 +12787,7 @@ async fn test_edits_around_toggled_modifications(
|
|||
let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(6)..=DisplayRow(9)],
|
||||
vec![DisplayRow(7)..=DisplayRow(10)],
|
||||
"Modified hunk should grow deleted lines on text modifications above"
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -12794,7 +12795,7 @@ async fn test_edits_around_toggled_modifications(
|
|||
vec![(
|
||||
"const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(10)
|
||||
DisplayRow(7)..DisplayRow(11)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12830,7 +12831,7 @@ async fn test_edits_around_toggled_modifications(
|
|||
let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(6)..=DisplayRow(8)],
|
||||
vec![DisplayRow(7)..=DisplayRow(9)],
|
||||
"Modified hunk should grow shrink lines on modification lines removal"
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -12838,7 +12839,7 @@ async fn test_edits_around_toggled_modifications(
|
|||
vec![(
|
||||
"const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(9)
|
||||
DisplayRow(7)..DisplayRow(10)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12880,7 +12881,7 @@ async fn test_edits_around_toggled_modifications(
|
|||
"const A: u32 = 42;\nconst B: u32 = 42;\nconst C: u32 = 42;\nconst D: u32 = 42;\n"
|
||||
.to_string(),
|
||||
DiffHunkStatus::Removed,
|
||||
DisplayRow(7)..DisplayRow(7)
|
||||
DisplayRow(8)..DisplayRow(8)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
@ -12974,14 +12975,14 @@ async fn test_multiple_expanded_hunks_merge(
|
|||
let all_expanded_hunks = expanded_hunks(editor, &snapshot, cx);
|
||||
assert_eq!(
|
||||
expanded_hunks_background_highlights(editor, cx),
|
||||
vec![DisplayRow(6)..=DisplayRow(6)],
|
||||
vec![DisplayRow(7)..=DisplayRow(7)],
|
||||
);
|
||||
assert_eq!(
|
||||
all_hunks,
|
||||
vec![(
|
||||
"const C: u32 = 42;\n".to_string(),
|
||||
DiffHunkStatus::Modified,
|
||||
DisplayRow(6)..DisplayRow(7)
|
||||
DisplayRow(7)..DisplayRow(8)
|
||||
)]
|
||||
);
|
||||
assert_eq!(all_hunks, all_expanded_hunks);
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
hover_popover::{
|
||||
self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
|
||||
},
|
||||
hunk_diff::{diff_hunk_to_display, DisplayDiffHunk, ExpandedHunk},
|
||||
hunk_diff::{diff_hunk_to_display, DisplayDiffHunk},
|
||||
hunk_status,
|
||||
items::BufferSearchHighlights,
|
||||
mouse_context_menu::{self, MenuPosition, MouseContextMenu},
|
||||
|
@ -20,8 +20,8 @@ use crate::{
|
|||
DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings,
|
||||
EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
|
||||
HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, LineDown, LineUp, OpenExcerpts, PageDown,
|
||||
PageUp, Point, RangeToAnchorExt, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap,
|
||||
ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
|
||||
PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint,
|
||||
CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
|
||||
};
|
||||
use client::ParticipantIndex;
|
||||
use collections::{BTreeMap, HashMap};
|
||||
|
@ -302,7 +302,7 @@ impl EditorElement {
|
|||
}
|
||||
register_action(view, cx, Editor::go_to_diagnostic);
|
||||
register_action(view, cx, Editor::go_to_prev_diagnostic);
|
||||
register_action(view, cx, Editor::go_to_hunk);
|
||||
register_action(view, cx, Editor::go_to_next_hunk);
|
||||
register_action(view, cx, Editor::go_to_prev_hunk);
|
||||
register_action(view, cx, |editor, a, cx| {
|
||||
editor.go_to_definition(a, cx).detach_and_log_err(cx);
|
||||
|
@ -489,28 +489,7 @@ impl EditorElement {
|
|||
let mut modifiers = event.modifiers;
|
||||
|
||||
if let Some(hovered_hunk) = hovered_hunk {
|
||||
if modifiers.control || modifiers.platform {
|
||||
editor.toggle_hovered_hunk(&hovered_hunk, cx);
|
||||
} else {
|
||||
let display_range = hovered_hunk
|
||||
.multi_buffer_range
|
||||
.clone()
|
||||
.to_display_points(&position_map.snapshot);
|
||||
let hunk_bounds = Self::diff_hunk_bounds(
|
||||
&position_map.snapshot,
|
||||
position_map.line_height,
|
||||
gutter_hitbox.bounds,
|
||||
&DisplayDiffHunk::Unfolded {
|
||||
diff_base_byte_range: hovered_hunk.diff_base_byte_range.clone(),
|
||||
display_row_range: display_range.start.row()..display_range.end.row(),
|
||||
multi_buffer_range: hovered_hunk.multi_buffer_range.clone(),
|
||||
status: hovered_hunk.status,
|
||||
},
|
||||
);
|
||||
if hunk_bounds.contains(&event.position) {
|
||||
editor.open_hunk_context_menu(hovered_hunk, event.position, cx);
|
||||
}
|
||||
}
|
||||
editor.toggle_hovered_hunk(&hovered_hunk, cx);
|
||||
cx.notify();
|
||||
return;
|
||||
} else if gutter_hitbox.is_hovered(cx) {
|
||||
|
@ -1303,13 +1282,13 @@ impl EditorElement {
|
|||
let display_hunks = buffer_snapshot
|
||||
.git_diff_hunks_in_range(buffer_start_row..buffer_end_row)
|
||||
.filter_map(|hunk| {
|
||||
let mut display_hunk = diff_hunk_to_display(&hunk, snapshot);
|
||||
let display_hunk = diff_hunk_to_display(&hunk, snapshot);
|
||||
|
||||
if let DisplayDiffHunk::Unfolded {
|
||||
multi_buffer_range,
|
||||
status,
|
||||
..
|
||||
} = &mut display_hunk
|
||||
} = &display_hunk
|
||||
{
|
||||
let mut is_expanded = false;
|
||||
while let Some(expanded_hunk) = expanded_hunks.peek() {
|
||||
|
@ -1332,11 +1311,7 @@ impl EditorElement {
|
|||
}
|
||||
match status {
|
||||
DiffHunkStatus::Added => {}
|
||||
DiffHunkStatus::Modified => {
|
||||
if is_expanded {
|
||||
*status = DiffHunkStatus::Added;
|
||||
}
|
||||
}
|
||||
DiffHunkStatus::Modified => {}
|
||||
DiffHunkStatus::Removed => {
|
||||
if is_expanded {
|
||||
return None;
|
||||
|
@ -3371,9 +3346,6 @@ impl EditorElement {
|
|||
for test_indicator in layout.test_indicators.iter_mut() {
|
||||
test_indicator.paint(cx);
|
||||
}
|
||||
for close_indicator in layout.close_indicators.iter_mut() {
|
||||
close_indicator.paint(cx);
|
||||
}
|
||||
|
||||
if let Some(indicator) = layout.code_actions_indicator.as_mut() {
|
||||
indicator.paint(cx);
|
||||
|
@ -4159,46 +4131,6 @@ impl EditorElement {
|
|||
+ 1;
|
||||
self.column_pixels(digit_count, cx)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn layout_hunk_diff_close_indicators(
|
||||
&self,
|
||||
line_height: Pixels,
|
||||
scroll_pixel_position: gpui::Point<Pixels>,
|
||||
gutter_dimensions: &GutterDimensions,
|
||||
gutter_hitbox: &Hitbox,
|
||||
rows_with_hunk_bounds: &HashMap<DisplayRow, Bounds<Pixels>>,
|
||||
expanded_hunks_by_rows: HashMap<DisplayRow, ExpandedHunk>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Vec<AnyElement> {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
expanded_hunks_by_rows
|
||||
.into_iter()
|
||||
.map(|(display_row, hunk)| {
|
||||
let button = editor.close_hunk_diff_button(
|
||||
HoveredHunk {
|
||||
multi_buffer_range: hunk.hunk_range,
|
||||
status: hunk.status,
|
||||
diff_base_byte_range: hunk.diff_base_byte_range,
|
||||
},
|
||||
display_row,
|
||||
cx,
|
||||
);
|
||||
|
||||
prepaint_gutter_button(
|
||||
button,
|
||||
display_row,
|
||||
line_height,
|
||||
gutter_dimensions,
|
||||
scroll_pixel_position,
|
||||
gutter_hitbox,
|
||||
rows_with_hunk_bounds,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -5549,15 +5481,6 @@ impl Element for EditorElement {
|
|||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let close_indicators = self.layout_hunk_diff_close_indicators(
|
||||
line_height,
|
||||
scroll_pixel_position,
|
||||
&gutter_dimensions,
|
||||
&gutter_hitbox,
|
||||
&rows_with_hunk_bounds,
|
||||
expanded_add_hunks_by_rows,
|
||||
cx,
|
||||
);
|
||||
|
||||
self.layout_signature_help(
|
||||
&hitbox,
|
||||
|
@ -5670,7 +5593,6 @@ impl Element for EditorElement {
|
|||
selections,
|
||||
mouse_context_menu,
|
||||
test_indicators,
|
||||
close_indicators,
|
||||
code_actions_indicator,
|
||||
gutter_fold_toggles,
|
||||
crease_trailers,
|
||||
|
@ -5812,7 +5734,6 @@ pub struct EditorLayout {
|
|||
selections: Vec<(PlayerColor, Vec<SelectionLayout>)>,
|
||||
code_actions_indicator: Option<AnyElement>,
|
||||
test_indicators: Vec<AnyElement>,
|
||||
close_indicators: Vec<AnyElement>,
|
||||
gutter_fold_toggles: Vec<Option<AnyElement>>,
|
||||
crease_trailers: Vec<Option<CreaseTrailerLayout>>,
|
||||
mouse_context_menu: Option<AnyElement>,
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
use collections::{hash_map, HashMap, HashSet};
|
||||
use git::diff::DiffHunkStatus;
|
||||
use gpui::{Action, AppContext, CursorStyle, Hsla, Model, MouseButton, Subscription, Task, View};
|
||||
use gpui::{Action, AnchorCorner, AppContext, CursorStyle, Hsla, Model, MouseButton, Task, View};
|
||||
use language::{Buffer, BufferId, Point};
|
||||
use multi_buffer::{
|
||||
Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow,
|
||||
MultiBufferSnapshot, ToPoint,
|
||||
};
|
||||
use settings::SettingsStore;
|
||||
use std::{
|
||||
ops::{Range, RangeInclusive},
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::{
|
||||
prelude::*, ActiveTheme, ContextMenu, InteractiveElement, IntoElement, ParentElement, Pixels,
|
||||
Styled, ViewContext, VisualContext,
|
||||
prelude::*, ActiveTheme, ContextMenu, IconButtonShape, InteractiveElement, IntoElement,
|
||||
ParentElement, PopoverMenu, Styled, Tooltip, ViewContext, VisualContext,
|
||||
};
|
||||
use util::{debug_panic, RangeExt};
|
||||
use util::RangeExt;
|
||||
|
||||
use crate::{
|
||||
editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections,
|
||||
mouse_context_menu::MouseContextMenu, BlockDisposition, BlockProperties, BlockStyle,
|
||||
CustomBlockId, DiffRowHighlight, DisplayRow, DisplaySnapshot, Editor, EditorElement,
|
||||
EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt, RevertFile, RevertSelectedHunks,
|
||||
ToDisplayPoint, ToggleHunkDiff,
|
||||
editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, BlockDisposition,
|
||||
BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow, DisplaySnapshot,
|
||||
Editor, EditorElement, EditorSnapshot, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk,
|
||||
RangeToAnchorExt, RevertFile, RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -41,7 +39,7 @@ pub(super) struct ExpandedHunks {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct ExpandedHunk {
|
||||
pub block: Option<CustomBlockId>,
|
||||
pub blocks: Vec<CustomBlockId>,
|
||||
pub hunk_range: Range<Anchor>,
|
||||
pub diff_base_byte_range: Range<usize>,
|
||||
pub status: DiffHunkStatus,
|
||||
|
@ -77,85 +75,6 @@ impl ExpandedHunks {
|
|||
}
|
||||
|
||||
impl Editor {
|
||||
pub(super) fn open_hunk_context_menu(
|
||||
&mut self,
|
||||
hovered_hunk: HoveredHunk,
|
||||
clicked_point: gpui::Point<Pixels>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let focus_handle = self.focus_handle.clone();
|
||||
let expanded = self
|
||||
.expanded_hunks
|
||||
.hunks(false)
|
||||
.any(|expanded_hunk| expanded_hunk.hunk_range == hovered_hunk.multi_buffer_range);
|
||||
let editor_handle = cx.view().clone();
|
||||
let editor_snapshot = self.snapshot(cx);
|
||||
let start_point = self
|
||||
.to_pixel_point(hovered_hunk.multi_buffer_range.start, &editor_snapshot, cx)
|
||||
.unwrap_or(clicked_point);
|
||||
let end_point = self
|
||||
.to_pixel_point(hovered_hunk.multi_buffer_range.start, &editor_snapshot, cx)
|
||||
.unwrap_or(clicked_point);
|
||||
let norm =
|
||||
|a: gpui::Point<Pixels>, b: gpui::Point<Pixels>| (a.x - b.x).abs() + (a.y - b.y).abs();
|
||||
let closest_source = if norm(start_point, clicked_point) < norm(end_point, clicked_point) {
|
||||
hovered_hunk.multi_buffer_range.start
|
||||
} else {
|
||||
hovered_hunk.multi_buffer_range.end
|
||||
};
|
||||
|
||||
self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
|
||||
self,
|
||||
closest_source,
|
||||
clicked_point,
|
||||
ContextMenu::build(cx, move |menu, _| {
|
||||
menu.on_blur_subscription(Subscription::new(|| {}))
|
||||
.context(focus_handle)
|
||||
.entry(
|
||||
if expanded {
|
||||
"Collapse Hunk"
|
||||
} else {
|
||||
"Expand Hunk"
|
||||
},
|
||||
Some(ToggleHunkDiff.boxed_clone()),
|
||||
{
|
||||
let editor = editor_handle.clone();
|
||||
let hunk = hovered_hunk.clone();
|
||||
move |cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_hovered_hunk(&hunk, cx);
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
.entry("Revert Hunk", Some(RevertSelectedHunks.boxed_clone()), {
|
||||
let editor = editor_handle.clone();
|
||||
let hunk = hovered_hunk.clone();
|
||||
move |cx| {
|
||||
let multi_buffer = editor.read(cx).buffer().clone();
|
||||
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
|
||||
let mut revert_changes = HashMap::default();
|
||||
if let Some(hunk) =
|
||||
crate::hunk_diff::to_diff_hunk(&hunk, &multi_buffer_snapshot)
|
||||
{
|
||||
Editor::prepare_revert_change(
|
||||
&mut revert_changes,
|
||||
&multi_buffer,
|
||||
&hunk,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
if !revert_changes.is_empty() {
|
||||
editor.update(cx, |editor, cx| editor.revert(revert_changes, cx));
|
||||
}
|
||||
}
|
||||
})
|
||||
.action("Revert File", RevertFile.boxed_clone())
|
||||
}),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn toggle_hovered_hunk(
|
||||
&mut self,
|
||||
hovered_hunk: &HoveredHunk,
|
||||
|
@ -264,7 +183,8 @@ impl Editor {
|
|||
break;
|
||||
} else if expanded_hunk_row_range == hunk_to_toggle_row_range {
|
||||
highlights_to_remove.push(expanded_hunk.hunk_range.clone());
|
||||
blocks_to_remove.extend(expanded_hunk.block);
|
||||
blocks_to_remove
|
||||
.extend(expanded_hunk.blocks.iter().copied());
|
||||
hunks_to_toggle.next();
|
||||
retain = false;
|
||||
break;
|
||||
|
@ -371,9 +291,17 @@ impl Editor {
|
|||
Err(ix) => ix,
|
||||
};
|
||||
|
||||
let block = match hunk.status {
|
||||
let blocks;
|
||||
match hunk.status {
|
||||
DiffHunkStatus::Removed => {
|
||||
self.insert_deleted_text_block(diff_base_buffer, deleted_text_lines, hunk, cx)
|
||||
blocks = self.insert_blocks(
|
||||
[
|
||||
self.hunk_header_block(&hunk, cx),
|
||||
Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx),
|
||||
],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
DiffHunkStatus::Added => {
|
||||
self.highlight_rows::<DiffRowHighlight>(
|
||||
|
@ -382,7 +310,7 @@ impl Editor {
|
|||
false,
|
||||
cx,
|
||||
);
|
||||
None
|
||||
blocks = self.insert_blocks([self.hunk_header_block(&hunk, cx)], None, cx);
|
||||
}
|
||||
DiffHunkStatus::Modified => {
|
||||
self.highlight_rows::<DiffRowHighlight>(
|
||||
|
@ -391,13 +319,20 @@ impl Editor {
|
|||
false,
|
||||
cx,
|
||||
);
|
||||
self.insert_deleted_text_block(diff_base_buffer, deleted_text_lines, hunk, cx)
|
||||
blocks = self.insert_blocks(
|
||||
[
|
||||
self.hunk_header_block(&hunk, cx),
|
||||
Self::deleted_text_block(hunk, diff_base_buffer, deleted_text_lines, cx),
|
||||
],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
};
|
||||
self.expanded_hunks.hunks.insert(
|
||||
block_insert_index,
|
||||
ExpandedHunk {
|
||||
block,
|
||||
blocks,
|
||||
hunk_range: hunk_start..hunk_end,
|
||||
status: hunk.status,
|
||||
folded: false,
|
||||
|
@ -408,109 +343,368 @@ impl Editor {
|
|||
Some(())
|
||||
}
|
||||
|
||||
fn insert_deleted_text_block(
|
||||
&mut self,
|
||||
fn hunk_header_block(
|
||||
&self,
|
||||
hunk: &HoveredHunk,
|
||||
cx: &mut ViewContext<'_, Editor>,
|
||||
) -> BlockProperties<Anchor> {
|
||||
let border_color = cx.theme().colors().border_disabled;
|
||||
let gutter_color = match hunk.status {
|
||||
DiffHunkStatus::Added => cx.theme().status().created,
|
||||
DiffHunkStatus::Modified => cx.theme().status().modified,
|
||||
DiffHunkStatus::Removed => cx.theme().status().deleted,
|
||||
};
|
||||
|
||||
BlockProperties {
|
||||
position: hunk.multi_buffer_range.start,
|
||||
height: 1,
|
||||
style: BlockStyle::Sticky,
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
render: Box::new({
|
||||
let editor = cx.view().clone();
|
||||
let hunk = hunk.clone();
|
||||
move |cx| {
|
||||
let hunk_controls_menu_handle =
|
||||
editor.read(cx).hunk_controls_menu_handle.clone();
|
||||
|
||||
h_flex()
|
||||
.id(cx.block_id)
|
||||
.w_full()
|
||||
.h(cx.line_height())
|
||||
.child(
|
||||
div()
|
||||
.id("gutter-strip")
|
||||
.w(EditorElement::diff_hunk_strip_width(cx.line_height()))
|
||||
.h_full()
|
||||
.bg(gutter_color)
|
||||
.cursor(CursorStyle::PointingHand)
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
let hunk = hunk.clone();
|
||||
move |_event, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_hovered_hunk(&hunk, cx);
|
||||
});
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.size_full()
|
||||
.justify_between()
|
||||
.border_t_1()
|
||||
.border_color(border_color)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.pl_6()
|
||||
.child(
|
||||
IconButton::new("next-hunk", IconName::ArrowDown)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Next Hunk",
|
||||
&GoToHunk,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
let hunk = hunk.clone();
|
||||
move |_event, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let position = hunk
|
||||
.multi_buffer_range
|
||||
.end
|
||||
.to_point(
|
||||
&snapshot.buffer_snapshot,
|
||||
);
|
||||
if let Some(hunk) = editor
|
||||
.go_to_hunk_after_position(
|
||||
&snapshot, position, cx,
|
||||
)
|
||||
{
|
||||
let multi_buffer_start = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(Point::new(
|
||||
hunk.row_range.start.0,
|
||||
0,
|
||||
));
|
||||
let multi_buffer_end = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_after(Point::new(
|
||||
hunk.row_range.end.0,
|
||||
0,
|
||||
));
|
||||
editor.expand_diff_hunk(
|
||||
None,
|
||||
&HoveredHunk {
|
||||
multi_buffer_range:
|
||||
multi_buffer_start
|
||||
..multi_buffer_end,
|
||||
status: hunk_status(&hunk),
|
||||
diff_base_byte_range: hunk
|
||||
.diff_base_byte_range,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("prev-hunk", IconName::ArrowUp)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Previous Hunk",
|
||||
&GoToPrevHunk,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
let hunk = hunk.clone();
|
||||
move |_event, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let position = hunk
|
||||
.multi_buffer_range
|
||||
.start
|
||||
.to_point(
|
||||
&snapshot.buffer_snapshot,
|
||||
);
|
||||
let hunk = editor
|
||||
.go_to_hunk_before_position(
|
||||
&snapshot, position, cx,
|
||||
);
|
||||
if let Some(hunk) = hunk {
|
||||
let multi_buffer_start = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(Point::new(
|
||||
hunk.row_range.start.0,
|
||||
0,
|
||||
));
|
||||
let multi_buffer_end = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_after(Point::new(
|
||||
hunk.row_range.end.0,
|
||||
0,
|
||||
));
|
||||
editor.expand_diff_hunk(
|
||||
None,
|
||||
&HoveredHunk {
|
||||
multi_buffer_range:
|
||||
multi_buffer_start
|
||||
..multi_buffer_end,
|
||||
status: hunk_status(&hunk),
|
||||
diff_base_byte_range: hunk
|
||||
.diff_base_byte_range,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.pr_6()
|
||||
.child({
|
||||
let focus = editor.focus_handle(cx);
|
||||
PopoverMenu::new("hunk-controls-dropdown")
|
||||
.trigger(
|
||||
IconButton::new(
|
||||
"toggle_editor_selections_icon",
|
||||
IconName::EllipsisVertical,
|
||||
)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected(
|
||||
hunk_controls_menu_handle.is_deployed(),
|
||||
)
|
||||
.when(
|
||||
!hunk_controls_menu_handle.is_deployed(),
|
||||
|this| {
|
||||
this.tooltip(|cx| {
|
||||
Tooltip::text("Hunk Controls", cx)
|
||||
})
|
||||
},
|
||||
),
|
||||
)
|
||||
.anchor(AnchorCorner::TopRight)
|
||||
.with_handle(hunk_controls_menu_handle)
|
||||
.menu(move |cx| {
|
||||
let focus = focus.clone();
|
||||
let menu =
|
||||
ContextMenu::build(cx, move |menu, _| {
|
||||
menu.context(focus.clone()).action(
|
||||
"Discard All",
|
||||
RevertFile.boxed_clone(),
|
||||
)
|
||||
});
|
||||
Some(menu)
|
||||
})
|
||||
})
|
||||
.child(
|
||||
IconButton::new("discard", IconName::RotateCcw)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Discard Hunk",
|
||||
&RevertSelectedHunks,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
let hunk = hunk.clone();
|
||||
move |_event, cx| {
|
||||
let multi_buffer =
|
||||
editor.read(cx).buffer().clone();
|
||||
let multi_buffer_snapshot =
|
||||
multi_buffer.read(cx).snapshot(cx);
|
||||
let mut revert_changes = HashMap::default();
|
||||
if let Some(hunk) =
|
||||
crate::hunk_diff::to_diff_hunk(
|
||||
&hunk,
|
||||
&multi_buffer_snapshot,
|
||||
)
|
||||
{
|
||||
Editor::prepare_revert_change(
|
||||
&mut revert_changes,
|
||||
&multi_buffer,
|
||||
&hunk,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
if !revert_changes.is_empty() {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.revert(revert_changes, cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("collapse", IconName::Close)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Collapse Hunk",
|
||||
&ToggleHunkDiff,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
let hunk = hunk.clone();
|
||||
move |_event, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_hovered_hunk(&hunk, cx);
|
||||
});
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn deleted_text_block(
|
||||
hunk: &HoveredHunk,
|
||||
diff_base_buffer: Model<Buffer>,
|
||||
deleted_text_height: u32,
|
||||
hunk: &HoveredHunk,
|
||||
cx: &mut ViewContext<'_, Self>,
|
||||
) -> Option<CustomBlockId> {
|
||||
cx: &mut ViewContext<'_, Editor>,
|
||||
) -> BlockProperties<Anchor> {
|
||||
let gutter_color = match hunk.status {
|
||||
DiffHunkStatus::Added => unreachable!(),
|
||||
DiffHunkStatus::Modified => cx.theme().status().modified,
|
||||
DiffHunkStatus::Removed => cx.theme().status().deleted,
|
||||
};
|
||||
let deleted_hunk_color = deleted_hunk_color(cx);
|
||||
let (editor_height, editor_with_deleted_text) =
|
||||
editor_with_deleted_text(diff_base_buffer, deleted_hunk_color, hunk, cx);
|
||||
let editor = cx.view().clone();
|
||||
let hunk = hunk.clone();
|
||||
let height = editor_height.max(deleted_text_height);
|
||||
let mut new_block_ids = self.insert_blocks(
|
||||
Some(BlockProperties {
|
||||
position: hunk.multi_buffer_range.start,
|
||||
height,
|
||||
style: BlockStyle::Flex,
|
||||
disposition: BlockDisposition::Above,
|
||||
render: Box::new(move |cx| {
|
||||
let width = EditorElement::diff_hunk_strip_width(cx.line_height());
|
||||
let gutter_dimensions = editor.read(cx.context).gutter_dimensions;
|
||||
BlockProperties {
|
||||
position: hunk.multi_buffer_range.start,
|
||||
height,
|
||||
style: BlockStyle::Flex,
|
||||
disposition: BlockDisposition::Above,
|
||||
priority: 0,
|
||||
render: Box::new(move |cx| {
|
||||
let width = EditorElement::diff_hunk_strip_width(cx.line_height());
|
||||
let gutter_dimensions = editor.read(cx.context).gutter_dimensions;
|
||||
|
||||
let close_button = editor.update(cx.context, |editor, cx| {
|
||||
let editor_snapshot = editor.snapshot(cx);
|
||||
let hunk_display_range = hunk
|
||||
.multi_buffer_range
|
||||
.clone()
|
||||
.to_display_points(&editor_snapshot);
|
||||
editor.close_hunk_diff_button(
|
||||
hunk.clone(),
|
||||
hunk_display_range.start.row(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
h_flex()
|
||||
.id("gutter with editor")
|
||||
.bg(deleted_hunk_color)
|
||||
.h(height as f32 * cx.line_height())
|
||||
.w_full()
|
||||
.child(
|
||||
h_flex()
|
||||
.id("gutter")
|
||||
.max_w(gutter_dimensions.full_width())
|
||||
.min_w(gutter_dimensions.full_width())
|
||||
.size_full()
|
||||
.child(
|
||||
h_flex()
|
||||
.id("gutter hunk")
|
||||
.bg(cx.theme().status().deleted)
|
||||
.pl(gutter_dimensions.margin
|
||||
+ gutter_dimensions
|
||||
.git_blame_entries_width
|
||||
.unwrap_or_default())
|
||||
.max_w(width)
|
||||
.min_w(width)
|
||||
.size_full()
|
||||
.cursor(CursorStyle::PointingHand)
|
||||
.on_mouse_down(MouseButton::Left, {
|
||||
let editor = editor.clone();
|
||||
let hunk = hunk.clone();
|
||||
move |event, cx| {
|
||||
let modifiers = event.modifiers;
|
||||
if modifiers.control || modifiers.platform {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_hovered_hunk(&hunk, cx);
|
||||
});
|
||||
} else {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.open_hunk_context_menu(
|
||||
hunk.clone(),
|
||||
event.position,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.size_full()
|
||||
.pt(rems(0.25))
|
||||
.justify_start()
|
||||
.child(close_button),
|
||||
),
|
||||
)
|
||||
.child(editor_with_deleted_text.clone())
|
||||
.into_any_element()
|
||||
}),
|
||||
priority: 0,
|
||||
h_flex()
|
||||
.id(cx.block_id)
|
||||
.bg(deleted_hunk_color)
|
||||
.h(height as f32 * cx.line_height())
|
||||
.w_full()
|
||||
.child(
|
||||
h_flex()
|
||||
.id("gutter")
|
||||
.max_w(gutter_dimensions.full_width())
|
||||
.min_w(gutter_dimensions.full_width())
|
||||
.size_full()
|
||||
.child(
|
||||
h_flex()
|
||||
.id("gutter hunk")
|
||||
.bg(gutter_color)
|
||||
.pl(gutter_dimensions.margin
|
||||
+ gutter_dimensions
|
||||
.git_blame_entries_width
|
||||
.unwrap_or_default())
|
||||
.max_w(width)
|
||||
.min_w(width)
|
||||
.size_full()
|
||||
.cursor(CursorStyle::PointingHand)
|
||||
.on_mouse_down(MouseButton::Left, {
|
||||
let editor = editor.clone();
|
||||
let hunk = hunk.clone();
|
||||
move |_event, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.toggle_hovered_hunk(&hunk, cx);
|
||||
});
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(editor_with_deleted_text.clone())
|
||||
.into_any_element()
|
||||
}),
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
if new_block_ids.len() == 1 {
|
||||
new_block_ids.pop()
|
||||
} else {
|
||||
debug_panic!(
|
||||
"Inserted one editor block but did not receive exactly one block id: {new_block_ids:?}"
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -521,7 +715,7 @@ impl Editor {
|
|||
.expanded_hunks
|
||||
.hunks
|
||||
.drain(..)
|
||||
.filter_map(|expanded_hunk| expanded_hunk.block)
|
||||
.flat_map(|expanded_hunk| expanded_hunk.blocks.into_iter())
|
||||
.collect::<HashSet<_>>();
|
||||
if to_remove.is_empty() {
|
||||
false
|
||||
|
@ -603,7 +797,7 @@ impl Editor {
|
|||
expanded_hunk.folded = true;
|
||||
highlights_to_remove
|
||||
.push(expanded_hunk.hunk_range.clone());
|
||||
if let Some(block) = expanded_hunk.block.take() {
|
||||
for block in expanded_hunk.blocks.drain(..) {
|
||||
blocks_to_remove.insert(block);
|
||||
}
|
||||
break;
|
||||
|
@ -650,7 +844,7 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
if !retain {
|
||||
blocks_to_remove.extend(expanded_hunk.block);
|
||||
blocks_to_remove.extend(expanded_hunk.blocks.drain(..));
|
||||
highlights_to_remove.push(expanded_hunk.hunk_range.clone());
|
||||
}
|
||||
retain
|
||||
|
@ -749,7 +943,7 @@ fn added_hunk_color(cx: &AppContext) -> Hsla {
|
|||
}
|
||||
|
||||
fn deleted_hunk_color(cx: &AppContext) -> Hsla {
|
||||
let mut deleted_color = cx.theme().status().git().deleted;
|
||||
let mut deleted_color = cx.theme().status().deleted;
|
||||
deleted_color.fade_out(0.7);
|
||||
deleted_color
|
||||
}
|
||||
|
@ -788,32 +982,15 @@ fn editor_with_deleted_text(
|
|||
false,
|
||||
cx,
|
||||
);
|
||||
|
||||
let subscription_editor = parent_editor.clone();
|
||||
editor._subscriptions.extend([
|
||||
cx.on_blur(&editor.focus_handle, |editor, cx| {
|
||||
editor.set_current_line_highlight(Some(CurrentLineHighlight::None));
|
||||
editor.set_current_line_highlight(Some(CurrentLineHighlight::None));
|
||||
editor
|
||||
._subscriptions
|
||||
.extend([cx.on_blur(&editor.focus_handle, |editor, cx| {
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.try_cancel();
|
||||
});
|
||||
cx.notify();
|
||||
}),
|
||||
cx.on_focus(&editor.focus_handle, move |editor, cx| {
|
||||
let restored_highlight = if let Some(parent_editor) = subscription_editor.upgrade()
|
||||
{
|
||||
parent_editor.read(cx).current_line_highlight
|
||||
} else {
|
||||
None
|
||||
};
|
||||
editor.set_current_line_highlight(restored_highlight);
|
||||
cx.notify();
|
||||
}),
|
||||
cx.observe_global::<SettingsStore>(|editor, cx| {
|
||||
if !editor.is_focused(cx) {
|
||||
editor.set_current_line_highlight(Some(CurrentLineHighlight::None));
|
||||
}
|
||||
}),
|
||||
]);
|
||||
})]);
|
||||
|
||||
let parent_editor_for_reverts = parent_editor.clone();
|
||||
let original_multi_buffer_range = hunk.multi_buffer_range.clone();
|
||||
let diff_base_range = hunk.diff_base_byte_range.clone();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue