extension_dap_adapter.rs

  1use std::{
  2    path::{Path, PathBuf},
  3    str::FromStr,
  4    sync::Arc,
  5};
  6
  7use anyhow::{Context, Result};
  8use async_trait::async_trait;
  9use dap::{
 10    StartDebuggingRequestArgumentsRequest,
 11    adapters::{
 12        DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition,
 13    },
 14};
 15use extension::{Extension, WorktreeDelegate};
 16use gpui::AsyncApp;
 17use task::{DebugScenario, ZedDebugConfig};
 18
 19pub(crate) struct ExtensionDapAdapter {
 20    extension: Arc<dyn Extension>,
 21    debug_adapter_name: Arc<str>,
 22    schema: serde_json::Value,
 23}
 24
 25impl ExtensionDapAdapter {
 26    pub(crate) fn new(
 27        extension: Arc<dyn extension::Extension>,
 28        debug_adapter_name: Arc<str>,
 29        schema_path: &Path,
 30    ) -> Result<Self> {
 31        let schema = std::fs::read_to_string(&schema_path).with_context(|| {
 32            format!(
 33                "Failed to read debug adapter schema for {debug_adapter_name} (from path: `{schema_path:?}`)"
 34            )
 35        })?;
 36        let schema = serde_json::Value::from_str(&schema).with_context(|| {
 37            format!("Debug adapter schema for {debug_adapter_name} is not a valid JSON")
 38        })?;
 39        Ok(Self {
 40            extension,
 41            debug_adapter_name,
 42            schema,
 43        })
 44    }
 45}
 46
 47/// An adapter that allows an [`dap::adapters::DapDelegate`] to be used as a [`WorktreeDelegate`].
 48struct WorktreeDelegateAdapter(pub Arc<dyn DapDelegate>);
 49
 50#[async_trait]
 51impl WorktreeDelegate for WorktreeDelegateAdapter {
 52    fn id(&self) -> u64 {
 53        self.0.worktree_id().to_proto()
 54    }
 55
 56    fn root_path(&self) -> String {
 57        self.0.worktree_root_path().to_string_lossy().to_string()
 58    }
 59
 60    async fn read_text_file(&self, path: PathBuf) -> Result<String> {
 61        self.0.read_text_file(path).await
 62    }
 63
 64    async fn which(&self, binary_name: String) -> Option<String> {
 65        self.0
 66            .which(binary_name.as_ref())
 67            .await
 68            .map(|path| path.to_string_lossy().to_string())
 69    }
 70
 71    async fn shell_env(&self) -> Vec<(String, String)> {
 72        self.0.shell_env().await.into_iter().collect()
 73    }
 74}
 75
 76#[async_trait(?Send)]
 77impl DebugAdapter for ExtensionDapAdapter {
 78    fn name(&self) -> DebugAdapterName {
 79        self.debug_adapter_name.as_ref().into()
 80    }
 81
 82    fn dap_schema(&self) -> serde_json::Value {
 83        self.schema.clone()
 84    }
 85
 86    async fn get_binary(
 87        &self,
 88        delegate: &Arc<dyn DapDelegate>,
 89        config: &DebugTaskDefinition,
 90        user_installed_path: Option<PathBuf>,
 91        // TODO support user args in the extension API
 92        _user_args: Option<Vec<String>>,
 93        _cx: &mut AsyncApp,
 94    ) -> Result<DebugAdapterBinary> {
 95        self.extension
 96            .get_dap_binary(
 97                self.debug_adapter_name.clone(),
 98                config.clone(),
 99                user_installed_path,
100                Arc::new(WorktreeDelegateAdapter(delegate.clone())),
101            )
102            .await
103    }
104
105    async fn config_from_zed_format(&self, zed_scenario: ZedDebugConfig) -> Result<DebugScenario> {
106        self.extension.dap_config_to_scenario(zed_scenario).await
107    }
108
109    async fn request_kind(
110        &self,
111        config: &serde_json::Value,
112    ) -> Result<StartDebuggingRequestArgumentsRequest> {
113        self.extension
114            .dap_request_kind(self.debug_adapter_name.clone(), config.clone())
115            .await
116    }
117}