use adapters::latest_github_release; use gpui::AsyncApp; use regex::Regex; use std::path::PathBuf; use task::{DebugRequestType, DebugTaskDefinition}; use crate::*; #[derive(Debug)] pub(crate) struct JsDebugAdapter { attach_processes: Regex, } impl Default for JsDebugAdapter { fn default() -> Self { Self { attach_processes: Regex::new(r"(?i)^(?:node|bun|iojs)(?:$|\b)") .expect("Regex compilation to succeed"), } } } impl JsDebugAdapter { const ADAPTER_NAME: &'static str = "JavaScript"; const ADAPTER_NPM_NAME: &'static str = "vscode-js-debug"; const ADAPTER_PATH: &'static str = "js-debug/src/dapDebugServer.js"; } #[async_trait(?Send)] impl DebugAdapter for JsDebugAdapter { fn name(&self) -> DebugAdapterName { DebugAdapterName(Self::ADAPTER_NAME.into()) } async fn fetch_latest_adapter_version( &self, delegate: &dyn DapDelegate, ) -> Result { let release = latest_github_release( &format!("{}/{}", "microsoft", Self::ADAPTER_NPM_NAME), true, false, delegate.http_client(), ) .await?; let asset_name = format!("js-debug-dap-{}.tar.gz", release.tag_name); Ok(AdapterVersion { tag_name: release.tag_name, url: release .assets .iter() .find(|asset| asset.name == asset_name) .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))? .browser_download_url .clone(), }) } async fn get_installed_binary( &self, delegate: &dyn DapDelegate, config: &DebugAdapterConfig, user_installed_path: Option, _: &mut AsyncApp, ) -> Result { let adapter_path = if let Some(user_installed_path) = user_installed_path { user_installed_path } else { let adapter_path = paths::debug_adapters_dir().join(self.name().as_ref()); let file_name_prefix = format!("{}_", self.name()); util::fs::find_file_name_in_dir(adapter_path.as_path(), |file_name| { file_name.starts_with(&file_name_prefix) }) .await .ok_or_else(|| anyhow!("Couldn't find JavaScript dap directory"))? }; let tcp_connection = config.tcp_connection.clone().unwrap_or_default(); let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?; Ok(DebugAdapterBinary { command: delegate .node_runtime() .binary_path() .await? .to_string_lossy() .into_owned(), arguments: Some(vec![ adapter_path.join(Self::ADAPTER_PATH).into(), port.to_string().into(), host.to_string().into(), ]), cwd: None, envs: None, connection: Some(adapters::TcpArguments { host, port, timeout, }), }) } async fn install_binary( &self, version: AdapterVersion, delegate: &dyn DapDelegate, ) -> Result<()> { adapters::download_adapter_from_github( self.name(), version, adapters::DownloadedFileType::GzipTar, delegate, ) .await?; return Ok(()); } fn request_args(&self, config: &DebugTaskDefinition) -> Value { let mut args = json!({ "type": "pwa-node", "request": match config.request { DebugRequestType::Launch(_) => "launch", DebugRequestType::Attach(_) => "attach", }, }); let map = args.as_object_mut().unwrap(); match &config.request { DebugRequestType::Attach(attach) => { map.insert("processId".into(), attach.process_id.into()); map.insert("stopOnEntry".into(), config.stop_on_entry.into()); } DebugRequestType::Launch(launch) => { map.insert("program".into(), launch.program.clone().into()); map.insert("args".into(), launch.args.clone().into()); map.insert( "cwd".into(), launch .cwd .as_ref() .map(|s| s.to_string_lossy().into_owned()) .into(), ); } } args } fn attach_processes_filter(&self) -> Regex { self.attach_processes.clone() } }