debugger: Add extensions support (#30625)

Piotr Osiewicz and Anthony created

Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Anthony <anthony@zed.dev>

Change summary

Cargo.lock                                                    |  16 
Cargo.toml                                                    |   2 
crates/dap/src/adapters.rs                                    |   2 
crates/dap/src/inline_value.rs                                |   2 
crates/debug_adapter_extension/Cargo.toml                     |  20 
crates/debug_adapter_extension/LICENSE-GPL                    |   1 
crates/debug_adapter_extension/src/debug_adapter_extension.rs |  40 +
crates/debug_adapter_extension/src/extension_dap_adapter.rs   |  49 ++
crates/extension/Cargo.toml                                   |   2 
crates/extension/src/extension.rs                             |   7 
crates/extension/src/extension_host_proxy.rs                  |  21 
crates/extension/src/types.rs                                 |   2 
crates/extension/src/types/dap.rs                             |   5 
crates/extension_api/src/extension_api.rs                     |  18 
crates/extension_api/wit/since_v0.6.0/dap.wit                 |  56 ++
crates/extension_api/wit/since_v0.6.0/extension.wit           |   5 
crates/extension_host/Cargo.toml                              |   1 
crates/extension_host/src/wasm_host.rs                        |  25 +
crates/extension_host/src/wasm_host/wit.rs                    |  32 +
crates/extension_host/src/wasm_host/wit/since_v0_6_0.rs       | 107 ++++
tooling/workspace-hack/Cargo.toml                             |   2 
21 files changed, 402 insertions(+), 13 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -4134,6 +4134,18 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "debug_adapter_extension"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "dap",
+ "extension",
+ "gpui",
+ "workspace-hack",
+]
+
 [[package]]
 name = "debugger_tools"
 version = "0.1.0"
