debugger: Enable manually restarting a session when a DAP server doesn't support restarting (#28908)

This PR also fixes the unexpected behavior of clicking restart when a
session is terminated and nothing happens.

And we fixed a small bug where `DebugClientAdapter.shutdown()` was never
called.

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <m@cole-miller.net>
This commit is contained in:
Anthony Eid 2025-04-16 16:56:37 -04:00 committed by GitHub
parent 21946691a4
commit 78c856cb75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 17 deletions

View file

@ -33,6 +33,7 @@ use std::sync::Arc;
use task::DebugTaskDefinition;
use terminal_view::terminal_panel::TerminalPanel;
use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
use util::debug_panic;
use workspace::{
Workspace,
dock::{DockPosition, Panel, PanelEvent},
@ -316,8 +317,20 @@ impl DebugPanel {
.any(|item| item.read(cx).session_id(cx) == session_id)
{
// We already have an item for this session.
debug_panic!("We should never reuse session ids");
return;
}
this.sessions.retain(|session| {
session
.read(cx)
.mode()
.as_running()
.map_or(false, |running_state| {
!running_state.read(cx).session().read(cx).is_terminated()
})
});
let session_item = DebugSession::running(
project,
this.workspace.clone(),
@ -769,9 +782,6 @@ impl DebugPanel {
this.restart_session(cx);
},
))
.disabled(
!capabilities.supports_restart_request.unwrap_or_default(),
)
.tooltip(move |window, cx| {
Tooltip::text("Restart")(window, cx)
}),

View file

@ -792,10 +792,48 @@ fn create_new_session(
this.update(cx, |_, cx| {
cx.subscribe(
&session,
move |this: &mut DapStore, _, event: &SessionStateEvent, cx| match event {
move |this: &mut DapStore, session, event: &SessionStateEvent, cx| match event {
SessionStateEvent::Shutdown => {
this.shutdown_session(session_id, cx).detach_and_log_err(cx);
}
SessionStateEvent::Restart => {
let Some((config, binary)) = session.read_with(cx, |session, _| {
session
.configuration()
.map(|config| (config, session.binary().clone()))
}) else {
log::error!("Failed to get debug config from session");
return;
};
let mut curr_session = session;
while let Some(parent_id) = curr_session.read(cx).parent_id() {
if let Some(parent_session) = this.sessions.get(&parent_id).cloned() {
curr_session = parent_session;
} else {
log::error!("Failed to get parent session from parent session id");
break;
}
}
let session_id = curr_session.read(cx).session_id();
let task = curr_session.update(cx, |session, cx| session.shutdown(cx));
cx.spawn(async move |this, cx| {
task.await;
this.update(cx, |this, cx| {
this.sessions.remove(&session_id);
this.new_session(binary, config, None, cx)
})?
.1
.await?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
},
)
.detach();

View file

@ -397,6 +397,7 @@ impl LocalMode {
self.definition.initialize_args.clone().unwrap_or(json!({})),
&mut raw.configuration,
);
// Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
let launch = match raw.request {
dap::StartDebuggingRequestArgumentsRequest::Launch => self.request(
@ -684,8 +685,9 @@ pub enum SessionEvent {
Threads,
}
pub(crate) enum SessionStateEvent {
pub(super) enum SessionStateEvent {
Shutdown,
Restart,
}
impl EventEmitter<SessionEvent> for Session {}
@ -1362,6 +1364,18 @@ impl Session {
&self.loaded_sources
}
fn fallback_to_manual_restart(
&mut self,
res: Result<()>,
cx: &mut Context<Self>,
) -> Option<()> {
if res.log_err().is_none() {
cx.emit(SessionStateEvent::Restart);
return None;
}
Some(())
}
fn empty_response(&mut self, res: Result<()>, _cx: &mut Context<Self>) -> Option<()> {
res.log_err()?;
Some(())
@ -1421,26 +1435,17 @@ impl Session {
}
pub fn restart(&mut self, args: Option<Value>, cx: &mut Context<Self>) {
if self.capabilities.supports_restart_request.unwrap_or(false) {
if self.capabilities.supports_restart_request.unwrap_or(false) && !self.is_terminated() {
self.request(
RestartCommand {
raw: args.unwrap_or(Value::Null),
},
Self::empty_response,
Self::fallback_to_manual_restart,
cx,
)
.detach();
} else {
self.request(
DisconnectCommand {
restart: Some(false),
terminate_debuggee: Some(true),
suspend_debuggee: Some(false),
},
Self::empty_response,
cx,
)
.detach();
cx.emit(SessionStateEvent::Restart);
}
}
@ -1475,8 +1480,14 @@ impl Session {
cx.emit(SessionStateEvent::Shutdown);
let debug_client = self.adapter_client();
cx.background_spawn(async move {
let _ = task.await;
if let Some(client) = debug_client {
client.shutdown().await.log_err();
}
})
}