use super::{ breakpoint_store::BreakpointStore, dap_command::EvaluateCommand, locators, session::{self, Session, SessionStateEvent},};use crate::{ InlayHint, InlayHintLabel, ProjectEnvironment, ResolveState, debugger::session::SessionQuirks, project_settings::ProjectSettings, terminals::{SshCommand, wrap_for_ssh}, worktree_store::WorktreeStore,};use anyhow::{Context as _, Result, anyhow};use async_trait::async_trait;use collections::HashMap;use dap::{ Capabilities, DapRegistry, DebugRequest, EvaluateArgumentsContext, StackFrameId, adapters::{ DapDelegate, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments, }, client::SessionId, inline_value::VariableLookupKind, messages::Message,};use fs::Fs;use futures::{ StreamExt, channel::mpsc::{self, UnboundedSender}, future::{Shared, join_all},};use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task};use http_client::HttpClient;use language::{Buffer, LanguageToolchainStore, language_settings::InlayHintKind};use node_runtime::NodeRuntime; use remote::{SshRemoteClient, ssh_session::SshArgs};use rpc::{ AnyProtoClient, TypedEnvelope, proto::{self},};use serde::{Deserialize, Serialize};use settings::{Settings, SettingsLocation, WorktreeId};use std::{ borrow::Borrow, collections::BTreeMap, ffi::OsStr, net::Ipv4Addr, path::{Path, PathBuf}, sync::{Arc, Once},};use task::{DebugScenario, SpawnInTerminal, TaskContext, TaskTemplate};use util::ResultExt as _;use worktree::Worktree; #[derive(Debug)]pub enum DapStoreEvent { DebugClientStarted(SessionId), DebugSessionInitialized(SessionId), DebugClientShutdown(SessionId), DebugClientEvent { session_id: SessionId, message: Message, }, Notification(String), RemoteHasInitialized,} enum DapStoreMode { Local(LocalDapStore), Ssh(SshDapStore), Collab,} pub struct LocalDapStore { fs: Arc<dyn Fs>, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>, environment: Entity<ProjectEnvironment>, toolchain_store: Arc<dyn LanguageToolchainStore>,} pub struct SshDapStore { ssh_client: Entity<SshRemoteClient>, upstream_client: AnyProtoClient, upstream_project_id: u64,} pub struct DapStore { mode: DapStoreMode, downstream_client: Option<(AnyProtoClient, u64)>, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, sessions: BTreeMap<SessionId, Entity<Session>>, next_session_id: u32, adapter_options: BTreeMap<DebugAdapterName, Arc<PersistedAdapterOptions>>,} impl EventEmitter<DapStoreEvent> for DapStore {} #[derive(Clone, Serialize, Deserialize)]pub struct PersistedExceptionBreakpoint { pub enabled: bool,} /// Represents best-effort serialization of adapter state during last session (e.g. watches)#[derive(Clone, Default, Serialize, Deserialize)]pub struct PersistedAdapterOptions { /// Which exception breakpoints were enabled during the last session with this adapter? pub exception_breakpoints: BTreeMap<String, PersistedExceptionBreakpoint>,} impl DapStore { pub fn init(client: &AnyProtoClient, cx: &mut App) { static ADD_LOCATORS: Once = Once::new(); ADD_LOCATORS.call_once(|| { let registry = DapRegistry::global(cx); registry.add_locator(Arc::new(locators::cargo::CargoLocator {})); registry.add_locator(Arc::new(locators::go::GoLocator {})); registry.add_locator(Arc::new(locators::node::NodeLocator)); registry.add_locator(Arc::new(locators::python::PythonLocator)); }); client.add_entity_request_handler(Self::handle_run_debug_locator); client.add_entity_request_handler(Self::handle_get_debug_adapter_binary); client.add_entity_message_handler(Self::handle_log_to_debug_console); } #[expect(clippy::too_many_arguments)] pub fn new_local( http_client: Arc<dyn HttpClient>, node_runtime: NodeRuntime, fs: Arc<dyn Fs>, environment: Entity<ProjectEnvironment>, toolchain_store: Arc<dyn LanguageToolchainStore>, worktree_store: Entity<WorktreeStore>, breakpoint_store: Entity<BreakpointStore>, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Local(LocalDapStore { fs, environment, http_client, node_runtime, toolchain_store, }); Self::new(mode, breakpoint_store, worktree_store, cx) } pub fn new_ssh( project_id: u64, ssh_client: Entity<SshRemoteClient>, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Ssh(SshDapStore { upstream_client: ssh_client.read(cx).proto_client(), ssh_client, upstream_project_id: project_id, }); Self::new(mode, breakpoint_store, worktree_store, cx) } pub fn new_collab( _project_id: u64, _upstream_client: AnyProtoClient, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, cx: &mut Context<Self>, ) -> Self { Self::new(DapStoreMode::Collab, breakpoint_store, worktree_store, cx) } fn new( mode: DapStoreMode, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, _cx: &mut Context<Self>, ) -> Self { Self { mode, next_session_id: 0, downstream_client: None, breakpoint_store, worktree_store, sessions: Default::default(), adapter_options: Default::default(), } } pub fn get_debug_adapter_binary( &mut self, definition: DebugTaskDefinition, session_id: SessionId, worktree: &Entity<Worktree>, console: UnboundedSender<String>, cx: &mut Context<Self>, ) -> Task<Result<DebugAdapterBinary>> { match &self.mode { DapStoreMode::Local(_) => { let Some(adapter) = DapRegistry::global(cx).adapter(&definition.adapter) else { return Task::ready(Err(anyhow!("Failed to find a debug adapter"))); }; let settings_location = SettingsLocation { worktree_id: worktree.read(cx).id(), path: Path::new(""), }; let dap_settings = ProjectSettings::get(Some(settings_location), cx) .dap .get(&adapter.name()); let user_installed_path = dap_settings.and_then(|s| s.binary.as_ref().map(PathBuf::from)); let user_args = dap_settings.map(|s| s.args.clone()); let delegate = self.delegate(&worktree, console, cx); let cwd: Arc<Path> = worktree.read(cx).abs_path().as_ref().into(); cx.spawn(async move |this, cx| { let mut binary = adapter .get_binary(&delegate, &definition, user_installed_path, user_args, cx) .await?; let env = this .update(cx, |this, cx| { this.as_local() .unwrap() .environment .update(cx, |environment, cx| { environment.get_directory_environment(cwd, cx) }) })? .await; if let Some(mut env) = env { env.extend(std::mem::take(&mut binary.envs)); binary.envs = env; } Ok(binary) }) } DapStoreMode::Ssh(ssh) => { let request = ssh.upstream_client.request(proto::GetDebugAdapterBinary { session_id: session_id.to_proto(), project_id: ssh.upstream_project_id, worktree_id: worktree.read(cx).id().to_proto(), definition: Some(definition.to_proto()), }); let ssh_client = ssh.ssh_client.clone(); cx.spawn(async move |_, cx| { let response = request.await?; let binary = DebugAdapterBinary::from_proto(response)?; let (mut ssh_command, envs, path_style) = ssh_client.read_with(cx, |ssh, _| { let (SshArgs { arguments, envs }, path_style) = ssh.ssh_info().context("SSH arguments not found")?; anyhow::Ok(( SshCommand { arguments }, envs.unwrap_or_default(), path_style, )) })??; let mut connection = None; if let Some(c) = binary.connection { let local_bind_addr = Ipv4Addr::LOCALHOST; let port = dap::transport::TcpTransport::unused_port(local_bind_addr).await?; ssh_command.add_port_forwarding(port, c.host.to_string(), c.port); connection = Some(TcpArguments { port, host: local_bind_addr, timeout: c.timeout, }) } let (program, args) = wrap_for_ssh( &ssh_command, binary .command .as_ref() .map(|command| (command, &binary.arguments)), binary.cwd.as_deref(), binary.envs, None, path_style, ); Ok(DebugAdapterBinary { command: Some(program), arguments: args, envs, cwd: None, connection, request_args: binary.request_args, }) }) } DapStoreMode::Collab => { Task::ready(Err(anyhow!("Debugging is not yet supported via collab"))) } } } pub fn debug_scenario_for_build_task( &self, build: TaskTemplate, adapter: DebugAdapterName, label: SharedString, cx: &mut App, ) -> Task<Option<DebugScenario>> { let locators = DapRegistry::global(cx).locators(); cx.background_spawn(async move { for locator in locators.values() { if let Some(scenario) = locator.create_scenario(&build, &label, &adapter).await { return Some(scenario); } } None }) } pub fn run_debug_locator( &mut self, locator_name: &str, build_command: SpawnInTerminal, cx: &mut Context<Self>, ) -> Task<Result<DebugRequest>> { match &self.mode { DapStoreMode::Local(_) => { // Pre-resolve args with existing environment. let locators = DapRegistry::global(cx).locators(); let locator = locators.get(locator_name); if let Some(locator) = locator.cloned() { cx.background_spawn(async move { let result = locator .run(build_command.clone()) .await .log_with_level(log::Level::Error); if let Some(result) = result { return Ok(result); } anyhow::bail!( "None of the locators for task `{}` completed successfully", build_command.label ) }) } else { Task::ready(Err(anyhow!( "Couldn't find any locator for task `{}`. Specify the `attach` or `launch` arguments in your debug scenario definition", build_command.label ))) } } DapStoreMode::Ssh(ssh) => { let request = ssh.upstream_client.request(proto::RunDebugLocators { project_id: ssh.upstream_project_id, build_command: Some(build_command.to_proto()), locator: locator_name.to_owned(), }); cx.background_spawn(async move { let response = request.await?; DebugRequest::from_proto(response) }) } DapStoreMode::Collab => { Task::ready(Err(anyhow!("Debugging is not yet supported via collab"))) } } } fn as_local(&self) -> Option<&LocalDapStore> { match &self.mode { DapStoreMode::Local(local_dap_store) => Some(local_dap_store), _ => None, } } pub fn new_session( &mut self, label: Option<SharedString>, adapter: DebugAdapterName, task_context: TaskContext, parent_session: Option<Entity<Session>>, quirks: SessionQuirks, cx: &mut Context<Self>, ) -> Entity<Session> { let session_id = SessionId(util::post_inc(&mut self.next_session_id)); if let Some(session) = &parent_session { session.update(cx, |session, _| { session.add_child_session_id(session_id); }); } let session = Session::new( self.breakpoint_store.clone(), session_id, parent_session, label, adapter, task_context, quirks, cx, ); self.sessions.insert(session_id, session.clone()); cx.notify(); cx.subscribe(&session, { move |this: &mut DapStore, _, event: &SessionStateEvent, cx| match event { SessionStateEvent::Shutdown => { this.shutdown_session(session_id, cx).detach_and_log_err(cx); } SessionStateEvent::Restart | SessionStateEvent::SpawnChildSession { .. } => {} SessionStateEvent::Running => { cx.emit(DapStoreEvent::DebugClientStarted(session_id)); } } }) .detach(); session } pub fn boot_session( &self, session: Entity<Session>, definition: DebugTaskDefinition, worktree: Entity<Worktree>, cx: &mut Context<Self>, ) -> Task<Result<()>> { let dap_store = cx.weak_entity(); let console = session.update(cx, |session, cx| session.console_output(cx)); let session_id = session.read(cx).session_id(); cx.spawn({ let session = session.clone(); async move |this, cx| { let binary = this .update(cx, |this, cx| { this.get_debug_adapter_binary( definition.clone(), session_id, &worktree, console, cx, ) })? .await?; session .update(cx, |session, cx| { session.boot(binary, worktree, dap_store, cx) })? .await } }) } pub fn session_by_id( &self, session_id: impl Borrow<SessionId>, ) -> Option<Entity<session::Session>> { let session_id = session_id.borrow(); let client = self.sessions.get(session_id).cloned(); client } pub fn sessions(&self) -> impl Iterator<Item = &Entity<Session>> { self.sessions.values() } pub fn capabilities_by_id( &self, session_id: impl Borrow<SessionId>, cx: &App, ) -> Option<Capabilities> { let session_id = session_id.borrow(); self.sessions .get(session_id) .map(|client| client.read(cx).capabilities.clone()) } pub fn breakpoint_store(&self) -> &Entity<BreakpointStore> { &self.breakpoint_store } pub fn worktree_store(&self) -> &Entity<WorktreeStore> { &self.worktree_store } #[allow(dead_code)]Lines 1–500 of 959. Load more
use super::{ breakpoint_store::BreakpointStore, dap_command::EvaluateCommand, locators, session::{self, Session, SessionStateEvent}, }; use crate::{ InlayHint, InlayHintLabel, ProjectEnvironment, ResolveState, debugger::session::SessionQuirks, project_settings::ProjectSettings, terminals::{SshCommand, wrap_for_ssh}, worktree_store::WorktreeStore, }; use anyhow::{Context as _, Result, anyhow}; use async_trait::async_trait; use collections::HashMap; use dap::{ Capabilities, DapRegistry, DebugRequest, EvaluateArgumentsContext, StackFrameId, adapters::{ DapDelegate, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments, }, client::SessionId, inline_value::VariableLookupKind, messages::Message, }; use fs::Fs; use futures::{ StreamExt, channel::mpsc::{self, UnboundedSender}, future::{Shared, join_all}, }; use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task}; use http_client::HttpClient; use language::{Buffer, LanguageToolchainStore, language_settings::InlayHintKind}; use node_runtime::NodeRuntime; use remote::{SshRemoteClient, ssh_session::SshArgs}; use rpc::{ AnyProtoClient, TypedEnvelope, proto::{self}, }; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsLocation, WorktreeId}; use std::{ borrow::Borrow, collections::BTreeMap, ffi::OsStr, net::Ipv4Addr, path::{Path, PathBuf}, sync::{Arc, Once}, }; use task::{DebugScenario, SpawnInTerminal, TaskContext, TaskTemplate}; use util::ResultExt as _; use worktree::Worktree; #[derive(Debug)] pub enum DapStoreEvent { DebugClientStarted(SessionId), DebugSessionInitialized(SessionId), DebugClientShutdown(SessionId), DebugClientEvent { session_id: SessionId, message: Message, }, Notification(String), RemoteHasInitialized, } enum DapStoreMode { Local(LocalDapStore), Ssh(SshDapStore), Collab, } pub struct LocalDapStore { fs: Arc<dyn Fs>, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>, environment: Entity<ProjectEnvironment>, toolchain_store: Arc<dyn LanguageToolchainStore>, } pub struct SshDapStore { ssh_client: Entity<SshRemoteClient>, upstream_client: AnyProtoClient, upstream_project_id: u64, } pub struct DapStore { mode: DapStoreMode, downstream_client: Option<(AnyProtoClient, u64)>, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, sessions: BTreeMap<SessionId, Entity<Session>>, next_session_id: u32, adapter_options: BTreeMap<DebugAdapterName, Arc<PersistedAdapterOptions>>, } impl EventEmitter<DapStoreEvent> for DapStore {} #[derive(Clone, Serialize, Deserialize)] pub struct PersistedExceptionBreakpoint { pub enabled: bool, } /// Represents best-effort serialization of adapter state during last session (e.g. watches) #[derive(Clone, Default, Serialize, Deserialize)] pub struct PersistedAdapterOptions { /// Which exception breakpoints were enabled during the last session with this adapter? pub exception_breakpoints: BTreeMap<String, PersistedExceptionBreakpoint>, } impl DapStore { pub fn init(client: &AnyProtoClient, cx: &mut App) { static ADD_LOCATORS: Once = Once::new(); ADD_LOCATORS.call_once(|| { let registry = DapRegistry::global(cx); registry.add_locator(Arc::new(locators::cargo::CargoLocator {})); registry.add_locator(Arc::new(locators::go::GoLocator {})); registry.add_locator(Arc::new(locators::node::NodeLocator)); registry.add_locator(Arc::new(locators::python::PythonLocator)); }); client.add_entity_request_handler(Self::handle_run_debug_locator); client.add_entity_request_handler(Self::handle_get_debug_adapter_binary); client.add_entity_message_handler(Self::handle_log_to_debug_console); } #[expect(clippy::too_many_arguments)] pub fn new_local( http_client: Arc<dyn HttpClient>, node_runtime: NodeRuntime, fs: Arc<dyn Fs>, environment: Entity<ProjectEnvironment>, toolchain_store: Arc<dyn LanguageToolchainStore>, worktree_store: Entity<WorktreeStore>, breakpoint_store: Entity<BreakpointStore>, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Local(LocalDapStore { fs, environment, http_client, node_runtime, toolchain_store, }); Self::new(mode, breakpoint_store, worktree_store, cx) } pub fn new_ssh( project_id: u64, ssh_client: Entity<SshRemoteClient>, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Ssh(SshDapStore { upstream_client: ssh_client.read(cx).proto_client(), ssh_client, upstream_project_id: project_id, }); Self::new(mode, breakpoint_store, worktree_store, cx) } pub fn new_collab( _project_id: u64, _upstream_client: AnyProtoClient, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, cx: &mut Context<Self>, ) -> Self { Self::new(DapStoreMode::Collab, breakpoint_store, worktree_store, cx) } fn new( mode: DapStoreMode, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, _cx: &mut Context<Self>, ) -> Self { Self { mode, next_session_id: 0, downstream_client: None, breakpoint_store, worktree_store, sessions: Default::default(), adapter_options: Default::default(), } } pub fn get_debug_adapter_binary( &mut self, definition: DebugTaskDefinition, session_id: SessionId, worktree: &Entity<Worktree>, console: UnboundedSender<String>, cx: &mut Context<Self>, ) -> Task<Result<DebugAdapterBinary>> { match &self.mode { DapStoreMode::Local(_) => { let Some(adapter) = DapRegistry::global(cx).adapter(&definition.adapter) else { return Task::ready(Err(anyhow!("Failed to find a debug adapter"))); }; let settings_location = SettingsLocation { worktree_id: worktree.read(cx).id(), path: Path::new(""), }; let dap_settings = ProjectSettings::get(Some(settings_location), cx) .dap .get(&adapter.name()); let user_installed_path = dap_settings.and_then(|s| s.binary.as_ref().map(PathBuf::from)); let user_args = dap_settings.map(|s| s.args.clone()); let delegate = self.delegate(&worktree, console, cx); let cwd: Arc<Path> = worktree.read(cx).abs_path().as_ref().into(); cx.spawn(async move |this, cx| { let mut binary = adapter .get_binary(&delegate, &definition, user_installed_path, user_args, cx) .await?; let env = this .update(cx, |this, cx| { this.as_local() .unwrap() .environment .update(cx, |environment, cx| { environment.get_directory_environment(cwd, cx) }) })? .await; if let Some(mut env) = env { env.extend(std::mem::take(&mut binary.envs)); binary.envs = env; } Ok(binary) }) } DapStoreMode::Ssh(ssh) => { let request = ssh.upstream_client.request(proto::GetDebugAdapterBinary { session_id: session_id.to_proto(), project_id: ssh.upstream_project_id, worktree_id: worktree.read(cx).id().to_proto(), definition: Some(definition.to_proto()), }); let ssh_client = ssh.ssh_client.clone(); cx.spawn(async move |_, cx| { let response = request.await?; let binary = DebugAdapterBinary::from_proto(response)?; let (mut ssh_command, envs, path_style) = ssh_client.read_with(cx, |ssh, _| { let (SshArgs { arguments, envs }, path_style) = ssh.ssh_info().context("SSH arguments not found")?; anyhow::Ok(( SshCommand { arguments }, envs.unwrap_or_default(), path_style, )) })??; let mut connection = None; if let Some(c) = binary.connection { let local_bind_addr = Ipv4Addr::LOCALHOST; let port = dap::transport::TcpTransport::unused_port(local_bind_addr).await?; ssh_command.add_port_forwarding(port, c.host.to_string(), c.port); connection = Some(TcpArguments { port, host: local_bind_addr, timeout: c.timeout, }) } let (program, args) = wrap_for_ssh( &ssh_command, binary .command .as_ref() .map(|command| (command, &binary.arguments)), binary.cwd.as_deref(), binary.envs, None, path_style, ); Ok(DebugAdapterBinary { command: Some(program), arguments: args, envs, cwd: None, connection, request_args: binary.request_args, }) }) } DapStoreMode::Collab => { Task::ready(Err(anyhow!("Debugging is not yet supported via collab"))) } } } pub fn debug_scenario_for_build_task( &self, build: TaskTemplate, adapter: DebugAdapterName, label: SharedString, cx: &mut App, ) -> Task<Option<DebugScenario>> { let locators = DapRegistry::global(cx).locators(); cx.background_spawn(async move { for locator in locators.values() { if let Some(scenario) = locator.create_scenario(&build, &label, &adapter).await { return Some(scenario); } } None }) } pub fn run_debug_locator( &mut self, locator_name: &str, build_command: SpawnInTerminal, cx: &mut Context<Self>, ) -> Task<Result<DebugRequest>> { match &self.mode { DapStoreMode::Local(_) => { // Pre-resolve args with existing environment. let locators = DapRegistry::global(cx).locators(); let locator = locators.get(locator_name); if let Some(locator) = locator.cloned() { cx.background_spawn(async move { let result = locator .run(build_command.clone()) .await .log_with_level(log::Level::Error); if let Some(result) = result { return Ok(result); } anyhow::bail!( "None of the locators for task `{}` completed successfully", build_command.label ) }) } else { Task::ready(Err(anyhow!( "Couldn't find any locator for task `{}`. Specify the `attach` or `launch` arguments in your debug scenario definition", build_command.label ))) } } DapStoreMode::Ssh(ssh) => { let request = ssh.upstream_client.request(proto::RunDebugLocators { project_id: ssh.upstream_project_id, build_command: Some(build_command.to_proto()), locator: locator_name.to_owned(), }); cx.background_spawn(async move { let response = request.await?; DebugRequest::from_proto(response) }) } DapStoreMode::Collab => { Task::ready(Err(anyhow!("Debugging is not yet supported via collab"))) } } } fn as_local(&self) -> Option<&LocalDapStore> { match &self.mode { DapStoreMode::Local(local_dap_store) => Some(local_dap_store), _ => None, } } pub fn new_session( &mut self, label: Option<SharedString>, adapter: DebugAdapterName, task_context: TaskContext, parent_session: Option<Entity<Session>>, quirks: SessionQuirks, cx: &mut Context<Self>, ) -> Entity<Session> { let session_id = SessionId(util::post_inc(&mut self.next_session_id)); if let Some(session) = &parent_session { session.update(cx, |session, _| { session.add_child_session_id(session_id); }); } let session = Session::new( self.breakpoint_store.clone(), session_id, parent_session, label, adapter, task_context, quirks, cx, ); self.sessions.insert(session_id, session.clone()); cx.notify(); cx.subscribe(&session, { move |this: &mut DapStore, _, event: &SessionStateEvent, cx| match event { SessionStateEvent::Shutdown => { this.shutdown_session(session_id, cx).detach_and_log_err(cx); } SessionStateEvent::Restart | SessionStateEvent::SpawnChildSession { .. } => {} SessionStateEvent::Running => { cx.emit(DapStoreEvent::DebugClientStarted(session_id)); } } }) .detach(); session } pub fn boot_session( &self, session: Entity<Session>, definition: DebugTaskDefinition, worktree: Entity<Worktree>, cx: &mut Context<Self>, ) -> Task<Result<()>> { let dap_store = cx.weak_entity(); let console = session.update(cx, |session, cx| session.console_output(cx)); let session_id = session.read(cx).session_id(); cx.spawn({ let session = session.clone(); async move |this, cx| { let binary = this .update(cx, |this, cx| { this.get_debug_adapter_binary( definition.clone(), session_id, &worktree, console, cx, ) })? .await?; session .update(cx, |session, cx| { session.boot(binary, worktree, dap_store, cx) })? .await } }) } pub fn session_by_id( &self, session_id: impl Borrow<SessionId>, ) -> Option<Entity<session::Session>> { let session_id = session_id.borrow(); let client = self.sessions.get(session_id).cloned(); client } pub fn sessions(&self) -> impl Iterator<Item = &Entity<Session>> { self.sessions.values() } pub fn capabilities_by_id( &self, session_id: impl Borrow<SessionId>, cx: &App, ) -> Option<Capabilities> { let session_id = session_id.borrow(); self.sessions .get(session_id) .map(|client| client.read(cx).capabilities.clone()) } pub fn breakpoint_store(&self) -> &Entity<BreakpointStore> { &self.breakpoint_store } pub fn worktree_store(&self) -> &Entity<WorktreeStore> { &self.worktree_store } #[allow(dead_code)]