@@ -5039,6 +5051,7 @@ dependencies = [
  "async-tar",
  "async-trait",
  "collections",
+ "dap",
  "fs",
  "futures 0.3.31",
  "gpui",
@@ -5051,6 +5064,7 @@ dependencies = [
  "semantic_version",
  "serde",
  "serde_json",
+ "task",
  "toml 0.8.20",
  "util",
  "wasm-encoder 0.221.3",
@@ -5094,6 +5108,7 @@ dependencies = [
  "client",
  "collections",
  "ctor",
+ "dap",
  "env_logger 0.11.8",
  "extension",
  "fs",
@@ -18018,7 +18033,6 @@ dependencies = [
  "aho-corasick",
  "anstream",
  "arrayvec",
- "async-compression",
  "async-std",
  "async-tungstenite",
  "aws-config",

Cargo.toml 🔗

@@ -37,6 +37,7 @@ members = [
     "crates/dap",
     "crates/dap_adapters",
     "crates/db",
+    "crates/debug_adapter_extension",
     "crates/debugger_tools",
     "crates/debugger_ui",
     "crates/deepseek",
@@ -243,6 +244,7 @@ credentials_provider = { path = "crates/credentials_provider" }
 dap = { path = "crates/dap" }
 dap_adapters = { path = "crates/dap_adapters" }
 db = { path = "crates/db" }
+debug_adapter_extension = { path = "crates/debug_adapter_extension" }
 debugger_tools = { path = "crates/debugger_tools" }
 debugger_ui = { path = "crates/debugger_ui" }
 deepseek = { path = "crates/deepseek" }

crates/dap/src/adapters.rs 🔗

@@ -4,7 +4,7 @@ use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
 use async_trait::async_trait;
 use collections::HashMap;
-use dap_types::{StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest};
+pub use dap_types::{StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest};
 use futures::io::BufReader;
 use gpui::{AsyncApp, SharedString};
 pub use http_client::{HttpClient, github::latest_github_release};

crates/dap/src/inline_value.rs 🔗

@@ -29,7 +29,7 @@ pub struct InlineValueLocation {
 /// during debugging sessions. Implementors must also handle variable scoping
 /// themselves by traversing the syntax tree upwards to determine whether a
 /// variable is local or global.
-pub trait InlineValueProvider {
+pub trait InlineValueProvider: 'static + Send + Sync {
     /// Provides a list of inline value locations based on the given node and source code.
     ///
     /// # Parameters

crates/debug_adapter_extension/Cargo.toml 🔗

@@ -0,0 +1,20 @@
+[package]
+name = "debug_adapter_extension"
+version = "0.1.0"
+license = "GPL-3.0-or-later"
+publish.workspace = true
+edition.workspace = true
+
+[dependencies]
+anyhow.workspace = true
+async-trait.workspace = true
+dap.workspace = true
+extension.workspace = true
+gpui.workspace = true
+workspace-hack = { version = "0.1", path = "../../tooling/workspace-hack" }
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/debug_adapter_extension.rs"

crates/debug_adapter_extension/src/debug_adapter_extension.rs 🔗

@@ -0,0 +1,40 @@
+mod extension_dap_adapter;
+
+use std::sync::Arc;
+
+use dap::DapRegistry;
+use extension::{ExtensionDebugAdapterProviderProxy, ExtensionHostProxy};
+use extension_dap_adapter::ExtensionDapAdapter;
+use gpui::App;
+
+pub fn init(extension_host_proxy: Arc<ExtensionHostProxy>, cx: &mut App) {
+    let language_server_registry_proxy = DebugAdapterRegistryProxy::new(cx);
+    extension_host_proxy.register_debug_adapter_proxy(language_server_registry_proxy);
+}
+
+#[derive(Clone)]
+struct DebugAdapterRegistryProxy {
+    debug_adapter_registry: DapRegistry,
+}
+
+impl DebugAdapterRegistryProxy {
+    fn new(cx: &mut App) -> Self {
+        Self {
+            debug_adapter_registry: DapRegistry::global(cx).clone(),
+        }
+    }
+}
+
+impl ExtensionDebugAdapterProviderProxy for DebugAdapterRegistryProxy {
+    fn register_debug_adapter(
+        &self,
+        extension: Arc<dyn extension::Extension>,
+        debug_adapter_name: Arc<str>,
+    ) {
+        self.debug_adapter_registry
+            .add_adapter(Arc::new(ExtensionDapAdapter::new(
+                extension,
+                debug_adapter_name,
+            )));
+    }
+}

crates/debug_adapter_extension/src/extension_dap_adapter.rs 🔗

@@ -0,0 +1,49 @@
+use std::{path::PathBuf, sync::Arc};
+
+use anyhow::Result;
+use async_trait::async_trait;
+use dap::adapters::{
+    DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition,
+};
+use extension::Extension;
+use gpui::AsyncApp;
+
+pub(crate) struct ExtensionDapAdapter {
+    extension: Arc<dyn Extension>,
+    debug_adapter_name: Arc<str>,
+}
+
+impl ExtensionDapAdapter {
+    pub(crate) fn new(
+        extension: Arc<dyn extension::Extension>,
+        debug_adapter_name: Arc<str>,
+    ) -> Self {
+        Self {
+            extension,
+            debug_adapter_name,
+        }
+    }
+}
+
+#[async_trait(?Send)]
+impl DebugAdapter for ExtensionDapAdapter {
+    fn name(&self) -> DebugAdapterName {
+        self.debug_adapter_name.as_ref().into()
+    }
+
+    async fn get_binary(
+        &self,
+        _: &dyn DapDelegate,
+        config: &DebugTaskDefinition,
+        user_installed_path: Option<PathBuf>,
+        _cx: &mut AsyncApp,
+    ) -> Result<DebugAdapterBinary> {
+        self.extension
+            .get_dap_binary(
+                self.debug_adapter_name.clone(),
+                config.clone(),
+                user_installed_path,
+            )
+            .await
+    }
+}

crates/extension/Cargo.toml 🔗

@@ -17,6 +17,7 @@ async-compression.workspace = true
 async-tar.workspace = true
 async-trait.workspace = true
 collections.workspace = true
+dap.workspace = true
 fs.workspace = true
 futures.workspace = true
 gpui.workspace = true
@@ -29,6 +30,7 @@ parking_lot.workspace = true
 semantic_version.workspace = true
 serde.workspace = true
 serde_json.workspace = true
+task.workspace = true
 toml.workspace = true
 util.workspace = true
 wasm-encoder.workspace = true

crates/extension/src/extension.rs 🔗

@@ -135,6 +135,13 @@ pub trait Extension: Send + Sync + 'static {
         package_name: Arc<str>,
         kv_store: Arc<dyn KeyValueStoreDelegate>,
     ) -> Result<()>;
+
+    async fn get_dap_binary(
+        &self,
+        dap_name: Arc<str>,
+        config: DebugTaskDefinition,
+        user_installed_path: Option<PathBuf>,
+    ) -> Result<DebugAdapterBinary>;
 }
 
 pub fn parse_wasm_extension_version(

crates/extension/src/extension_host_proxy.rs 🔗

@@ -29,6 +29,7 @@ pub struct ExtensionHostProxy {
     slash_command_proxy: RwLock<Option<Arc<dyn ExtensionSlashCommandProxy>>>,
     context_server_proxy: RwLock<Option<Arc<dyn ExtensionContextServerProxy>>>,
     indexed_docs_provider_proxy: RwLock<Option<Arc<dyn ExtensionIndexedDocsProviderProxy>>>,
+    debug_adapter_provider_proxy: RwLock<Option<Arc<dyn ExtensionDebugAdapterProviderProxy>>>,
 }
 
 impl ExtensionHostProxy {
@@ -54,6 +55,7 @@ impl ExtensionHostProxy {
             slash_command_proxy: RwLock::default(),
             context_server_proxy: RwLock::default(),
             indexed_docs_provider_proxy: RwLock::default(),
+            debug_adapter_provider_proxy: RwLock::default(),
         }
     }
 
@@ -93,6 +95,11 @@ impl ExtensionHostProxy {
             .write()
             .replace(Arc::new(proxy));
     }
+    pub fn register_debug_adapter_proxy(&self, proxy: impl ExtensionDebugAdapterProviderProxy) {
+        self.debug_adapter_provider_proxy
+            .write()
+            .replace(Arc::new(proxy));
+    }
 }
 
 pub trait ExtensionThemeProxy: Send + Sync + 'static {
@@ -402,3 +409,17 @@ impl ExtensionIndexedDocsProviderProxy for ExtensionHostProxy {
         proxy.register_indexed_docs_provider(extension, provider_id)
     }
 }
+
+pub trait ExtensionDebugAdapterProviderProxy: Send + Sync + 'static {
+    fn register_debug_adapter(&self, extension: Arc<dyn Extension>, debug_adapter_name: Arc<str>);
+}
+
+impl ExtensionDebugAdapterProviderProxy for ExtensionHostProxy {
+    fn register_debug_adapter(&self, extension: Arc<dyn Extension>, debug_adapter_name: Arc<str>) {
+        let Some(proxy) = self.debug_adapter_provider_proxy.read().clone() else {
+            return;
+        };
+
+        proxy.register_debug_adapter(extension, debug_adapter_name)
+    }
+}

crates/extension/src/types.rs 🔗

@@ -1,10 +1,12 @@
 mod context_server;
+mod dap;
 mod lsp;
 mod slash_command;
 
 use std::ops::Range;
 
 pub use context_server::*;
+pub use dap::*;
 pub use lsp::*;
 pub use slash_command::*;
 

crates/extension/src/types/dap.rs 🔗

@@ -0,0 +1,5 @@
+pub use dap::{
+    StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,
+    adapters::{DebugAdapterBinary, DebugTaskDefinition, TcpArguments},
+};
+pub use task::{AttachRequest, DebugRequest, LaunchRequest, TcpArgumentsTemplate};

crates/extension_api/src/extension_api.rs 🔗

@@ -187,6 +187,16 @@ pub trait Extension: Send + Sync {
     ) -> Result<(), String> {
         Err("`index_docs` not implemented".to_string())
     }
+
+    /// Returns the debug adapter binary for the specified adapter name and configuration.
+    fn get_dap_binary(
+        &mut self,
+        _adapter_name: String,
+        _config: DebugTaskDefinition,
+        _user_provided_path: Option<String>,
+    ) -> Result<DebugAdapterBinary, String> {
+        Err("`get_dap_binary` not implemented".to_string())
+    }
 }
 
 /// Registers the provided type as a Zed extension.
@@ -371,6 +381,14 @@ impl wit::Guest for Component {
     ) -> Result<(), String> {
         extension().index_docs(provider, package, database)
     }
+
+    fn get_dap_binary(
+        adapter_name: String,
+        config: DebugTaskDefinition,
+        user_installed_path: Option<String>,
+    ) -> Result<DebugAdapterBinary, String> {
+        extension().get_dap_binary(adapter_name, config, user_installed_path)
+    }
 }
 
 /// The ID of a language server.

crates/extension_api/wit/since_v0.6.0/dap.wit 🔗

@@ -0,0 +1,56 @@
+interface dap {
+    use common.{env-vars};
+    record launch-request {
+        program: string,
+        cwd: option<string>,
+        args: list<string>,
+        envs: env-vars,
+    }
+
+    record attach-request {
+        process-id: option<u32>,
+    }
+
+    variant debug-request {
+        launch(launch-request),
+        attach(attach-request)
+    }
+
+    record tcp-arguments {
+        port: u16,
+        host: u32,
+        timeout: option<u64>,
+    }
+
+    record tcp-arguments-template {
+        port: option<u16>,
+        host: option<u32>,
+        timeout: option<u64>,
+    }
+    record debug-task-definition {
+        label: string,
+        adapter: string,
+        request: debug-request,
+        initialize-args: option<string>,
+        stop-on-entry: option<bool>,
+        tcp-connection: option<tcp-arguments-template>,
+    }
+
+    enum start-debugging-request-arguments-request {
+        launch,
+        attach,
+    }
+    record start-debugging-request-arguments {
+        configuration: string,
+        request: start-debugging-request-arguments-request,
+
+    }
+    record debug-adapter-binary {
+        command: string,
+        arguments: list<string>,
+        envs: env-vars,
+        cwd: option<string>,
+        connection: option<tcp-arguments>,
+        request-args: start-debugging-request-arguments
+    }
+}

crates/extension_api/wit/since_v0.6.0/extension.wit 🔗

@@ -2,6 +2,7 @@ package zed:extension;
 
 world extension {
     import context-server;
+    import dap;
     import github;
     import http-client;
     import platform;
@@ -10,6 +11,7 @@ world extension {
 
     use common.{env-vars, range};
     use context-server.{context-server-configuration};
+    use dap.{debug-adapter-binary, debug-task-definition};
     use lsp.{completion, symbol};
     use process.{command};
     use slash-command.{slash-command, slash-command-argument-completion, slash-command-output};
@@ -153,4 +155,7 @@ world extension {
 
     /// Indexes the docs for the specified package.
     export index-docs: func(provider-name: string, package-name: string, database: borrow<key-value-store>) -> result<_, string>;
+
+    /// Returns a configured debug adapter binary for a given debug task.
+    export get-dap-binary: func(adapter-name: string, config: debug-task-definition, user-installed-path: option<string>) -> result<debug-adapter-binary, string>;
 }

crates/extension_host/Cargo.toml 🔗

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

crates/extension_host/src/wasm_host.rs 🔗

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

crates/extension_host/src/wasm_host/wit.rs 🔗

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

crates/extension_host/src/wasm_host/wit/since_v0_6_0.rs 🔗

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

tooling/workspace-hack/Cargo.toml 🔗

@@ -19,7 +19,6 @@ ahash = { version = "0.8", features = ["serde"] }
 aho-corasick = { version = "1" }
 anstream = { version = "0.6" }
 arrayvec = { version = "0.7", features = ["serde"] }
-async-compression = { version = "0.4", default-features = false, features = ["deflate", "deflate64", "futures-io", "gzip"] }
 async-std = { version = "1", features = ["attributes", "unstable"] }
 async-tungstenite = { version = "0.29", features = ["tokio-rustls-manual-roots"] }
 aws-config = { version = "1", features = ["behavior-version-latest"] }
@@ -136,7 +135,6 @@ ahash = { version = "0.8", features = ["serde"] }
 aho-corasick = { version = "1" }
 anstream = { version = "0.6" }
 arrayvec = { version = "0.7", features = ["serde"] }
-async-compression = { version = "0.4", default-features = false, features = ["deflate", "deflate64", "futures-io", "gzip"] }
 async-std = { version = "1", features = ["attributes", "unstable"] }
 async-tungstenite = { version = "0.29", features = ["tokio-rustls-manual-roots"] }
 aws-config = { version = "1", features = ["behavior-version-latest"] }