use super::{ breakpoint_store::BreakpointStore, dap_command::EvaluateCommand, locators, session::{self, Session, SessionStateEvent},};use crate::{ InlayHint, InlayHintLabel, ProjectEnvironment, ResolveState, 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, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest, EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, Source, StackFrameId, adapters::{ DapDelegate, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments, }, client::SessionId, inline_value::VariableLookupKind, messages::Message, requests::{Completions, Evaluate},};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;use rpc::{ AnyProtoClient, TypedEnvelope, proto::{self},};use settings::{Settings, WorktreeId};use std::{ borrow::Borrow, collections::BTreeMap, ffi::OsStr, net::Ipv4Addr, path::{Path, PathBuf}, sync::{Arc, Once},};use task::{DebugScenario, SpawnInTerminal, 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,} impl EventEmitter<DapStoreEvent> for DapStore {} 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(), } } pub fn get_debug_adapter_binary( &mut self, definition: DebugTaskDefinition, session_id: SessionId, console: UnboundedSender<String>, cx: &mut Context<Self>, ) -> Task<Result<DebugAdapterBinary>> { match &self.mode { DapStoreMode::Local(_) => { let Some(worktree) = self.worktree_store.read(cx).visible_worktrees(cx).next() else { return Task::ready(Err(anyhow!("Failed to find a worktree"))); }; let Some(adapter) = DapRegistry::global(cx).adapter(&definition.adapter) else { return Task::ready(Err(anyhow!("Failed to find a debug adapter"))); }; let user_installed_path = ProjectSettings::get_global(cx) .dap .get(&adapter.name()) .and_then(|s| s.binary.as_ref().map(PathBuf::from)); 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, 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, 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 = ssh_client.read_with(cx, |ssh, _| { anyhow::Ok(SshCommand { arguments: ssh.ssh_args().context("SSH arguments not found")?, }) })??; 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, Some((&binary.command, &binary.arguments)), binary.cwd.as_deref(), binary.envs, None, ); Ok(DebugAdapterBinary { command: program, arguments: args, envs: HashMap::default(), 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, ) -> Option<DebugScenario> { DapRegistry::global(cx) .locators() .values() .find_map(|locator| locator.create_scenario(&build, &label, adapter.clone())) } 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: SharedString, adapter: DebugAdapterName, parent_session: Option<Entity<Session>>, 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, 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, cx: &mut Context<Self>, ) -> Task<Result<()>> { let Some(worktree) = self.worktree_store.read(cx).visible_worktrees(cx).next() else { return Task::ready(Err(anyhow!("Failed to find a worktree"))); }; 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, 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)] async fn handle_ignore_breakpoint_state( this: Entity<Self>, envelope: TypedEnvelope<proto::IgnoreBreakpointState>, mut cx: AsyncApp, ) -> Result<()> { let session_id = SessionId::from_proto(envelope.payload.session_id); this.update(&mut cx, |this, cx| { if let Some(session) = this.session_by_id(&session_id) { session.update(cx, |session, cx| { session.set_ignore_breakpoints(envelope.payload.ignore, cx) }) } else { Task::ready(HashMap::default()) } })? .await; Ok(()) } fn delegate( &self, worktree: &Entity<Worktree>, console: UnboundedSender<String>, cx: &mut App, ) -> Arc<dyn DapDelegate> { let Some(local_store) = self.as_local() else { unimplemented!("Starting session on remote side"); }; Arc::new(DapAdapterDelegate::new( local_store.fs.clone(), worktree.read(cx).snapshot(), console, local_store.node_runtime.clone(), local_store.http_client.clone(), local_store.toolchain_store.clone(), local_store.environment.update(cx, |env, cx| { env.get_worktree_environment(worktree.clone(), cx)Lines 1–500 of 885. Load more
use super::{ breakpoint_store::BreakpointStore, dap_command::EvaluateCommand, locators, session::{self, Session, SessionStateEvent}, }; use crate::{ InlayHint, InlayHintLabel, ProjectEnvironment, ResolveState, 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, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest, EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, Source, StackFrameId, adapters::{ DapDelegate, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments, }, client::SessionId, inline_value::VariableLookupKind, messages::Message, requests::{Completions, Evaluate}, }; 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; use rpc::{ AnyProtoClient, TypedEnvelope, proto::{self}, }; use settings::{Settings, WorktreeId}; use std::{ borrow::Borrow, collections::BTreeMap, ffi::OsStr, net::Ipv4Addr, path::{Path, PathBuf}, sync::{Arc, Once}, }; use task::{DebugScenario, SpawnInTerminal, 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, } impl EventEmitter<DapStoreEvent> for DapStore {} 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(), } } pub fn get_debug_adapter_binary( &mut self, definition: DebugTaskDefinition, session_id: SessionId, console: UnboundedSender<String>, cx: &mut Context<Self>, ) -> Task<Result<DebugAdapterBinary>> { match &self.mode { DapStoreMode::Local(_) => { let Some(worktree) = self.worktree_store.read(cx).visible_worktrees(cx).next() else { return Task::ready(Err(anyhow!("Failed to find a worktree"))); }; let Some(adapter) = DapRegistry::global(cx).adapter(&definition.adapter) else { return Task::ready(Err(anyhow!("Failed to find a debug adapter"))); }; let user_installed_path = ProjectSettings::get_global(cx) .dap .get(&adapter.name()) .and_then(|s| s.binary.as_ref().map(PathBuf::from)); 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, 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, 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 = ssh_client.read_with(cx, |ssh, _| { anyhow::Ok(SshCommand { arguments: ssh.ssh_args().context("SSH arguments not found")?, }) })??; 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, Some((&binary.command, &binary.arguments)), binary.cwd.as_deref(), binary.envs, None, ); Ok(DebugAdapterBinary { command: program, arguments: args, envs: HashMap::default(), 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, ) -> Option<DebugScenario> { DapRegistry::global(cx) .locators() .values() .find_map(|locator| locator.create_scenario(&build, &label, adapter.clone())) } 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: SharedString, adapter: DebugAdapterName, parent_session: Option<Entity<Session>>, 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, 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, cx: &mut Context<Self>, ) -> Task<Result<()>> { let Some(worktree) = self.worktree_store.read(cx).visible_worktrees(cx).next() else { return Task::ready(Err(anyhow!("Failed to find a worktree"))); }; 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, 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)] async fn handle_ignore_breakpoint_state( this: Entity<Self>, envelope: TypedEnvelope<proto::IgnoreBreakpointState>, mut cx: AsyncApp, ) -> Result<()> { let session_id = SessionId::from_proto(envelope.payload.session_id); this.update(&mut cx, |this, cx| { if let Some(session) = this.session_by_id(&session_id) { session.update(cx, |session, cx| { session.set_ignore_breakpoints(envelope.payload.ignore, cx) }) } else { Task::ready(HashMap::default()) } })? .await; Ok(()) } fn delegate( &self, worktree: &Entity<Worktree>, console: UnboundedSender<String>, cx: &mut App, ) -> Arc<dyn DapDelegate> { let Some(local_store) = self.as_local() else { unimplemented!("Starting session on remote side"); }; Arc::new(DapAdapterDelegate::new( local_store.fs.clone(), worktree.read(cx).snapshot(), console, local_store.node_runtime.clone(), local_store.http_client.clone(), local_store.toolchain_store.clone(), local_store.environment.update(cx, |env, cx| { env.get_worktree_environment(worktree.clone(), cx)