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