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.
This commit is contained in:
Piotr Osiewicz 2025-06-12 17:27:44 +02:00 committed by GitHub
parent bb5a763ef7
commit 5923ba4992
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 63 additions and 25 deletions

View file

@ -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)?,

View file

@ -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