@@ -8984,6 +8984,7 @@ impl Editor {
};
window.focus(&editor.focus_handle(cx), cx);
+ editor.update_breakpoint_collision_on_toggle(row, &edit_action);
editor.edit_breakpoint_at_anchor(
position,
breakpoint.as_ref().clone(),
@@ -11847,7 +11848,19 @@ impl Editor {
return;
}
+ let snapshot = self.snapshot(window, cx);
for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
+ if self.gutter_breakpoint_indicator.0.is_some() {
+ let display_row = anchor
+ .to_point(snapshot.buffer_snapshot())
+ .to_display_point(&snapshot.display_snapshot)
+ .row();
+ self.update_breakpoint_collision_on_toggle(
+ display_row,
+ &BreakpointEditAction::Toggle,
+ );
+ }
+
if let Some(breakpoint) = breakpoint {
self.edit_breakpoint_at_anchor(
anchor,
@@ -11866,6 +11879,21 @@ impl Editor {
}
}
+ fn update_breakpoint_collision_on_toggle(
+ &mut self,
+ display_row: DisplayRow,
+ edit_action: &BreakpointEditAction,
+ ) {
+ if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
+ if breakpoint_indicator.display_row == display_row
+ && matches!(edit_action, BreakpointEditAction::Toggle)
+ {
+ breakpoint_indicator.collides_with_existing_breakpoint =
+ !breakpoint_indicator.collides_with_existing_breakpoint;
+ }
+ }
+ }
+
pub fn edit_breakpoint_at_anchor(
&mut self,
breakpoint_position: Anchor,
@@ -24836,6 +24836,108 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
);
}
+#[gpui::test]
+async fn test_breakpoint_phantom_indicator_collision_on_toggle(cx: &mut TestAppContext) {
+ init_test(cx, |_| {});
+
+ let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(
+ path!("/a"),
+ json!({
+ "main.rs": sample_text,
+ }),
+ )
+ .await;
+ let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
+ let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
+ let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
+ let worktree_id = workspace
+ .update(cx, |workspace, _window, cx| {
+ workspace.project().update(cx, |project, cx| {
+ project.worktrees(cx).next().unwrap().read(cx).id()
+ })
+ })
+ .unwrap();
+
+ let buffer = project
+ .update(cx, |project, cx| {
+ project.open_buffer((worktree_id, rel_path("main.rs")), cx)
+ })
+ .await
+ .unwrap();
+
+ let (editor, cx) = cx.add_window_view(|window, cx| {
+ Editor::new(
+ EditorMode::full(),
+ MultiBuffer::build_from_buffer(buffer, cx),
+ Some(project.clone()),
+ window,
+ cx,
+ )
+ });
+
+ // Simulate hovering over row 0 with no existing breakpoint.
+ editor.update(cx, |editor, _cx| {
+ editor.gutter_breakpoint_indicator.0 = Some(PhantomBreakpointIndicator {
+ display_row: DisplayRow(0),
+ is_active: true,
+ collides_with_existing_breakpoint: false,
+ });
+ });
+
+ // Toggle breakpoint on the same row (row 0) โ collision should flip to true.
+ editor.update_in(cx, |editor, window, cx| {
+ editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
+ });
+ editor.update(cx, |editor, _cx| {
+ let indicator = editor.gutter_breakpoint_indicator.0.unwrap();
+ assert!(
+ indicator.collides_with_existing_breakpoint,
+ "Adding a breakpoint on the hovered row should set collision to true"
+ );
+ });
+
+ // Toggle again on the same row โ breakpoint is removed, collision should flip back to false.
+ editor.update_in(cx, |editor, window, cx| {
+ editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
+ });
+ editor.update(cx, |editor, _cx| {
+ let indicator = editor.gutter_breakpoint_indicator.0.unwrap();
+ assert!(
+ !indicator.collides_with_existing_breakpoint,
+ "Removing a breakpoint on the hovered row should set collision to false"
+ );
+ });
+
+ // Now move cursor to row 2 while phantom indicator stays on row 0.
+ editor.update_in(cx, |editor, window, cx| {
+ editor.move_down(&MoveDown, window, cx);
+ editor.move_down(&MoveDown, window, cx);
+ });
+
+ // Ensure phantom indicator is still on row 0, not colliding.
+ editor.update(cx, |editor, _cx| {
+ editor.gutter_breakpoint_indicator.0 = Some(PhantomBreakpointIndicator {
+ display_row: DisplayRow(0),
+ is_active: true,
+ collides_with_existing_breakpoint: false,
+ });
+ });
+
+ // Toggle breakpoint on row 2 (cursor row) โ phantom on row 0 should NOT be affected.
+ editor.update_in(cx, |editor, window, cx| {
+ editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
+ });
+ editor.update(cx, |editor, _cx| {
+ let indicator = editor.gutter_breakpoint_indicator.0.unwrap();
+ assert!(
+ !indicator.collides_with_existing_breakpoint,
+ "Toggling a breakpoint on a different row should not affect the phantom indicator"
+ );
+ });
+}
+
#[gpui::test]
async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
init_test(cx, |_| {});