debugger: Fix crash when dragging pane items to split view (#46806)

xcb3d and Anthony Eid created

## Summary

Fixes crash when dragging debugger panel items
(terminal/console/variables/frames) to another pane as split view.

  **Root causes:**
1. Double borrow panic - `pane_group.split()` was called synchronously
inside a `Context<Pane>` update, causing "cannot update Pane while it is
already being updated"
2. `unwrap()` calls that panic when items are in transition during drag
operations
3. `debug_assert!` in `pane_at_pixel_position` fails when `members` and
`bounding_boxes` are temporarily out of sync after deferred split

  **Fixes:**
- Defer entire split+move operation via `cx.spawn_in()` to avoid double
borrow
- Replace `unwrap()` with graceful early returns in `activate_item` and
`run_in_terminal`
- Handle `members.len() != bounding_boxes.len()` gracefully in
`pane_at_pixel_position`

Closes #46784

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Anthony Eid <anthony@zed.dev>

Change summary

crates/debugger_ui/src/session/running.rs | 83 +++++++++++-------------
1 file changed, 38 insertions(+), 45 deletions(-)

Detailed changes

crates/debugger_ui/src/session/running.rs 🔗

@@ -335,56 +335,49 @@ pub(crate) fn new_debugger_pane(
             let source = tab.pane.clone();
             let item_id_to_move = item.item_id();
 
-            let Ok(new_split_pane) = pane
-                .drag_split_direction()
-                .map(|split_direction| {
-                    weak_running.update(cx, |running, cx| {
-                        let new_pane =
-                            new_debugger_pane(workspace.clone(), project.clone(), window, cx);
-                        let _previous_subscription = running.pane_close_subscriptions.insert(
-                            new_pane.entity_id(),
-                            cx.subscribe_in(&new_pane, window, RunningState::handle_pane_event),
-                        );
-                        debug_assert!(_previous_subscription.is_none());
-                        running
-                            .panes
-                            .split(&this_pane, &new_pane, split_direction, cx)?;
-                        anyhow::Ok(new_pane)
-                    })
-                })
-                .transpose()
-            else {
-                return ControlFlow::Break(());
-            };
-
-            match new_split_pane.transpose() {
-                // Source pane may be the one currently updated, so defer the move.
-                Ok(Some(new_pane)) => cx
-                    .spawn_in(window, async move |_, cx| {
-                        cx.update(|window, cx| {
-                            move_item(
-                                &source,
-                                &new_pane,
-                                item_id_to_move,
-                                new_pane.read(cx).active_item_index(),
-                                true,
-                                window,
-                                cx,
-                            );
-                        })
-                        .ok();
-                    })
-                    .detach(),
+            let Some(split_direction) = pane.drag_split_direction() else {
                 // If we drop into existing pane or current pane,
                 // regular pane drop handler will take care of it,
                 // using the right tab index for the operation.
-                Ok(None) => return ControlFlow::Continue(()),
-                err @ Err(_) => {
-                    err.log_err();
-                    return ControlFlow::Break(());
-                }
+                return ControlFlow::Continue(());
             };
 
+            let workspace = workspace.clone();
+            let weak_running = weak_running.clone();
+            // Source pane may be the one currently updated, so defer the move.
+            window.defer(cx, move |window, cx| {
+                let new_pane = weak_running.update(cx, |running, cx| {
+                    let new_pane =
+                        new_debugger_pane(workspace.clone(), project.clone(), window, cx);
+                    let _previous_subscription = running.pane_close_subscriptions.insert(
+                        new_pane.entity_id(),
+                        cx.subscribe_in(&new_pane, window, RunningState::handle_pane_event),
+                    );
+                    debug_assert!(_previous_subscription.is_none());
+                    running
+                        .panes
+                        .split(&this_pane, &new_pane, split_direction, cx)?;
+                    anyhow::Ok(new_pane)
+                });
+
+                match new_pane.and_then(|r| r) {
+                    Ok(new_pane) => {
+                        move_item(
+                            &source,
+                            &new_pane,
+                            item_id_to_move,
+                            new_pane.read(cx).active_item_index(),
+                            true,
+                            window,
+                            cx,
+                        );
+                    }
+                    Err(err) => {
+                        log::error!("{err:?}");
+                    }
+                };
+            });
+
             ControlFlow::Break(())
         }
     };