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 <djsmits12@gmail.com>
This commit is contained in:
Anthony Eid 2025-08-06 18:10:17 -04:00 committed by GitHub
parent a5dd8d0052
commit 3ea90e397b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 38 additions and 27 deletions

View file

@ -74,6 +74,12 @@ impl Borrow<str> for DebugAdapterName {
} }
} }
impl Borrow<SharedString> for DebugAdapterName {
fn borrow(&self) -> &SharedString {
&self.0
}
}
impl std::fmt::Display for DebugAdapterName { impl std::fmt::Display for DebugAdapterName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f) std::fmt::Display::fmt(&self.0, f)

View file

@ -87,7 +87,7 @@ impl DapRegistry {
self.0.read().adapters.get(name).cloned() self.0.read().adapters.get(name).cloned()
} }
pub fn enumerate_adapters(&self) -> Vec<DebugAdapterName> { pub fn enumerate_adapters<B: FromIterator<DebugAdapterName>>(&self) -> B {
self.0.read().adapters.keys().cloned().collect() self.0.read().adapters.keys().cloned().collect()
} }
} }

View file

@ -300,7 +300,7 @@ impl DebugPanel {
}); });
session.update(cx, |session, _| match &mut session.mode { session.update(cx, |session, _| match &mut session.mode {
SessionState::Building(state_task) => { SessionState::Booting(state_task) => {
*state_task = Some(boot_task); *state_task = Some(boot_task);
} }
SessionState::Running(_) => { SessionState::Running(_) => {

View file

@ -1,5 +1,5 @@
use anyhow::{Context as _, bail}; use anyhow::{Context as _, bail};
use collections::{FxHashMap, HashMap}; use collections::{FxHashMap, HashMap, HashSet};
use language::LanguageRegistry; use language::LanguageRegistry;
use std::{ use std::{
borrow::Cow, borrow::Cow,
@ -450,7 +450,7 @@ impl NewProcessModal {
.and_then(|buffer| buffer.read(cx).language()) .and_then(|buffer| buffer.read(cx).language())
.cloned(); .cloned();
let mut available_adapters = workspace let mut available_adapters: Vec<_> = workspace
.update(cx, |_, cx| DapRegistry::global(cx).enumerate_adapters()) .update(cx, |_, cx| DapRegistry::global(cx).enumerate_adapters())
.unwrap_or_default(); .unwrap_or_default();
if let Some(language) = active_buffer_language { if let Some(language) = active_buffer_language {
@ -1054,6 +1054,9 @@ impl DebugDelegate {
}) })
}) })
}); });
let valid_adapters: HashSet<_> = cx.global::<DapRegistry>().enumerate_adapters();
cx.spawn(async move |this, cx| { cx.spawn(async move |this, cx| {
let (recent, scenarios) = if let Some(task) = task { let (recent, scenarios) = if let Some(task) = task {
task.await task.await
@ -1094,6 +1097,7 @@ impl DebugDelegate {
} => !(hide_vscode && dir.ends_with(".vscode")), } => !(hide_vscode && dir.ends_with(".vscode")),
_ => true, _ => true,
}) })
.filter(|(_, scenario)| valid_adapters.contains(&scenario.adapter))
.map(|(kind, scenario)| { .map(|(kind, scenario)| {
let (language, scenario) = let (language, scenario) =
Self::get_scenario_kind(&languages, &dap_registry, scenario); Self::get_scenario_kind(&languages, &dap_registry, scenario);

View file

@ -1651,7 +1651,7 @@ impl RunningState {
let is_building = self.session.update(cx, |session, cx| { let is_building = self.session.update(cx, |session, cx| {
session.shutdown(cx).detach(); session.shutdown(cx).detach();
matches!(session.mode, session::SessionState::Building(_)) matches!(session.mode, session::SessionState::Booting(_))
}); });
if is_building { if is_building {

View file

@ -298,7 +298,7 @@ async fn test_dap_adapter_config_conversion_and_validation(cx: &mut TestAppConte
let adapter_names = cx.update(|cx| { let adapter_names = cx.update(|cx| {
let registry = DapRegistry::global(cx); let registry = DapRegistry::global(cx);
registry.enumerate_adapters() registry.enumerate_adapters::<Vec<_>>()
}); });
let zed_config = ZedDebugConfig { let zed_config = ZedDebugConfig {

View file

@ -56,7 +56,7 @@ use std::{
}; };
use task::TaskContext; use task::TaskContext;
use text::{PointUtf16, ToPointUtf16}; use text::{PointUtf16, ToPointUtf16};
use util::{ResultExt, maybe}; use util::{ResultExt, debug_panic, maybe};
use worktree::Worktree; use worktree::Worktree;
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
@ -141,7 +141,10 @@ pub struct DataBreakpointState {
} }
pub enum SessionState { pub enum SessionState {
Building(Option<Task<Result<()>>>), /// 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<Task<Result<()>>>),
Running(RunningMode), Running(RunningMode),
} }
@ -574,7 +577,7 @@ impl SessionState {
{ {
match self { match self {
SessionState::Running(debug_adapter_client) => debug_adapter_client.request(request), 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:?}" "no adapter running to send request: {request:?}"
))), ))),
} }
@ -583,7 +586,7 @@ impl SessionState {
/// Did this debug session stop at least once? /// Did this debug session stop at least once?
pub(crate) fn has_ever_stopped(&self) -> bool { pub(crate) fn has_ever_stopped(&self) -> bool {
match self { match self {
SessionState::Building(_) => false, SessionState::Booting(_) => false,
SessionState::Running(running_mode) => running_mode.has_ever_stopped, SessionState::Running(running_mode) => running_mode.has_ever_stopped,
} }
} }
@ -839,7 +842,7 @@ impl Session {
.detach(); .detach();
let this = Self { let this = Self {
mode: SessionState::Building(None), mode: SessionState::Booting(None),
id: session_id, id: session_id,
child_session_ids: HashSet::default(), child_session_ids: HashSet::default(),
parent_session, parent_session,
@ -879,7 +882,7 @@ impl Session {
pub fn worktree(&self) -> Option<Entity<Worktree>> { pub fn worktree(&self) -> Option<Entity<Worktree>> {
match &self.mode { match &self.mode {
SessionState::Building(_) => None, SessionState::Booting(_) => None,
SessionState::Running(local_mode) => local_mode.worktree.upgrade(), SessionState::Running(local_mode) => local_mode.worktree.upgrade(),
} }
} }
@ -940,14 +943,12 @@ impl Session {
.await?; .await?;
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
match &mut this.mode { 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); task.take().unwrap().detach_and_log_err(cx);
} }
_ => { SessionState::Booting(_) => {}
debug_assert!( SessionState::Running(_) => {
this.parent_session.is_some(), debug_panic!("Attempting to boot a session that is already running");
"Booting a root debug session without a boot task"
);
} }
}; };
this.mode = SessionState::Running(mode); this.mode = SessionState::Running(mode);
@ -1043,7 +1044,7 @@ impl Session {
pub fn binary(&self) -> Option<&DebugAdapterBinary> { pub fn binary(&self) -> Option<&DebugAdapterBinary> {
match &self.mode { match &self.mode {
SessionState::Building(_) => None, SessionState::Booting(_) => None,
SessionState::Running(running_mode) => Some(&running_mode.binary), SessionState::Running(running_mode) => Some(&running_mode.binary),
} }
} }
@ -1089,26 +1090,26 @@ impl Session {
pub fn is_started(&self) -> bool { pub fn is_started(&self) -> bool {
match &self.mode { match &self.mode {
SessionState::Building(_) => false, SessionState::Booting(_) => false,
SessionState::Running(running) => running.is_started, SessionState::Running(running) => running.is_started,
} }
} }
pub fn is_building(&self) -> bool { 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> { pub fn as_running_mut(&mut self) -> Option<&mut RunningMode> {
match &mut self.mode { match &mut self.mode {
SessionState::Running(local_mode) => Some(local_mode), SessionState::Running(local_mode) => Some(local_mode),
SessionState::Building(_) => None, SessionState::Booting(_) => None,
} }
} }
pub fn as_running(&self) -> Option<&RunningMode> { pub fn as_running(&self) -> Option<&RunningMode> {
match &self.mode { match &self.mode {
SessionState::Running(local_mode) => Some(local_mode), SessionState::Running(local_mode) => Some(local_mode),
SessionState::Building(_) => None, SessionState::Booting(_) => None,
} }
} }
@ -1302,7 +1303,7 @@ impl Session {
SessionState::Running(local_mode) => { SessionState::Running(local_mode) => {
local_mode.initialize_sequence(&self.capabilities, initialize_rx, dap_store, cx) local_mode.initialize_sequence(&self.capabilities, initialize_rx, dap_store, cx)
} }
SessionState::Building(_) => { SessionState::Booting(_) => {
Task::ready(Err(anyhow!("cannot initialize, still building"))) Task::ready(Err(anyhow!("cannot initialize, still building")))
} }
} }
@ -1339,7 +1340,7 @@ impl Session {
}) })
.detach(); .detach();
} }
SessionState::Building(_) => {} SessionState::Booting(_) => {}
} }
} }
@ -2145,7 +2146,7 @@ impl Session {
) )
} }
} }
SessionState::Building(build_task) => { SessionState::Booting(build_task) => {
build_task.take(); build_task.take();
Task::ready(Some(())) Task::ready(Some(()))
} }
@ -2199,7 +2200,7 @@ impl Session {
pub fn adapter_client(&self) -> Option<Arc<DebugAdapterClient>> { pub fn adapter_client(&self) -> Option<Arc<DebugAdapterClient>> {
match self.mode { match self.mode {
SessionState::Running(ref local) => Some(local.client.clone()), SessionState::Running(ref local) => Some(local.client.clone()),
SessionState::Building(_) => None, SessionState::Booting(_) => None,
} }
} }