From c441b651fa9ed18370f1c724f265732321a98550 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 9 Apr 2025 12:57:24 +0200 Subject: [PATCH] debugger: Add support for CodeLLDB (#28376) Closes #ISSUE Release Notes: - N/A --- crates/dap/src/adapters.rs | 3 +- crates/dap_adapters/src/codelldb.rs | 141 +++++++++++++++++++++++ crates/dap_adapters/src/dap_adapters.rs | 3 + crates/debugger_ui/src/debugger_panel.rs | 6 +- crates/project/src/debugger/dap_store.rs | 26 +++-- crates/project/src/terminals.rs | 6 +- 6 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 crates/dap_adapters/src/codelldb.rs diff --git a/crates/dap/src/adapters.rs b/crates/dap/src/adapters.rs index 8dfd4896d0..175fdd8c2d 100644 --- a/crates/dap/src/adapters.rs +++ b/crates/dap/src/adapters.rs @@ -93,7 +93,7 @@ pub struct TcpArguments { pub port: u16, pub timeout: Option, } -#[derive(Debug, Clone)] +#[derive(Default, Debug, Clone)] pub struct DebugAdapterBinary { pub command: String, pub arguments: Option>, @@ -102,6 +102,7 @@ pub struct DebugAdapterBinary { pub connection: Option, } +#[derive(Debug)] pub struct AdapterVersion { pub tag_name: String, pub url: String, diff --git a/crates/dap_adapters/src/codelldb.rs b/crates/dap_adapters/src/codelldb.rs new file mode 100644 index 0000000000..2d07648427 --- /dev/null +++ b/crates/dap_adapters/src/codelldb.rs @@ -0,0 +1,141 @@ +use std::{path::PathBuf, sync::OnceLock}; + +use anyhow::{Result, bail}; +use async_trait::async_trait; +use dap::adapters::latest_github_release; +use gpui::AsyncApp; +use task::{DebugAdapterConfig, DebugRequestType, DebugTaskDefinition}; + +use crate::*; + +#[derive(Default)] +pub(crate) struct CodeLldbDebugAdapter { + last_known_version: OnceLock, +} + +impl CodeLldbDebugAdapter { + const ADAPTER_NAME: &'static str = "CodeLLDB"; +} + +#[async_trait(?Send)] +impl DebugAdapter for CodeLldbDebugAdapter { + fn name(&self) -> DebugAdapterName { + DebugAdapterName(Self::ADAPTER_NAME.into()) + } + + async fn install_binary( + &self, + version: AdapterVersion, + delegate: &dyn DapDelegate, + ) -> Result<()> { + adapters::download_adapter_from_github( + self.name(), + version, + adapters::DownloadedFileType::Vsix, + delegate, + ) + .await?; + + Ok(()) + } + + async fn fetch_latest_adapter_version( + &self, + delegate: &dyn DapDelegate, + ) -> Result { + let release = + latest_github_release("vadimcn/codelldb", true, false, delegate.http_client()).await?; + + let arch = match std::env::consts::ARCH { + "aarch64" => "arm64", + "x86_64" => "x64", + _ => { + return Err(anyhow!( + "unsupported architecture {}", + std::env::consts::ARCH + )); + } + }; + let platform = match std::env::consts::OS { + "macos" => "darwin", + "linux" => "linux", + "windows" => "win32", + _ => { + return Err(anyhow!( + "unsupported operating system {}", + std::env::consts::OS + )); + } + }; + let asset_name = format!("codelldb-{platform}-{arch}.vsix"); + let _ = self.last_known_version.set(release.tag_name.clone()); + let ret = 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(), + }; + + Ok(ret) + } + + async fn get_installed_binary( + &self, + _: &dyn DapDelegate, + _: &DebugAdapterConfig, + _: Option, + _: &mut AsyncApp, + ) -> Result { + let Some(version) = self.last_known_version.get() else { + bail!("Could not determine latest CodeLLDB version"); + }; + let adapter_path = paths::debug_adapters_dir().join(&Self::ADAPTER_NAME); + let version_path = adapter_path.join(format!("{}_{}", Self::ADAPTER_NAME, version)); + + let adapter_dir = version_path.join("extension").join("adapter"); + let command = adapter_dir.join("codelldb"); + let command = command + .to_str() + .map(ToOwned::to_owned) + .ok_or_else(|| anyhow!("Adapter path is expected to be valid UTF-8"))?; + Ok(DebugAdapterBinary { + command, + cwd: Some(adapter_dir), + ..Default::default() + }) + } + + fn request_args(&self, config: &DebugTaskDefinition) -> Value { + let mut args = json!({ + "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("pid".into(), attach.process_id.into()); + } + DebugRequestType::Launch(launch) => { + map.insert("program".into(), launch.program.clone().into()); + + if !launch.args.is_empty() { + map.insert("args".into(), launch.args.clone().into()); + } + + if let Some(stop_on_entry) = config.stop_on_entry { + map.insert("stopOnEntry".into(), stop_on_entry.into()); + } + if let Some(cwd) = launch.cwd.as_ref() { + map.insert("cwd".into(), cwd.to_string_lossy().into_owned().into()); + } + } + } + args + } +} diff --git a/crates/dap_adapters/src/dap_adapters.rs b/crates/dap_adapters/src/dap_adapters.rs index b337bfbf5a..320b5336fc 100644 --- a/crates/dap_adapters/src/dap_adapters.rs +++ b/crates/dap_adapters/src/dap_adapters.rs @@ -1,3 +1,4 @@ +mod codelldb; mod gdb; mod go; mod javascript; @@ -9,6 +10,7 @@ use std::{net::Ipv4Addr, sync::Arc}; use anyhow::{Result, anyhow}; use async_trait::async_trait; +use codelldb::CodeLldbDebugAdapter; use dap::{ DapRegistry, adapters::{ @@ -26,6 +28,7 @@ use serde_json::{Value, json}; use task::{DebugAdapterConfig, TCPHost}; pub fn init(registry: Arc) { + registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default())); registry.add_adapter(Arc::from(PythonDebugAdapter)); registry.add_adapter(Arc::from(PhpDebugAdapter)); registry.add_adapter(Arc::from(JsDebugAdapter::default())); diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 11dd116c8c..6db230a6f1 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -25,7 +25,9 @@ use project::{ }; use rpc::proto::{self}; use settings::Settings; -use std::{any::TypeId, path::PathBuf}; +use std::any::TypeId; +use std::path::Path; +use std::sync::Arc; use task::DebugTaskDefinition; use terminal_view::terminal_panel::TerminalPanel; use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*}; @@ -272,7 +274,7 @@ impl DebugPanel { fn handle_run_in_terminal_request( &self, title: Option, - cwd: PathBuf, + cwd: Option>, command: Option, args: Vec, envs: HashMap, diff --git a/crates/project/src/debugger/dap_store.rs b/crates/project/src/debugger/dap_store.rs index 28ca1516b8..23f72440a5 100644 --- a/crates/project/src/debugger/dap_store.rs +++ b/crates/project/src/debugger/dap_store.rs @@ -38,7 +38,7 @@ use std::{ borrow::Borrow, collections::{BTreeMap, HashSet}, ffi::OsStr, - path::PathBuf, + path::{Path, PathBuf}, sync::{Arc, atomic::Ordering::SeqCst}, }; use std::{collections::VecDeque, sync::atomic::AtomicU32}; @@ -57,7 +57,7 @@ pub enum DapStoreEvent { RunInTerminal { session_id: SessionId, title: Option, - cwd: PathBuf, + cwd: Option>, command: Option, args: Vec, envs: HashMap, @@ -549,10 +549,10 @@ impl DapStore { let seq = request.seq; - let cwd = PathBuf::from(request_args.cwd); + let cwd = Path::new(&request_args.cwd); + match cwd.try_exists() { - Ok(true) => (), - Ok(false) | Err(_) => { + Ok(false) | Err(_) if !request_args.cwd.is_empty() => { return session.update(cx, |session, cx| { session.respond_to_client( seq, @@ -574,8 +574,8 @@ impl DapStore { ) }); } + _ => (), } - let mut args = request_args.args.clone(); // Handle special case for NodeJS debug adapter @@ -602,7 +602,19 @@ impl DapStore { } let (tx, mut rx) = mpsc::channel::>(1); - + let cwd = Some(cwd) + .filter(|cwd| cwd.as_os_str().len() > 0) + .map(Arc::from) + .or_else(|| { + self.session_by_id(session_id) + .and_then(|session| { + session + .read(cx) + .configuration() + .and_then(|config| config.request.cwd()) + }) + .map(Arc::from) + }); cx.emit(DapStoreEvent::RunInTerminal { session_id, title: request_args.title, diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 6e24259ebd..e489841752 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -40,7 +40,7 @@ pub enum TerminalKind { command: Option, args: Vec, envs: HashMap, - cwd: PathBuf, + cwd: Option>, title: Option, }, } @@ -101,7 +101,7 @@ impl Project { self.active_project_directory(cx) } } - TerminalKind::Debug { cwd, .. } => Some(Arc::from(cwd.as_path())), + TerminalKind::Debug { cwd, .. } => cwd.clone(), }; let mut settings_location = None; @@ -205,7 +205,7 @@ impl Project { this.active_project_directory(cx) } } - TerminalKind::Debug { cwd, .. } => Some(Arc::from(cwd.as_path())), + TerminalKind::Debug { cwd, .. } => cwd.clone(), }; let ssh_details = this.ssh_details(cx);