debugger: Always focus the active session whenever it is stopped (#31182)

Closes #ISSUE

Release Notes:

- debugger: Fixed child debug sessions taking precedence over the
parents when spawned.
This commit is contained in:
Piotr Osiewicz 2025-05-22 17:23:31 +02:00 committed by GitHub
parent ee4e43f1b6
commit fa1abd8201
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 131 additions and 64 deletions

View file

@ -273,7 +273,7 @@ impl DebugPanel {
let session = session.clone();
async move |this, cx| {
let debug_session =
Self::register_session(this.clone(), session.clone(), cx).await?;
Self::register_session(this.clone(), session.clone(), true, cx).await?;
let definition = debug_session
.update_in(cx, |debug_session, window, cx| {
debug_session.running_state().update(cx, |running, cx| {
@ -318,69 +318,21 @@ impl DebugPanel {
pub(crate) async fn register_session(
this: WeakEntity<Self>,
session: Entity<Session>,
focus: bool,
cx: &mut AsyncWindowContext,
) -> Result<Entity<DebugSession>> {
let adapter_name = session.update(cx, |session, _| session.adapter())?;
this.update_in(cx, |_, window, cx| {
cx.subscribe_in(
&session,
window,
move |this, session, event: &SessionStateEvent, window, cx| match event {
SessionStateEvent::Restart => {
this.handle_restart_request(session.clone(), window, cx);
}
SessionStateEvent::SpawnChildSession { request } => {
this.handle_start_debugging_request(request, session.clone(), window, cx);
}
_ => {}
},
)
.detach();
})
.ok();
let debug_session = register_session_inner(&this, session, cx).await?;
let serialized_layout = persistence::get_serialized_layout(adapter_name).await;
let workspace = this.update_in(cx, |this, window, cx| {
if focus {
this.activate_session(debug_session.clone(), window, cx);
}
let (debug_session, workspace) = this.update_in(cx, |this, window, cx| {
this.sessions.retain(|session| {
!session
.read(cx)
.running_state()
.read(cx)
.session()
.read(cx)
.is_terminated()
});
let debug_session = DebugSession::running(
this.project.clone(),
this.workspace.clone(),
session,
cx.weak_entity(),
serialized_layout,
this.position(window, cx).axis(),
window,
cx,
);
// We might want to make this an event subscription and only notify when a new thread is selected
// This is used to filter the command menu correctly
cx.observe(
&debug_session.read(cx).running_state().clone(),
|_, _, cx| cx.notify(),
)
.detach();
this.sessions.push(debug_session.clone());
this.activate_session(debug_session.clone(), window, cx);
(debug_session, this.workspace.clone())
this.workspace.clone()
})?;
workspace.update_in(cx, |workspace, window, cx| {
workspace.focus_panel::<Self>(window, cx);
})?;
Ok(debug_session)
}
@ -418,7 +370,7 @@ impl DebugPanel {
});
(session, task)
})?;
Self::register_session(this, session, cx).await?;
Self::register_session(this.clone(), session, true, cx).await?;
task.await
})
.detach_and_log_err(cx);
@ -441,7 +393,6 @@ impl DebugPanel {
let adapter = parent_session.read(cx).adapter().clone();
let mut binary = parent_session.read(cx).binary().clone();
binary.request_args = request.clone();
cx.spawn_in(window, async move |this, cx| {
let (session, task) = dap_store_handle.update(cx, |dap_store, cx| {
let session =
@ -452,7 +403,7 @@ impl DebugPanel {
});
(session, task)
})?;
Self::register_session(this, session, cx).await?;
Self::register_session(this, session, false, cx).await?;
task.await
})
.detach_and_log_err(cx);
@ -910,6 +861,21 @@ impl DebugPanel {
}
}
pub(crate) fn activate_session_by_id(
&mut self,
session_id: SessionId,
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(session) = self
.sessions
.iter()
.find(|session| session.read(cx).session_id(cx) == session_id)
{
self.activate_session(session.clone(), window, cx);
}
}
pub(crate) fn activate_session(
&mut self,
session_item: Entity<DebugSession>,
@ -923,7 +889,7 @@ impl DebugPanel {
this.go_to_selected_stack_frame(window, cx);
});
});
self.active_session = Some(session_item.clone());
self.active_session = Some(session_item);
cx.notify();
}
@ -999,6 +965,67 @@ impl DebugPanel {
}
}
async fn register_session_inner(
this: &WeakEntity<DebugPanel>,
session: Entity<Session>,
cx: &mut AsyncWindowContext,
) -> Result<Entity<DebugSession>> {
let adapter_name = session.update(cx, |session, _| session.adapter())?;
this.update_in(cx, |_, window, cx| {
cx.subscribe_in(
&session,
window,
move |this, session, event: &SessionStateEvent, window, cx| match event {
SessionStateEvent::Restart => {
this.handle_restart_request(session.clone(), window, cx);
}
SessionStateEvent::SpawnChildSession { request } => {
this.handle_start_debugging_request(request, session.clone(), window, cx);
}
_ => {}
},
)
.detach();
})
.ok();
let serialized_layout = persistence::get_serialized_layout(adapter_name).await;
let debug_session = this.update_in(cx, |this, window, cx| {
this.sessions.retain(|session| {
!session
.read(cx)
.running_state()
.read(cx)
.session()
.read(cx)
.is_terminated()
});
let debug_session = DebugSession::running(
this.project.clone(),
this.workspace.clone(),
session,
cx.weak_entity(),
serialized_layout,
this.position(window, cx).axis(),
window,
cx,
);
// We might want to make this an event subscription and only notify when a new thread is selected
// This is used to filter the command menu correctly
cx.observe(
&debug_session.read(cx).running_state().clone(),
|_, _, cx| cx.notify(),
)
.detach();
this.sessions.push(debug_session.clone());
debug_session
})?;
Ok(debug_session)
}
impl EventEmitter<PanelEvent> for DebugPanel {}
impl EventEmitter<DebugPanelEvent> for DebugPanel {}