use super::{ breakpoint_store::BreakpointStore, dap_command::EvaluateCommand, locators, session::{self, Session, SessionStateEvent},};use remote::Interactive; use crate::{ InlayHint, InlayHintLabel, ProjectEnvironment, ResolveState, debugger::session::SessionQuirks, project_settings::{DapBinary, ProjectSettings}, 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, RemoveOptions};use futures::{ StreamExt, TryStreamExt as _, channel::mpsc::{self, UnboundedSender}, future::{Shared, join_all},};use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, TaskExt};use http_client::HttpClient;use language::{Buffer, LanguageToolchainStore};use node_runtime::NodeRuntime;use settings::InlayHintKind; use remote::RemoteClient;use rpc::{ AnyProtoClient, TypedEnvelope, proto::{self},};use serde::{Deserialize, Serialize};use settings::{Settings, SettingsLocation, WorktreeId};use std::{ borrow::Borrow, collections::BTreeMap, ffi::OsStr, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, sync::{Arc, Once},};use task::{DebugScenario, SharedTaskContext, SpawnInTerminal, TaskTemplate};use util::{ResultExt as _, rel_path::RelPath};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), Remote(RemoteDapStore), Collab,} pub struct LocalDapStore { fs: Arc<dyn Fs>, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>, environment: Entity<ProjectEnvironment>, toolchain_store: Arc<dyn LanguageToolchainStore>, is_headless: bool,} pub struct RemoteDapStore { remote_client: Entity<RemoteClient>, upstream_client: AnyProtoClient, upstream_project_id: u64, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>,} 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>, is_headless: bool, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Local(LocalDapStore { fs: fs.clone(), environment, http_client, node_runtime, toolchain_store, is_headless, }); Self::new(mode, breakpoint_store, worktree_store, fs, cx) } pub fn new_remote( project_id: u64, remote_client: Entity<RemoteClient>, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Remote(RemoteDapStore { upstream_client: remote_client.read(cx).proto_client(), remote_client, upstream_project_id: project_id, node_runtime, http_client, }); Self::new(mode, breakpoint_store, worktree_store, fs, cx) } pub fn new_collab( _project_id: u64, _upstream_client: AnyProtoClient, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { Self::new( DapStoreMode::Collab, breakpoint_store, worktree_store, fs, cx, ) } fn new( mode: DapStoreMode, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { cx.background_spawn(async move { let dir = paths::debug_adapters_dir().join("js-debug-companion"); let mut children = fs.read_dir(&dir).await?.try_collect::<Vec<_>>().await?; children.sort_by_key(|child| semver::Version::parse(child.file_name()?.to_str()?).ok()); if let Some(child) = children.last() && let Some(name) = child.file_name() && let Some(name) = name.to_str() && semver::Version::parse(name).is_ok() { children.pop(); } for child in children { fs.remove_dir( &child, RemoveOptions { recursive: true, ignore_if_not_exists: true, }, ) .await .ok(); } anyhow::Ok(()) }) .detach(); 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: RelPath::empty(), }; let dap_settings = ProjectSettings::get(Some(settings_location), cx) .dap .get(&adapter.name()); let user_installed_path = dap_settings.and_then(|s| match &s.binary { DapBinary::Default => None, DapBinary::Custom(binary) => { let path = PathBuf::from(binary); Some(worktree.read(cx).resolve_relative_path(path)) } }); let user_args = dap_settings.and_then(|s| s.args.clone()); let user_env = dap_settings.and_then(|s| s.env.clone()); let delegate = self.delegate(worktree, console, cx); let worktree = worktree.clone(); cx.spawn(async move |this, cx| { let mut binary = adapter .get_binary( &delegate, &definition, user_installed_path, user_args, user_env, cx, ) .await?; let env = this .update(cx, |this, cx| { this.as_local() .unwrap() .environment .update(cx, |environment, cx| { environment.worktree_environment(worktree, cx) }) })? .await; if let Some(mut env) = env { env.extend(std::mem::take(&mut binary.envs)); binary.envs = env; } Ok(binary) }) } DapStoreMode::Remote(remote) => { let request = remote .upstream_client .request(proto::GetDebugAdapterBinary { session_id: session_id.to_proto(), project_id: remote.upstream_project_id, worktree_id: worktree.read(cx).id().to_proto(), definition: Some(definition.to_proto()), }); let remote = remote.remote_client.clone(); cx.spawn(async move |_, cx| { let response = request.await?; let binary = DebugAdapterBinary::from_proto(response)?; let port_forwarding; let connection; if let Some(c) = binary.connection { let host = IpAddr::V4(Ipv4Addr::LOCALHOST); let port; if remote.read_with(cx, |remote, _cx| remote.shares_network_interface()) { port = c.port; port_forwarding = None; } else { port = dap::transport::TcpTransport::unused_port(host).await?; port_forwarding = Some((port, c.host.to_string(), c.port)); } connection = Some(TcpArguments { port, host, timeout: c.timeout, }) } else { port_forwarding = None; connection = None; } let command = remote.read_with(cx, |remote, _cx| { remote.build_command( binary.command, &binary.arguments, &binary.envs, binary.cwd.map(|path| path.display().to_string()), port_forwarding, Interactive::No, ) })?; Ok(DebugAdapterBinary { command: Some(command.program), arguments: command.args, envs: command.env, 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); let executor = cx.background_executor().clone(); if let Some(locator) = locator.cloned() { cx.background_spawn(async move { let result = locator .run(build_command.clone(), executor) .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::Remote(remote) => { let request = remote.upstream_client.request(proto::RunDebugLocators { project_id: remote.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: SharedTaskContext, 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 (remote_client, node_runtime, http_client) = match &self.mode { DapStoreMode::Local(_) => (None, None, None), DapStoreMode::Remote(remote_dap_store) => ( Some(remote_dap_store.remote_client.clone()), Some(remote_dap_store.node_runtime.clone()), Some(remote_dap_store.http_client.clone()), ), DapStoreMode::Collab => (None, None, None), }; let session = Session::new( self.breakpoint_store.clone(), session_id, parent_session, label, adapter, task_context, quirks, remote_client, node_runtime, http_client, 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));Lines 1–500 of 1037. Load more
use super::{ breakpoint_store::BreakpointStore, dap_command::EvaluateCommand, locators, session::{self, Session, SessionStateEvent}, }; use remote::Interactive; use crate::{ InlayHint, InlayHintLabel, ProjectEnvironment, ResolveState, debugger::session::SessionQuirks, project_settings::{DapBinary, ProjectSettings}, 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, RemoveOptions}; use futures::{ StreamExt, TryStreamExt as _, channel::mpsc::{self, UnboundedSender}, future::{Shared, join_all}, }; use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, TaskExt}; use http_client::HttpClient; use language::{Buffer, LanguageToolchainStore}; use node_runtime::NodeRuntime; use settings::InlayHintKind; use remote::RemoteClient; use rpc::{ AnyProtoClient, TypedEnvelope, proto::{self}, }; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsLocation, WorktreeId}; use std::{ borrow::Borrow, collections::BTreeMap, ffi::OsStr, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, sync::{Arc, Once}, }; use task::{DebugScenario, SharedTaskContext, SpawnInTerminal, TaskTemplate}; use util::{ResultExt as _, rel_path::RelPath}; 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), Remote(RemoteDapStore), Collab, } pub struct LocalDapStore { fs: Arc<dyn Fs>, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>, environment: Entity<ProjectEnvironment>, toolchain_store: Arc<dyn LanguageToolchainStore>, is_headless: bool, } pub struct RemoteDapStore { remote_client: Entity<RemoteClient>, upstream_client: AnyProtoClient, upstream_project_id: u64, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>, } 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>, is_headless: bool, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Local(LocalDapStore { fs: fs.clone(), environment, http_client, node_runtime, toolchain_store, is_headless, }); Self::new(mode, breakpoint_store, worktree_store, fs, cx) } pub fn new_remote( project_id: u64, remote_client: Entity<RemoteClient>, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, node_runtime: NodeRuntime, http_client: Arc<dyn HttpClient>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { let mode = DapStoreMode::Remote(RemoteDapStore { upstream_client: remote_client.read(cx).proto_client(), remote_client, upstream_project_id: project_id, node_runtime, http_client, }); Self::new(mode, breakpoint_store, worktree_store, fs, cx) } pub fn new_collab( _project_id: u64, _upstream_client: AnyProtoClient, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { Self::new( DapStoreMode::Collab, breakpoint_store, worktree_store, fs, cx, ) } fn new( mode: DapStoreMode, breakpoint_store: Entity<BreakpointStore>, worktree_store: Entity<WorktreeStore>, fs: Arc<dyn Fs>, cx: &mut Context<Self>, ) -> Self { cx.background_spawn(async move { let dir = paths::debug_adapters_dir().join("js-debug-companion"); let mut children = fs.read_dir(&dir).await?.try_collect::<Vec<_>>().await?; children.sort_by_key(|child| semver::Version::parse(child.file_name()?.to_str()?).ok()); if let Some(child) = children.last() && let Some(name) = child.file_name() && let Some(name) = name.to_str() && semver::Version::parse(name).is_ok() { children.pop(); } for child in children { fs.remove_dir( &child, RemoveOptions { recursive: true, ignore_if_not_exists: true, }, ) .await .ok(); } anyhow::Ok(()) }) .detach(); 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: RelPath::empty(), }; let dap_settings = ProjectSettings::get(Some(settings_location), cx) .dap .get(&adapter.name()); let user_installed_path = dap_settings.and_then(|s| match &s.binary { DapBinary::Default => None, DapBinary::Custom(binary) => { let path = PathBuf::from(binary); Some(worktree.read(cx).resolve_relative_path(path)) } }); let user_args = dap_settings.and_then(|s| s.args.clone()); let user_env = dap_settings.and_then(|s| s.env.clone()); let delegate = self.delegate(worktree, console, cx); let worktree = worktree.clone(); cx.spawn(async move |this, cx| { let mut binary = adapter .get_binary( &delegate, &definition, user_installed_path, user_args, user_env, cx, ) .await?; let env = this .update(cx, |this, cx| { this.as_local() .unwrap() .environment .update(cx, |environment, cx| { environment.worktree_environment(worktree, cx) }) })? .await; if let Some(mut env) = env { env.extend(std::mem::take(&mut binary.envs)); binary.envs = env; } Ok(binary) }) } DapStoreMode::Remote(remote) => { let request = remote .upstream_client .request(proto::GetDebugAdapterBinary { session_id: session_id.to_proto(), project_id: remote.upstream_project_id, worktree_id: worktree.read(cx).id().to_proto(), definition: Some(definition.to_proto()), }); let remote = remote.remote_client.clone(); cx.spawn(async move |_, cx| { let response = request.await?; let binary = DebugAdapterBinary::from_proto(response)?; let port_forwarding; let connection; if let Some(c) = binary.connection { let host = IpAddr::V4(Ipv4Addr::LOCALHOST); let port; if remote.read_with(cx, |remote, _cx| remote.shares_network_interface()) { port = c.port; port_forwarding = None; } else { port = dap::transport::TcpTransport::unused_port(host).await?; port_forwarding = Some((port, c.host.to_string(), c.port)); } connection = Some(TcpArguments { port, host, timeout: c.timeout, }) } else { port_forwarding = None; connection = None; } let command = remote.read_with(cx, |remote, _cx| { remote.build_command( binary.command, &binary.arguments, &binary.envs, binary.cwd.map(|path| path.display().to_string()), port_forwarding, Interactive::No, ) })?; Ok(DebugAdapterBinary { command: Some(command.program), arguments: command.args, envs: command.env, 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); let executor = cx.background_executor().clone(); if let Some(locator) = locator.cloned() { cx.background_spawn(async move { let result = locator .run(build_command.clone(), executor) .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::Remote(remote) => { let request = remote.upstream_client.request(proto::RunDebugLocators { project_id: remote.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: SharedTaskContext, 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 (remote_client, node_runtime, http_client) = match &self.mode { DapStoreMode::Local(_) => (None, None, None), DapStoreMode::Remote(remote_dap_store) => ( Some(remote_dap_store.remote_client.clone()), Some(remote_dap_store.node_runtime.clone()), Some(remote_dap_store.http_client.clone()), ), DapStoreMode::Collab => (None, None, None), }; let session = Session::new( self.breakpoint_store.clone(), session_id, parent_session, label, adapter, task_context, quirks, remote_client, node_runtime, http_client, 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));