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}