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