ZIm/crates/debug_adapter_extension/src/extension_dap_adapter.rs
Cole Miller c9bd409732
debugger: Support passing custom arguments to debug adapters (#33251)
Custom arguments replace any arguments that we normally pass to the DAP.
For interpreted languages, they are passed to the interpreter after the
DAP path or module. They can be combined with a custom binary, or you
can omit `dap.binary` and just customize the arguments to the DAPs we
download.

This doesn't take care of updating the extension API to support custom
arguments.

Release Notes:

- debugger: Implemented support for passing custom arguments to a debug
adapter binary using the `dap.args` setting.
- debugger: Fixed not being able to use the `dap` setting in
`.zed/settings.json`.
2025-06-23 17:06:48 +00:00

117 lines
3.3 KiB
Rust

use std::{
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
};
use anyhow::{Context, Result};
use async_trait::async_trait;
use dap::{
StartDebuggingRequestArgumentsRequest,
adapters::{
DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition,
},
};
use extension::{Extension, WorktreeDelegate};
use gpui::AsyncApp;
use task::{DebugScenario, ZedDebugConfig};
pub(crate) struct ExtensionDapAdapter {
extension: Arc<dyn Extension>,
debug_adapter_name: Arc<str>,
schema: serde_json::Value,
}
impl ExtensionDapAdapter {
pub(crate) fn new(
extension: Arc<dyn extension::Extension>,
debug_adapter_name: Arc<str>,
schema_path: &Path,
) -> Result<Self> {
let schema = std::fs::read_to_string(&schema_path).with_context(|| {
format!(
"Failed to read debug adapter schema for {debug_adapter_name} (from path: `{schema_path:?}`)"
)
})?;
let schema = serde_json::Value::from_str(&schema).with_context(|| {
format!("Debug adapter schema for {debug_adapter_name} is not a valid JSON")
})?;
Ok(Self {
extension,
debug_adapter_name,
schema,
})
}
}
/// An adapter that allows an [`dap::adapters::DapDelegate`] to be used as a [`WorktreeDelegate`].
struct WorktreeDelegateAdapter(pub Arc<dyn DapDelegate>);
#[async_trait]
impl WorktreeDelegate for WorktreeDelegateAdapter {
fn id(&self) -> u64 {
self.0.worktree_id().to_proto()
}
fn root_path(&self) -> String {
self.0.worktree_root_path().to_string_lossy().to_string()
}
async fn read_text_file(&self, path: PathBuf) -> Result<String> {
self.0.read_text_file(path).await
}
async fn which(&self, binary_name: String) -> Option<String> {
self.0
.which(binary_name.as_ref())
.await
.map(|path| path.to_string_lossy().to_string())
}
async fn shell_env(&self) -> Vec<(String, String)> {
self.0.shell_env().await.into_iter().collect()
}
}
#[async_trait(?Send)]
impl DebugAdapter for ExtensionDapAdapter {
fn name(&self) -> DebugAdapterName {
self.debug_adapter_name.as_ref().into()
}
fn dap_schema(&self) -> serde_json::Value {
self.schema.clone()
}
async fn get_binary(
&self,
delegate: &Arc<dyn DapDelegate>,
config: &DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
// TODO support user args in the extension API
_user_args: Option<Vec<String>>,
_cx: &mut AsyncApp,
) -> Result<DebugAdapterBinary> {
self.extension
.get_dap_binary(
self.debug_adapter_name.clone(),
config.clone(),
user_installed_path,
Arc::new(WorktreeDelegateAdapter(delegate.clone())),
)
.await
}
async fn config_from_zed_format(&self, zed_scenario: ZedDebugConfig) -> Result<DebugScenario> {
self.extension.dap_config_to_scenario(zed_scenario).await
}
async fn request_kind(
&self,
config: &serde_json::Value,
) -> Result<StartDebuggingRequestArgumentsRequest> {
self.extension
.dap_request_kind(self.debug_adapter_name.clone(), config.clone())
.await
}
}