1use adapters::latest_github_release;
2use anyhow::bail;
3use dap::adapters::TcpArguments;
4use gpui::AsyncApp;
5use std::path::PathBuf;
6use task::DebugTaskDefinition;
7
8use crate::*;
9
10#[derive(Default)]
11pub(crate) struct PhpDebugAdapter;
12
13impl PhpDebugAdapter {
14 const ADAPTER_NAME: &'static str = "PHP";
15 const ADAPTER_PACKAGE_NAME: &'static str = "vscode-php-debug";
16 const ADAPTER_PATH: &'static str = "extension/out/phpDebug.js";
17}
18
19#[async_trait(?Send)]
20impl DebugAdapter for PhpDebugAdapter {
21 fn name(&self) -> DebugAdapterName {
22 DebugAdapterName(Self::ADAPTER_NAME.into())
23 }
24
25 async fn fetch_latest_adapter_version(
26 &self,
27 delegate: &dyn DapDelegate,
28 ) -> Result<AdapterVersion> {
29 let release = latest_github_release(
30 &format!("{}/{}", "xdebug", Self::ADAPTER_PACKAGE_NAME),
31 true,
32 false,
33 delegate.http_client(),
34 )
35 .await?;
36
37 let asset_name = format!("php-debug-{}.vsix", release.tag_name.replace("v", ""));
38
39 Ok(AdapterVersion {
40 tag_name: release.tag_name,
41 url: release
42 .assets
43 .iter()
44 .find(|asset| asset.name == asset_name)
45 .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?
46 .browser_download_url
47 .clone(),
48 })
49 }
50
51 async fn get_installed_binary(
52 &self,
53 delegate: &dyn DapDelegate,
54 config: &DebugAdapterConfig,
55 user_installed_path: Option<PathBuf>,
56 _: &mut AsyncApp,
57 ) -> Result<DebugAdapterBinary> {
58 let adapter_path = if let Some(user_installed_path) = user_installed_path {
59 user_installed_path
60 } else {
61 let adapter_path = paths::debug_adapters_dir().join(self.name().as_ref());
62
63 let file_name_prefix = format!("{}_", self.name());
64
65 util::fs::find_file_name_in_dir(adapter_path.as_path(), |file_name| {
66 file_name.starts_with(&file_name_prefix)
67 })
68 .await
69 .ok_or_else(|| anyhow!("Couldn't find PHP dap directory"))?
70 };
71
72 let Some(tcp_connection) = config.tcp_connection.clone() else {
73 bail!("PHP Debug Adapter expects tcp connection arguments to be provided");
74 };
75 let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
76
77 Ok(DebugAdapterBinary {
78 command: delegate
79 .node_runtime()
80 .binary_path()
81 .await?
82 .to_string_lossy()
83 .into_owned(),
84 arguments: Some(vec![
85 adapter_path.join(Self::ADAPTER_PATH).into(),
86 format!("--server={}", port).into(),
87 ]),
88 connection: Some(TcpArguments {
89 port,
90 host,
91 timeout,
92 }),
93 cwd: None,
94 envs: None,
95 })
96 }
97
98 async fn install_binary(
99 &self,
100 version: AdapterVersion,
101 delegate: &dyn DapDelegate,
102 ) -> Result<()> {
103 adapters::download_adapter_from_github(
104 self.name(),
105 version,
106 adapters::DownloadedFileType::Vsix,
107 delegate,
108 )
109 .await?;
110
111 Ok(())
112 }
113
114 fn request_args(&self, config: &DebugTaskDefinition) -> Value {
115 match &config.request {
116 dap::DebugRequestType::Attach(_) => {
117 // php adapter does not support attaching
118 json!({})
119 }
120 dap::DebugRequestType::Launch(launch_config) => {
121 json!({
122 "program": launch_config.program,
123 "cwd": launch_config.cwd,
124 })
125 }
126 }
127 }
128}