use std::{ path::{Path, PathBuf}, str::FromStr, sync::Arc, }; use anyhow::{Context, Result}; use async_trait::async_trait; use dap::{ StartDebuggingRequestArgumentsRequest, adapters::{ DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, }, }; use extension::{Extension, WorktreeDelegate}; use gpui::AsyncApp; use task::{DebugScenario, ZedDebugConfig}; pub(crate) struct ExtensionDapAdapter { extension: Arc, debug_adapter_name: Arc, schema: serde_json::Value, } impl ExtensionDapAdapter { pub(crate) fn new( extension: Arc, debug_adapter_name: Arc, schema_path: &Path, ) -> Result { let schema = std::fs::read_to_string(&schema_path).with_context(|| { format!( "Failed to read debug adapter schema for {debug_adapter_name} (from path: `{schema_path:?}`)" ) })?; let schema = serde_json::Value::from_str(&schema).with_context(|| { format!("Debug adapter schema for {debug_adapter_name} is not a valid JSON") })?; Ok(Self { extension, debug_adapter_name, schema, }) } } /// An adapter that allows an [`dap::adapters::DapDelegate`] to be used as a [`WorktreeDelegate`]. struct WorktreeDelegateAdapter(pub Arc); #[async_trait] impl WorktreeDelegate for WorktreeDelegateAdapter { fn id(&self) -> u64 { self.0.worktree_id().to_proto() } fn root_path(&self) -> String { self.0.worktree_root_path().to_string_lossy().to_string() } async fn read_text_file(&self, path: PathBuf) -> Result { self.0.read_text_file(path).await } async fn which(&self, binary_name: String) -> Option { self.0 .which(binary_name.as_ref()) .await .map(|path| path.to_string_lossy().to_string()) } async fn shell_env(&self) -> Vec<(String, String)> { self.0.shell_env().await.into_iter().collect() } } #[async_trait(?Send)] impl DebugAdapter for ExtensionDapAdapter { fn name(&self) -> DebugAdapterName { self.debug_adapter_name.as_ref().into() } fn dap_schema(&self) -> serde_json::Value { self.schema.clone() } async fn get_binary( &self, delegate: &Arc, config: &DebugTaskDefinition, user_installed_path: Option, _cx: &mut AsyncApp, ) -> Result { self.extension .get_dap_binary( self.debug_adapter_name.clone(), config.clone(), user_installed_path, Arc::new(WorktreeDelegateAdapter(delegate.clone())), ) .await } async fn config_from_zed_format(&self, zed_scenario: ZedDebugConfig) -> Result { self.extension.dap_config_to_scenario(zed_scenario).await } async fn request_kind( &self, config: &serde_json::Value, ) -> Result { self.extension .dap_request_kind(self.debug_adapter_name.clone(), config.clone()) .await } }