debugger: Allow users to shutdown debug sessions while they're booting (#34362)
This solves problems where users couldn't shut down sessions while locators or build tasks are running. I renamed `debugger::Session::Mode` enum to `SessionState` to be more clear when it's referenced in other crates. I also embedded the boot task that is created in `SessionState::Building` variant. This allows sessions to shut down all created threads in their boot process in a clean and idiomatic way. Finally, I added a method on terminal that allows killing the active task. Release Notes: - Debugger: Allow shutting down debug sessions while they're booting up
This commit is contained in:
parent
970a1066f5
commit
8f6b9f0d65
5 changed files with 156 additions and 79 deletions
|
@ -27,7 +27,7 @@ use text::ToPoint as _;
|
|||
|
||||
use itertools::Itertools as _;
|
||||
use language::Buffer;
|
||||
use project::debugger::session::{Session, SessionQuirks, SessionStateEvent};
|
||||
use project::debugger::session::{Session, SessionQuirks, SessionState, SessionStateEvent};
|
||||
use project::{DebugScenarioContext, Fs, ProjectPath, TaskSourceKind, WorktreeId};
|
||||
use project::{Project, debugger::session::ThreadStatus};
|
||||
use rpc::proto::{self};
|
||||
|
@ -36,7 +36,7 @@ use std::sync::{Arc, LazyLock};
|
|||
use task::{DebugScenario, TaskContext};
|
||||
use tree_sitter::{Query, StreamingIterator as _};
|
||||
use ui::{ContextMenu, Divider, PopoverMenuHandle, Tooltip, prelude::*};
|
||||
use util::{ResultExt, maybe};
|
||||
use util::{ResultExt, debug_panic, maybe};
|
||||
use workspace::SplitDirection;
|
||||
use workspace::item::SaveOptions;
|
||||
use workspace::{
|
||||
|
@ -278,22 +278,34 @@ impl DebugPanel {
|
|||
}
|
||||
});
|
||||
|
||||
cx.spawn(async move |_, cx| {
|
||||
if let Err(error) = task.await {
|
||||
log::error!("{error}");
|
||||
session
|
||||
.update(cx, |session, cx| {
|
||||
session
|
||||
.console_output(cx)
|
||||
.unbounded_send(format!("error: {}", error))
|
||||
.ok();
|
||||
session.shutdown(cx)
|
||||
})?
|
||||
.await;
|
||||
let boot_task = cx.spawn({
|
||||
let session = session.clone();
|
||||
|
||||
async move |_, cx| {
|
||||
if let Err(error) = task.await {
|
||||
log::error!("{error}");
|
||||
session
|
||||
.update(cx, |session, cx| {
|
||||
session
|
||||
.console_output(cx)
|
||||
.unbounded_send(format!("error: {}", error))
|
||||
.ok();
|
||||
session.shutdown(cx)
|
||||
})?
|
||||
.await;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
|
||||
session.update(cx, |session, _| match &mut session.mode {
|
||||
SessionState::Building(state_task) => {
|
||||
*state_task = Some(boot_task);
|
||||
}
|
||||
SessionState::Running(_) => {
|
||||
debug_panic!("Session state should be in building because we are just starting it");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn rerun_last_session(
|
||||
|
@ -826,13 +838,24 @@ impl DebugPanel {
|
|||
.on_click(window.listener_for(
|
||||
&running_state,
|
||||
|this, _, _window, cx| {
|
||||
this.stop_thread(cx);
|
||||
if this.session().read(cx).is_building() {
|
||||
this.session().update(cx, |session, cx| {
|
||||
session.shutdown(cx).detach()
|
||||
});
|
||||
} else {
|
||||
this.stop_thread(cx);
|
||||
}
|
||||
},
|
||||
))
|
||||
.disabled(active_session.as_ref().is_none_or(
|
||||
|session| {
|
||||
session
|
||||
.read(cx)
|
||||
.session(cx)
|
||||
.read(cx)
|
||||
.is_terminated()
|
||||
},
|
||||
))
|
||||
.disabled(
|
||||
thread_status != ThreadStatus::Stopped
|
||||
&& thread_status != ThreadStatus::Running,
|
||||
)
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
let label = if capabilities
|
||||
|
|
|
@ -34,7 +34,7 @@ use loaded_source_list::LoadedSourceList;
|
|||
use module_list::ModuleList;
|
||||
use project::{
|
||||
DebugScenarioContext, Project, WorktreeId,
|
||||
debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus},
|
||||
debugger::session::{self, Session, SessionEvent, SessionStateEvent, ThreadId, ThreadStatus},
|
||||
terminals::TerminalKind,
|
||||
};
|
||||
use rpc::proto::ViewId;
|
||||
|
@ -770,6 +770,15 @@ impl RunningState {
|
|||
cx.on_focus_out(&focus_handle, window, |this, _, window, cx| {
|
||||
this.serialize_layout(window, cx);
|
||||
}),
|
||||
cx.subscribe(
|
||||
&session,
|
||||
|this, session, event: &SessionStateEvent, cx| match event {
|
||||
SessionStateEvent::Shutdown if session.read(cx).is_building() => {
|
||||
this.shutdown(cx);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
let mut pane_close_subscriptions = HashMap::default();
|
||||
|
@ -884,6 +893,7 @@ impl RunningState {
|
|||
let weak_project = project.downgrade();
|
||||
let weak_workspace = workspace.downgrade();
|
||||
let is_local = project.read(cx).is_local();
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let DebugScenario {
|
||||
adapter,
|
||||
|
@ -1599,9 +1609,21 @@ impl RunningState {
|
|||
})
|
||||
.log_err();
|
||||
|
||||
self.session.update(cx, |session, cx| {
|
||||
let is_building = self.session.update(cx, |session, cx| {
|
||||
session.shutdown(cx).detach();
|
||||
})
|
||||
matches!(session.mode, session::SessionState::Building(_))
|
||||
});
|
||||
|
||||
if is_building {
|
||||
self.debug_terminal.update(cx, |terminal, cx| {
|
||||
if let Some(view) = terminal.terminal.as_ref() {
|
||||
view.update(cx, |view, cx| {
|
||||
view.terminal()
|
||||
.update(cx, |terminal, _| terminal.kill_active_task())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop_thread(&self, cx: &mut Context<Self>) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue