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