dap: Allow user to pass custom envs to adapter via project settings (#40490)

It is now possible to configure logging level of CodeLLDB adapter via
envs specified in project settings like so:

```
{
    "dap": {
        "CodeLLDB": {
            "envs": {
                "RUST_LOG": "debug"
            }
        }
    }
}
```

Release Notes:

- N/A
This commit is contained in:
Jakub Konka
2025-10-17 18:48:00 +02:00
committed by GitHub
parent c1e87c8a00
commit 73e028c01c
13 changed files with 62 additions and 19 deletions

1
Cargo.lock generated
View File

@@ -4852,6 +4852,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"collections",
"dap",
"extension",
"gpui",

View File

@@ -1925,6 +1925,11 @@
// DAP Specific settings.
"dap": {
// Specify the DAP name as a key here.
"CodeLLDB": {
"env": {
"RUST_LOG": "info"
}
}
},
// Common language server settings.
"global_lsp_settings": {

View File

@@ -356,6 +356,7 @@ pub trait DebugAdapter: 'static + Send + Sync {
config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary>;
@@ -455,6 +456,7 @@ impl DebugAdapter for FakeAdapter {
task_definition: &DebugTaskDefinition,
_: Option<PathBuf>,
_: Option<Vec<String>>,
_: Option<HashMap<String, String>>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
let connection = task_definition

View File

@@ -2,6 +2,7 @@ use std::{path::PathBuf, sync::OnceLock};
use anyhow::{Context as _, Result};
use async_trait::async_trait;
use collections::HashMap;
use dap::adapters::{DebugTaskDefinition, latest_github_release};
use futures::StreamExt;
use gpui::AsyncApp;
@@ -329,6 +330,7 @@ impl DebugAdapter for CodeLldbDebugAdapter {
config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
let mut command = user_installed_path
@@ -378,11 +380,6 @@ impl DebugAdapter for CodeLldbDebugAdapter {
};
let mut json_config = config.config.clone();
// Enable info level for CodeLLDB by default.
// Logs can then be viewed in our DAP logs.
let mut envs = collections::HashMap::default();
envs.insert("RUST_LOG".to_string(), "info".to_string());
Ok(DebugAdapterBinary {
command: Some(command.unwrap()),
cwd: Some(delegate.worktree_root_path().to_path_buf()),
@@ -407,7 +404,7 @@ impl DebugAdapter for CodeLldbDebugAdapter {
request_args: self
.request_args(delegate, json_config, &config.label)
.await?,
envs,
envs: user_env.unwrap_or_default(),
connection: None,
})
}

View File

@@ -1,7 +1,8 @@
use std::{collections::HashMap, ffi::OsStr};
use std::ffi::OsStr;
use anyhow::{Context as _, Result, bail};
use async_trait::async_trait;
use collections::HashMap;
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
use gpui::AsyncApp;
use task::{DebugScenario, ZedDebugConfig};
@@ -160,6 +161,7 @@ impl DebugAdapter for GdbDebugAdapter {
config: &DebugTaskDefinition,
user_installed_path: Option<std::path::PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
let user_setting_path = user_installed_path
@@ -188,7 +190,7 @@ impl DebugAdapter for GdbDebugAdapter {
Ok(DebugAdapterBinary {
command: Some(gdb_path),
arguments: user_args.unwrap_or_else(|| vec!["-i=dap".into()]),
envs: HashMap::default(),
envs: user_env.unwrap_or_default(),
cwd: Some(delegate.worktree_root_path().to_path_buf()),
connection: None,
request_args: StartDebuggingRequestArguments {

View File

@@ -409,6 +409,7 @@ impl DebugAdapter for GoDebugAdapter {
task_definition: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
_cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
let adapter_path = paths::debug_adapters_dir().join(&Self::ADAPTER_NAME);
@@ -460,7 +461,7 @@ impl DebugAdapter for GoDebugAdapter {
let connection;
let mut configuration = task_definition.config.clone();
let mut envs = HashMap::default();
let mut envs = user_env.unwrap_or_default();
if let Some(configuration) = configuration.as_object_mut() {
configuration

View File

@@ -52,12 +52,13 @@ impl JsDebugAdapter {
task_definition: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
_: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default();
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
let mut envs = HashMap::default();
let mut envs = user_env.unwrap_or_default();
let mut configuration = task_definition.config.clone();
if let Some(configuration) = configuration.as_object_mut() {
@@ -100,9 +101,9 @@ impl JsDebugAdapter {
}
if let Some(env) = configuration.get("env").cloned()
&& let Ok(env) = serde_json::from_value(env)
&& let Ok(env) = serde_json::from_value::<HashMap<String, String>>(env)
{
envs = env;
envs.extend(env.into_iter());
}
configuration
@@ -504,6 +505,7 @@ impl DebugAdapter for JsDebugAdapter {
config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
if self.checked.set(()).is_ok() {
@@ -521,8 +523,15 @@ impl DebugAdapter for JsDebugAdapter {
}
}
self.get_installed_binary(delegate, config, user_installed_path, user_args, cx)
.await
self.get_installed_binary(
delegate,
config,
user_installed_path,
user_args,
user_env,
cx,
)
.await
}
fn label_for_child_session(&self, args: &StartDebuggingRequestArguments) -> Option<String> {

View File

@@ -1,5 +1,6 @@
use crate::*;
use anyhow::{Context as _, bail};
use collections::HashMap;
use dap::{DebugRequest, StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
use fs::RemoveOptions;
use futures::{StreamExt, TryStreamExt};
@@ -16,7 +17,6 @@ use std::ffi::OsString;
use std::net::Ipv4Addr;
use std::str::FromStr;
use std::{
collections::HashMap,
ffi::OsStr,
path::{Path, PathBuf},
};
@@ -312,6 +312,7 @@ impl PythonDebugAdapter {
config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
python_from_toolchain: Option<String>,
) -> Result<DebugAdapterBinary> {
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
@@ -349,7 +350,7 @@ impl PythonDebugAdapter {
timeout,
}),
cwd: Some(delegate.worktree_root_path().to_path_buf()),
envs: HashMap::default(),
envs: user_env.unwrap_or_default(),
request_args: self.request_args(delegate, config).await?,
})
}
@@ -744,6 +745,7 @@ impl DebugAdapter for PythonDebugAdapter {
config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
user_args: Option<Vec<String>>,
user_env: Option<HashMap<String, String>>,
cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
if let Some(local_path) = &user_installed_path {
@@ -752,7 +754,14 @@ impl DebugAdapter for PythonDebugAdapter {
local_path.display()
);
return self
.get_installed_binary(delegate, config, Some(local_path.clone()), user_args, None)
.get_installed_binary(
delegate,
config,
Some(local_path.clone()),
user_args,
user_env,
None,
)
.await;
}
@@ -790,12 +799,13 @@ impl DebugAdapter for PythonDebugAdapter {
config,
None,
user_args,
user_env,
Some(toolchain.path.to_string()),
)
.await;
}
self.get_installed_binary(delegate, config, None, user_args, None)
self.get_installed_binary(delegate, config, None, user_args, user_env, None)
.await
}

View File

@@ -8,6 +8,7 @@ edition.workspace = true
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
collections.workspace = true
dap.workspace = true
extension.workspace = true
gpui.workspace = true

View File

@@ -6,6 +6,7 @@ use std::{
use anyhow::{Context, Result};
use async_trait::async_trait;
use collections::HashMap;
use dap::{
StartDebuggingRequestArgumentsRequest,
adapters::{
@@ -91,6 +92,8 @@ impl DebugAdapter for ExtensionDapAdapter {
user_installed_path: Option<PathBuf>,
// TODO support user args in the extension API
_user_args: Option<Vec<String>>,
// TODO support user env in the extension API
_user_env: Option<HashMap<String, String>>,
_cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
self.extension

View File

@@ -264,13 +264,21 @@ impl DapStore {
DapBinary::Custom(binary) => Some(PathBuf::from(binary)),
});
let user_args = dap_settings.map(|s| s.args.clone());
let user_env = dap_settings.map(|s| s.env.clone());
let delegate = self.delegate(worktree, console, cx);
let cwd: Arc<Path> = worktree.read(cx).abs_path().as_ref().into();
cx.spawn(async move |this, cx| {
let mut binary = adapter
.get_binary(&delegate, &definition, user_installed_path, user_args, cx)
.get_binary(
&delegate,
&definition,
user_installed_path,
user_args,
user_env,
cx,
)
.await?;
let env = this

View File

@@ -1215,6 +1215,7 @@ pub fn local_settings_kind_to_proto(kind: LocalSettingsKind) -> proto::LocalSett
pub struct DapSettings {
pub binary: DapBinary,
pub args: Vec<String>,
pub env: HashMap<String, String>,
}
impl From<DapSettingsContent> for DapSettings {
@@ -1224,6 +1225,7 @@ impl From<DapSettingsContent> for DapSettings {
.binary
.map_or_else(|| DapBinary::Default, |binary| DapBinary::Custom(binary)),
args: content.args.unwrap_or_default(),
env: content.env.unwrap_or_default(),
}
}
}

View File

@@ -154,6 +154,8 @@ pub struct DapSettingsContent {
pub binary: Option<String>,
#[serde(default)]
pub args: Option<Vec<String>>,
#[serde(default)]
pub env: Option<HashMap<String, String>>,
}
#[skip_serializing_none]