debugger: Add extensions support (#30625)

Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
Piotr Osiewicz 2025-05-14 00:42:51 +02:00 committed by GitHub
parent 6fc9036063
commit 9826b7b5c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 402 additions and 13 deletions

View file

@ -22,6 +22,7 @@ async-tar.workspace = true
async-trait.workspace = true
client.workspace = true
collections.workspace = true
dap.workspace = true
extension.workspace = true
fs.workspace = true
futures.workspace = true

View file

@ -4,9 +4,9 @@ use crate::ExtensionManifest;
use anyhow::{Context as _, Result, anyhow, bail};
use async_trait::async_trait;
use extension::{
CodeLabel, Command, Completion, ContextServerConfiguration, ExtensionHostProxy,
KeyValueStoreDelegate, ProjectDelegate, SlashCommand, SlashCommandArgumentCompletion,
SlashCommandOutput, Symbol, WorktreeDelegate,
CodeLabel, Command, Completion, ContextServerConfiguration, DebugAdapterBinary,
DebugTaskDefinition, ExtensionHostProxy, KeyValueStoreDelegate, ProjectDelegate, SlashCommand,
SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate,
};
use fs::{Fs, normalize_path};
use futures::future::LocalBoxFuture;
@ -374,6 +374,25 @@ impl extension::Extension for WasmExtension {
})
.await
}
async fn get_dap_binary(
&self,
dap_name: Arc<str>,
config: DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
) -> Result<DebugAdapterBinary> {
self.call(|extension, store| {
async move {
let dap_binary = extension
.call_get_dap_binary(store, dap_name, config, user_installed_path)
.await?
.map_err(|err| anyhow!("{err:?}"))?;
let dap_binary = dap_binary.try_into()?;
Ok(dap_binary)
}
.boxed()
})
.await
}
}
pub struct WasmState {

View file

@ -7,16 +7,16 @@ mod since_v0_3_0;
mod since_v0_4_0;
mod since_v0_5_0;
mod since_v0_6_0;
use extension::{KeyValueStoreDelegate, WorktreeDelegate};
use extension::{DebugTaskDefinition, KeyValueStoreDelegate, WorktreeDelegate};
use language::LanguageName;
use lsp::LanguageServerName;
use release_channel::ReleaseChannel;
use since_v0_6_0 as latest;
use super::{WasmState, wasm_engine};
use anyhow::{Context as _, Result, anyhow};
use semantic_version::SemanticVersion;
use std::{ops::RangeInclusive, sync::Arc};
use since_v0_6_0 as latest;
use std::{ops::RangeInclusive, path::PathBuf, sync::Arc};
use wasmtime::{
Store,
component::{Component, Linker, Resource},
@ -25,7 +25,7 @@ use wasmtime::{
#[cfg(test)]
pub use latest::CodeLabelSpanLiteral;
pub use latest::{
CodeLabel, CodeLabelSpan, Command, ExtensionProject, Range, SlashCommand,
CodeLabel, CodeLabelSpan, Command, DebugAdapterBinary, ExtensionProject, Range, SlashCommand,
zed::extension::context_server::ContextServerConfiguration,
zed::extension::lsp::{
Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
@ -897,6 +897,30 @@ impl Extension {
}
}
}
pub async fn call_get_dap_binary(
&self,
store: &mut Store<WasmState>,
adapter_name: Arc<str>,
task: DebugTaskDefinition,
user_installed_path: Option<PathBuf>,
) -> Result<Result<DebugAdapterBinary, String>> {
match self {
Extension::V0_6_0(ext) => {
let dap_binary = ext
.call_get_dap_binary(
store,
&adapter_name,
&task.try_into()?,
user_installed_path.as_ref().and_then(|p| p.to_str()),
)
.await?
.map_err(|e| anyhow!("{e:?}"))?;
Ok(Ok(dap_binary))
}
_ => Err(anyhow!("`get_dap_binary` not available prior to v0.6.0")),
}
}
}
trait ToWasmtimeResult<T> {

View file

@ -1,4 +1,10 @@
use crate::wasm_host::wit::since_v0_6_0::slash_command::SlashCommandOutputSection;
use crate::wasm_host::wit::since_v0_6_0::{
dap::{
AttachRequest, DebugRequest, LaunchRequest, StartDebuggingRequestArguments,
StartDebuggingRequestArgumentsRequest, TcpArguments, TcpArgumentsTemplate,
},
slash_command::SlashCommandOutputSection,
};
use crate::wasm_host::wit::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind};
use crate::wasm_host::{WasmState, wit::ToWasmtimeResult};
use ::http_client::{AsyncBody, HttpRequestExt};
@ -17,6 +23,7 @@ use project::project_settings::ProjectSettings;
use semantic_version::SemanticVersion;
use std::{
env,
net::Ipv4Addr,
path::{Path, PathBuf},
sync::{Arc, OnceLock},
};
@ -72,6 +79,101 @@ impl From<Command> for extension::Command {
}
}
impl From<extension::LaunchRequest> for LaunchRequest {
fn from(value: extension::LaunchRequest) -> Self {
Self {
program: value.program,
cwd: value.cwd.map(|path| path.to_string_lossy().into_owned()),
envs: value.env.into_iter().collect(),
args: value.args,
}
}
}
impl From<StartDebuggingRequestArgumentsRequest>
for extension::StartDebuggingRequestArgumentsRequest
{
fn from(value: StartDebuggingRequestArgumentsRequest) -> Self {
match value {
StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
}
}
}
impl TryFrom<StartDebuggingRequestArguments> for extension::StartDebuggingRequestArguments {
type Error = anyhow::Error;
fn try_from(value: StartDebuggingRequestArguments) -> Result<Self, Self::Error> {
Ok(Self {
configuration: serde_json::from_str(&value.configuration)?,
request: value.request.into(),
})
}
}
impl From<TcpArguments> for extension::TcpArguments {
fn from(value: TcpArguments) -> Self {
Self {
host: value.host.into(),
port: value.port,
timeout: value.timeout,
}
}
}
impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
fn from(value: extension::TcpArgumentsTemplate) -> Self {
Self {
host: value.host.map(Ipv4Addr::to_bits),
port: value.port,
timeout: value.timeout,
}
}
}
impl From<extension::AttachRequest> for AttachRequest {
fn from(value: extension::AttachRequest) -> Self {
Self {
process_id: value.process_id,
}
}
}
impl From<extension::DebugRequest> for DebugRequest {
fn from(value: extension::DebugRequest) -> Self {
match value {
extension::DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
extension::DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
}
}
}
impl TryFrom<extension::DebugTaskDefinition> for DebugTaskDefinition {
type Error = anyhow::Error;
fn try_from(value: extension::DebugTaskDefinition) -> Result<Self, Self::Error> {
let initialize_args = value.initialize_args.map(|s| s.to_string());
Ok(Self {
label: value.label.to_string(),
adapter: value.adapter.to_string(),
request: value.request.into(),
initialize_args,
stop_on_entry: value.stop_on_entry,
tcp_connection: value.tcp_connection.map(Into::into),
})
}
}
impl TryFrom<DebugAdapterBinary> for extension::DebugAdapterBinary {
type Error = anyhow::Error;
fn try_from(value: DebugAdapterBinary) -> Result<Self, Self::Error> {
Ok(Self {
command: value.command,
arguments: value.arguments,
envs: value.envs.into_iter().collect(),
cwd: value.cwd.map(|s| s.into()),
connection: value.connection.map(Into::into),
request_args: value.request_args.try_into()?,
})
}
}
impl From<CodeLabel> for extension::CodeLabel {
fn from(value: CodeLabel) -> Self {
Self {
@ -627,6 +729,9 @@ impl slash_command::Host for WasmState {}
#[async_trait]
impl context_server::Host for WasmState {}
#[async_trait]
impl dap::Host for WasmState {}
impl ExtensionImports for WasmState {
async fn get_settings(
&mut self,