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}