From 5923ba4992960a91c0b0c7ccd05e73aa39f8ca36 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Thu, 12 Jun 2025 17:27:44 +0200 Subject: [PATCH] debugger: Allow use of externally-managed Delve for Go debugging (#32613) Closes #ISSUE Release Notes: - Go debug scenarios can now use an externally-managed Delve instance. Use `tcp_connection` in your debug scenario definition to provide adapter's address. --- crates/dap_adapters/src/go.rs | 63 ++++++++++++++++++++++------------- docs/src/debugger.md | 25 ++++++++++++-- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/crates/dap_adapters/src/go.rs b/crates/dap_adapters/src/go.rs index 7b80c303b5..727efdc6fc 100644 --- a/crates/dap_adapters/src/go.rs +++ b/crates/dap_adapters/src/go.rs @@ -2,7 +2,7 @@ use anyhow::{Context as _, bail}; use dap::{ StartDebuggingRequestArguments, adapters::{ - DebugTaskDefinition, DownloadedFileType, download_adapter_from_github, + DebugTaskDefinition, DownloadedFileType, TcpArguments, download_adapter_from_github, latest_github_release, }, }; @@ -10,6 +10,7 @@ use dap::{ use gpui::{AsyncApp, SharedString}; use language::LanguageName; use std::{collections::HashMap, env::consts, ffi::OsStr, path::PathBuf, sync::OnceLock}; +use task::TcpArgumentsTemplate; use util; use crate::*; @@ -433,10 +434,6 @@ impl DebugAdapter for GoDebugAdapter { adapter_path.join("dlv").to_string_lossy().to_string() }; - let minidelve_path = self.install_shim(delegate).await?; - let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default(); - - let (host, port, _) = crate::configure_tcp_connection(tcp_connection).await?; let cwd = task_definition .config @@ -445,22 +442,9 @@ impl DebugAdapter for GoDebugAdapter { .map(PathBuf::from) .unwrap_or_else(|| delegate.worktree_root_path().to_path_buf()); - let arguments = if cfg!(windows) { - vec![ - delve_path, - "dap".into(), - "--listen".into(), - format!("{}:{}", host, port), - "--headless".into(), - ] - } else { - vec![ - delve_path, - "dap".into(), - "--listen".into(), - format!("{}:{}", host, port), - ] - }; + let arguments; + let command; + let connection; let mut configuration = task_definition.config.clone(); if let Some(configuration) = configuration.as_object_mut() { @@ -469,12 +453,45 @@ impl DebugAdapter for GoDebugAdapter { .or_insert_with(|| delegate.worktree_root_path().to_string_lossy().into()); } + if let Some(connection_options) = &task_definition.tcp_connection { + command = None; + arguments = vec![]; + let (host, port, timeout) = + crate::configure_tcp_connection(connection_options.clone()).await?; + connection = Some(TcpArguments { + host, + port, + timeout, + }); + } else { + let minidelve_path = self.install_shim(delegate).await?; + let (host, port, _) = + crate::configure_tcp_connection(TcpArgumentsTemplate::default()).await?; + command = Some(minidelve_path.to_string_lossy().into_owned()); + connection = None; + arguments = if cfg!(windows) { + vec![ + delve_path, + "dap".into(), + "--listen".into(), + format!("{}:{}", host, port), + "--headless".into(), + ] + } else { + vec![ + delve_path, + "dap".into(), + "--listen".into(), + format!("{}:{}", host, port), + ] + }; + } Ok(DebugAdapterBinary { - command: Some(minidelve_path.to_string_lossy().into_owned()), + command, arguments, cwd: Some(cwd), envs: HashMap::default(), - connection: None, + connection, request_args: StartDebuggingRequestArguments { configuration, request: self.request_kind(&task_definition.config)?, diff --git a/docs/src/debugger.md b/docs/src/debugger.md index 2ffe585614..f6ae7c0561 100644 --- a/docs/src/debugger.md +++ b/docs/src/debugger.md @@ -281,7 +281,7 @@ Given an externally-ran web server (e.g. with `npx serve` or `npx live-server`) #### Go Zed uses [delve](https://github.com/go-delve/delve?tab=readme-ov-file) to debug Go applications. Zed will automatically create debug scenarios for `func main` in your main packages, and also -for any tests, so you can use the Play button in the gutter to debug these without configuration. We do not yet support attaching to an existing running copy of delve. +for any tests, so you can use the Play button in the gutter to debug these without configuration. ##### Debug Go Packages @@ -350,6 +350,27 @@ and the "build" command should build that. } ``` +##### Attaching to an existing instance of Delve + +You might find yourself needing to connect to an existing instance of Delve that's not necessarily running on your machine; in such case, you can use `tcp_arguments` to instrument Zed's connection to Delve. + +```` +{ + "adapter": "Delve", + "label": "Connect to a running Delve instance", + "program": "/Users/zed/Projects/language_repositories/golang/hello/hello", + "cwd": "/Users/zed/Projects/language_repositories/golang/hello", + "args": [], + "env": {}, + "request": "launch", + "mode": "exec", + "stopOnEntry": false, + "tcp_connection": { "host": "123.456.789.012", "port": 53412 } +} +``` + +In such case Zed won't spawn a new instance of Delve, as it opts to use an existing one. The consequence of this is that *there will be no terminal* in Zed; you have to interact with the Delve instance directly, as it handles stdin/stdout of the debuggee. + ### Ruby To run a ruby task in the debugger, you will need to configure it in the `.zed/debug.json` file in your project. We don't yet have automatic detection of ruby tasks, nor do we support connecting to an existing process. @@ -371,7 +392,7 @@ The configuration should look like this: // "cwd": "" } } -``` +```` ## Breakpoints