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