Fix detach (#32506)

Conrad Irwin created

Release Notes:

- debugger: Fix detach to not terminate debuggee (and only be available
when detaching makes sense)

Change summary

crates/debugger_ui/src/debugger_panel.rs | 56 ++++++++++++++++---------
crates/debugger_ui/src/debugger_ui.rs    |  9 ++++
crates/project/src/debugger/session.rs   | 10 ++++
3 files changed, 53 insertions(+), 22 deletions(-)

Detailed changes

crates/debugger_ui/src/debugger_panel.rs 🔗

@@ -542,6 +542,8 @@ impl DebugPanel {
                                             project::debugger::session::ThreadStatus::Exited,
                                         );
                                     let capabilities = running_state.read(cx).capabilities(cx);
+                                    let supports_detach =
+                                        running_state.read(cx).session().read(cx).is_attached();
                                     this.map(|this| {
                                         if thread_status == ThreadStatus::Running {
                                             this.child(
@@ -730,27 +732,39 @@ impl DebugPanel {
                                                 }
                                             }),
                                     )
-                                    .child(
-                                        IconButton::new("debug-disconnect", IconName::DebugDetach)
-                                            .icon_size(IconSize::XSmall)
-                                            .on_click(window.listener_for(
-                                                &running_state,
-                                                |this, _, _, cx| {
-                                                    this.detach_client(cx);
-                                                },
-                                            ))
-                                            .tooltip({
-                                                let focus_handle = focus_handle.clone();
-                                                move |window, cx| {
-                                                    Tooltip::for_action_in(
-                                                        "Detach",
-                                                        &Detach,
-                                                        &focus_handle,
-                                                        window,
-                                                        cx,
-                                                    )
-                                                }
-                                            }),
+                                    .when(
+                                        supports_detach,
+                                        |div| {
+                                            div.child(
+                                                IconButton::new(
+                                                    "debug-disconnect",
+                                                    IconName::DebugDetach,
+                                                )
+                                                .disabled(
+                                                    thread_status != ThreadStatus::Stopped
+                                                        && thread_status != ThreadStatus::Running,
+                                                )
+                                                .icon_size(IconSize::XSmall)
+                                                .on_click(window.listener_for(
+                                                    &running_state,
+                                                    |this, _, _, cx| {
+                                                        this.detach_client(cx);
+                                                    },
+                                                ))
+                                                .tooltip({
+                                                    let focus_handle = focus_handle.clone();
+                                                    move |window, cx| {
+                                                        Tooltip::for_action_in(
+                                                            "Detach",
+                                                            &Detach,
+                                                            &focus_handle,
+                                                            window,
+                                                            cx,
+                                                        )
+                                                    }
+                                                }),
+                                            )
+                                        },
                                     )
                                 },
                             ),

crates/debugger_ui/src/debugger_ui.rs 🔗

@@ -113,6 +113,7 @@ pub fn init(cx: &mut App) {
                     let caps = running_state.capabilities(cx);
                     let supports_restart = caps.supports_restart_request.unwrap_or_default();
                     let supports_step_back = caps.supports_step_back.unwrap_or_default();
+                    let supports_detach = running_state.session().read(cx).is_attached();
                     let status = running_state.thread_status(cx);
 
                     let active_item = active_item.downgrade();
@@ -195,6 +196,14 @@ pub fn init(cx: &mut App) {
                             },
                         ))
                     })
+                    .when(supports_detach, |div| {
+                        let active_item = active_item.clone();
+                        div.on_action(move |_: &Detach, _, cx| {
+                            active_item
+                                .update(cx, |item, cx| item.detach_client(cx))
+                                .ok();
+                        })
+                    })
                     .when(supports_restart, |div| {
                         let active_item = active_item.clone();
                         div.on_action(move |_: &Restart, _, cx| {

crates/project/src/debugger/session.rs 🔗

@@ -26,6 +26,7 @@ use dap::{
 use dap::{
     ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEvent, OutputEventCategory,
     RunInTerminalRequestArguments, StackFramePresentationHint, StartDebuggingRequestArguments,
+    StartDebuggingRequestArgumentsRequest,
 };
 use futures::SinkExt;
 use futures::channel::{mpsc, oneshot};
@@ -2217,10 +2218,17 @@ impl Session {
         self.locations.get(&reference).cloned()
     }
 
+    pub fn is_attached(&self) -> bool {
+        let Mode::Running(local_mode) = &self.mode else {
+            return false;
+        };
+        local_mode.binary.request_args.request == StartDebuggingRequestArgumentsRequest::Attach
+    }
+
     pub fn disconnect_client(&mut self, cx: &mut Context<Self>) {
         let command = DisconnectCommand {
             restart: Some(false),
-            terminate_debuggee: Some(true),
+            terminate_debuggee: Some(false),
             suspend_debuggee: Some(false),
         };