Detailed changes
@@ -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(),
+ self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
+ }
+
+ 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(selection.head().row),
- ),
+ snapshot
+ .buffer_snapshot
+ .git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(position.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,
- );
+ 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 seek_in_direction(
+ 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,
- diff_base_buffer: Model<Buffer>,
- deleted_text_height: u32,
+ fn hunk_header_block(
+ &self,
hunk: &HoveredHunk,
- cx: &mut ViewContext<'_, Self>,
- ) -> Option<CustomBlockId> {
- 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;
-
- 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,
- )
- });
+ 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("gutter with editor")
- .bg(deleted_hunk_color)
- .h(height as f32 * cx.line_height())
+ .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()
- .id("gutter")
- .max_w(gutter_dimensions.full_width())
- .min_w(gutter_dimensions.full_width())
.size_full()
+ .justify_between()
+ .border_t_1()
+ .border_color(border_color)
.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,
+ .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(
- v_flex()
- .size_full()
- .pt(rems(0.25))
- .justify_start()
- .child(close_button),
+ 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);
+ });
+ }
+ }),
+ ),
),
)
- .child(editor_with_deleted_text.clone())
.into_any_element()
- }),
- priority: 0,
+ }
+ }),
+ }
+ }
+
+ fn deleted_text_block(
+ hunk: &HoveredHunk,
+ diff_base_buffer: Model<Buffer>,
+ deleted_text_height: u32,
+ 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);
+ 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;
+
+ 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();