From 3ea90e397b84fbeabd01443c37f69319002a9dfd Mon Sep 17 00:00:00 2001 From: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:10:17 -0400 Subject: [PATCH] debugger: Filter out debug scenarios with invalid Adapters from debug picker (#35744) I also removed a debug assertion that wasn't true when a debug session was restarting through a request, because there wasn't a booting task Zed needed to run before the session. I renamed SessionState::Building to SessionState::Booting as well, because building implies that we're building code while booting the session covers more cases and is more accurate. Release Notes: - debugger: Filter out more invalid debug configurations from the debug picker Co-authored-by: Remco Smits --- crates/dap/src/adapters.rs | 6 +++ crates/dap/src/registry.rs | 2 +- crates/debugger_ui/src/debugger_panel.rs | 2 +- crates/debugger_ui/src/new_process_modal.rs | 8 +++- crates/debugger_ui/src/session/running.rs | 2 +- .../src/tests/new_process_modal.rs | 2 +- crates/project/src/debugger/session.rs | 43 ++++++++++--------- 7 files changed, 38 insertions(+), 27 deletions(-) diff --git a/crates/dap/src/adapters.rs b/crates/dap/src/adapters.rs index 0c88f37ff8..687305ae94 100644 --- a/crates/dap/src/adapters.rs +++ b/crates/dap/src/adapters.rs @@ -74,6 +74,12 @@ impl Borrow for DebugAdapterName { } } +impl Borrow for DebugAdapterName { + fn borrow(&self) -> &SharedString { + &self.0 + } +} + impl std::fmt::Display for DebugAdapterName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.0, f) diff --git a/crates/dap/src/registry.rs b/crates/dap/src/registry.rs index d56e2f8f34..212fa2bc23 100644 --- a/crates/dap/src/registry.rs +++ b/crates/dap/src/registry.rs @@ -87,7 +87,7 @@ impl DapRegistry { self.0.read().adapters.get(name).cloned() } - pub fn enumerate_adapters(&self) -> Vec { + pub fn enumerate_adapters>(&self) -> B { self.0.read().adapters.keys().cloned().collect() } } diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index d81c593484..0ac419580b 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -300,7 +300,7 @@ impl DebugPanel { }); session.update(cx, |session, _| match &mut session.mode { - SessionState::Building(state_task) => { + SessionState::Booting(state_task) => { *state_task = Some(boot_task); } SessionState::Running(_) => { diff --git a/crates/debugger_ui/src/new_process_modal.rs b/crates/debugger_ui/src/new_process_modal.rs index 2695941bc0..4ac8e371a1 100644 --- a/crates/debugger_ui/src/new_process_modal.rs +++ b/crates/debugger_ui/src/new_process_modal.rs @@ -1,5 +1,5 @@ use anyhow::{Context as _, bail}; -use collections::{FxHashMap, HashMap}; +use collections::{FxHashMap, HashMap, HashSet}; use language::LanguageRegistry; use std::{ borrow::Cow, @@ -450,7 +450,7 @@ impl NewProcessModal { .and_then(|buffer| buffer.read(cx).language()) .cloned(); - let mut available_adapters = workspace + let mut available_adapters: Vec<_> = workspace .update(cx, |_, cx| DapRegistry::global(cx).enumerate_adapters()) .unwrap_or_default(); if let Some(language) = active_buffer_language { @@ -1054,6 +1054,9 @@ impl DebugDelegate { }) }) }); + + let valid_adapters: HashSet<_> = cx.global::().enumerate_adapters(); + cx.spawn(async move |this, cx| { let (recent, scenarios) = if let Some(task) = task { task.await @@ -1094,6 +1097,7 @@ impl DebugDelegate { } => !(hide_vscode && dir.ends_with(".vscode")), _ => true, }) + .filter(|(_, scenario)| valid_adapters.contains(&scenario.adapter)) .map(|(kind, scenario)| { let (language, scenario) = Self::get_scenario_kind(&languages, &dap_registry, scenario); diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index 2651a94520..f2f9e17d89 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -1651,7 +1651,7 @@ impl RunningState { let is_building = self.session.update(cx, |session, cx| { session.shutdown(cx).detach(); - matches!(session.mode, session::SessionState::Building(_)) + matches!(session.mode, session::SessionState::Booting(_)) }); if is_building { diff --git a/crates/debugger_ui/src/tests/new_process_modal.rs b/crates/debugger_ui/src/tests/new_process_modal.rs index 0805060bf4..d6b0dfa004 100644 --- a/crates/debugger_ui/src/tests/new_process_modal.rs +++ b/crates/debugger_ui/src/tests/new_process_modal.rs @@ -298,7 +298,7 @@ async fn test_dap_adapter_config_conversion_and_validation(cx: &mut TestAppConte let adapter_names = cx.update(|cx| { let registry = DapRegistry::global(cx); - registry.enumerate_adapters() + registry.enumerate_adapters::>() }); let zed_config = ZedDebugConfig { diff --git a/crates/project/src/debugger/session.rs b/crates/project/src/debugger/session.rs index f60a7becf7..d9c28df497 100644 --- a/crates/project/src/debugger/session.rs +++ b/crates/project/src/debugger/session.rs @@ -56,7 +56,7 @@ use std::{ }; use task::TaskContext; use text::{PointUtf16, ToPointUtf16}; -use util::{ResultExt, maybe}; +use util::{ResultExt, debug_panic, maybe}; use worktree::Worktree; #[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)] @@ -141,7 +141,10 @@ pub struct DataBreakpointState { } pub enum SessionState { - Building(Option>>), + /// Represents a session that is building/initializing + /// even if a session doesn't have a pre build task this state + /// is used to run all the async tasks that are required to start the session + Booting(Option>>), Running(RunningMode), } @@ -574,7 +577,7 @@ impl SessionState { { match self { SessionState::Running(debug_adapter_client) => debug_adapter_client.request(request), - SessionState::Building(_) => Task::ready(Err(anyhow!( + SessionState::Booting(_) => Task::ready(Err(anyhow!( "no adapter running to send request: {request:?}" ))), } @@ -583,7 +586,7 @@ impl SessionState { /// Did this debug session stop at least once? pub(crate) fn has_ever_stopped(&self) -> bool { match self { - SessionState::Building(_) => false, + SessionState::Booting(_) => false, SessionState::Running(running_mode) => running_mode.has_ever_stopped, } } @@ -839,7 +842,7 @@ impl Session { .detach(); let this = Self { - mode: SessionState::Building(None), + mode: SessionState::Booting(None), id: session_id, child_session_ids: HashSet::default(), parent_session, @@ -879,7 +882,7 @@ impl Session { pub fn worktree(&self) -> Option> { match &self.mode { - SessionState::Building(_) => None, + SessionState::Booting(_) => None, SessionState::Running(local_mode) => local_mode.worktree.upgrade(), } } @@ -940,14 +943,12 @@ impl Session { .await?; this.update(cx, |this, cx| { match &mut this.mode { - SessionState::Building(task) if task.is_some() => { + SessionState::Booting(task) if task.is_some() => { task.take().unwrap().detach_and_log_err(cx); } - _ => { - debug_assert!( - this.parent_session.is_some(), - "Booting a root debug session without a boot task" - ); + SessionState::Booting(_) => {} + SessionState::Running(_) => { + debug_panic!("Attempting to boot a session that is already running"); } }; this.mode = SessionState::Running(mode); @@ -1043,7 +1044,7 @@ impl Session { pub fn binary(&self) -> Option<&DebugAdapterBinary> { match &self.mode { - SessionState::Building(_) => None, + SessionState::Booting(_) => None, SessionState::Running(running_mode) => Some(&running_mode.binary), } } @@ -1089,26 +1090,26 @@ impl Session { pub fn is_started(&self) -> bool { match &self.mode { - SessionState::Building(_) => false, + SessionState::Booting(_) => false, SessionState::Running(running) => running.is_started, } } pub fn is_building(&self) -> bool { - matches!(self.mode, SessionState::Building(_)) + matches!(self.mode, SessionState::Booting(_)) } pub fn as_running_mut(&mut self) -> Option<&mut RunningMode> { match &mut self.mode { SessionState::Running(local_mode) => Some(local_mode), - SessionState::Building(_) => None, + SessionState::Booting(_) => None, } } pub fn as_running(&self) -> Option<&RunningMode> { match &self.mode { SessionState::Running(local_mode) => Some(local_mode), - SessionState::Building(_) => None, + SessionState::Booting(_) => None, } } @@ -1302,7 +1303,7 @@ impl Session { SessionState::Running(local_mode) => { local_mode.initialize_sequence(&self.capabilities, initialize_rx, dap_store, cx) } - SessionState::Building(_) => { + SessionState::Booting(_) => { Task::ready(Err(anyhow!("cannot initialize, still building"))) } } @@ -1339,7 +1340,7 @@ impl Session { }) .detach(); } - SessionState::Building(_) => {} + SessionState::Booting(_) => {} } } @@ -2145,7 +2146,7 @@ impl Session { ) } } - SessionState::Building(build_task) => { + SessionState::Booting(build_task) => { build_task.take(); Task::ready(Some(())) } @@ -2199,7 +2200,7 @@ impl Session { pub fn adapter_client(&self) -> Option> { match self.mode { SessionState::Running(ref local) => Some(local.client.clone()), - SessionState::Building(_) => None, + SessionState::Booting(_) => None, } }