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