pub mod wit; use crate::capability_granter::CapabilityGranter;use crate::{ExtensionManifest, ExtensionSettings};use anyhow::{Context as _, Result, anyhow, bail};use async_trait::async_trait;use dap::{DebugRequest, StartDebuggingRequestArgumentsRequest};use extension::{ CodeLabel, Command, Completion, ContextServerConfiguration, DebugAdapterBinary, DebugTaskDefinition, ExtensionCapability, ExtensionHostProxy, KeyValueStoreDelegate, ProjectDelegate, SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate,};use fs::Fs;use futures::future::LocalBoxFuture;use futures::{ Future, FutureExt, StreamExt as _, channel::{ mpsc::{self, UnboundedSender}, oneshot, }, future::BoxFuture,};use gpui::{App, AsyncApp, BackgroundExecutor, Task};use http_client::HttpClient;use language::LanguageName;use lsp::LanguageServerName;use moka::sync::Cache;use node_runtime::NodeRuntime;use release_channel::ReleaseChannel;use semver::Version;use settings::Settings;use std::{ borrow::Cow, path::{Path, PathBuf}, sync::{Arc, LazyLock, OnceLock}, time::Duration,};use task::{DebugScenario, SpawnInTerminal, TaskTemplate, ZedDebugConfig};use util::paths::SanitizedPath;use wasmtime::{ CacheStore, Engine, Store, component::{Component, ResourceTable},};use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView};use wit::Extension; pub struct WasmHost { engine: Engine, release_channel: ReleaseChannel, http_client: Arc<dyn HttpClient>, node_runtime: NodeRuntime, pub(crate) proxy: Arc<ExtensionHostProxy>, fs: Arc<dyn Fs>, pub work_dir: PathBuf, /// The capabilities granted to extensions running on the host. pub(crate) granted_capabilities: Vec<ExtensionCapability>, _main_thread_message_task: Task<()>, main_thread_message_tx: mpsc::UnboundedSender<MainThreadCall>,} #[derive(Clone, Debug)]pub struct WasmExtension { tx: UnboundedSender<ExtensionCall>, pub manifest: Arc<ExtensionManifest>, pub work_dir: Arc<Path>, #[allow(unused)] pub zed_api_version: Version, _task: Arc<Task<Result<(), gpui_tokio::JoinError>>>,} impl Drop for WasmExtension { fn drop(&mut self) { self.tx.close_channel(); }} #[async_trait]impl extension::Extension for WasmExtension { fn manifest(&self) -> Arc<ExtensionManifest> { self.manifest.clone() } fn work_dir(&self) -> Arc<Path> { self.work_dir.clone() } async fn language_server_command( &self, language_server_id: LanguageServerName, language_name: LanguageName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Command> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let command = extension .call_language_server_command( store, &language_server_id, &language_name, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; Ok(command.into()) } .boxed() }) .await? } async fn language_server_initialization_options( &self, language_server_id: LanguageServerName, language_name: LanguageName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_initialization_options( store, &language_server_id, &language_name, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn language_server_workspace_configuration( &self, language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_workspace_configuration( store, &language_server_id, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn language_server_initialization_options_schema( &self, language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; extension .call_language_server_initialization_options_schema( store, &language_server_id, resource, ) .await } .boxed() }) .await? } async fn language_server_workspace_configuration_schema( &self, language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; extension .call_language_server_workspace_configuration_schema( store, &language_server_id, resource, ) .await } .boxed() }) .await? } async fn language_server_additional_initialization_options( &self, language_server_id: LanguageServerName, target_language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_additional_initialization_options( store, &language_server_id, &target_language_server_id, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn language_server_additional_workspace_configuration( &self, language_server_id: LanguageServerName, target_language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_additional_workspace_configuration( store, &language_server_id, &target_language_server_id, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn labels_for_completions( &self, language_server_id: LanguageServerName, completions: Vec<Completion>, ) -> Result<Vec<Option<CodeLabel>>> { self.call(|extension, store| { async move { let labels = extension .call_labels_for_completions( store, &language_server_id, completions.into_iter().map(Into::into).collect(), ) .await? .map_err(|err| store.data().extension_error(err))?; Ok(labels .into_iter() .map(|label| label.map(Into::into)) .collect()) } .boxed() }) .await? } async fn labels_for_symbols( &self, language_server_id: LanguageServerName, symbols: Vec<Symbol>, ) -> Result<Vec<Option<CodeLabel>>> { self.call(|extension, store| { async move { let labels = extension .call_labels_for_symbols( store, &language_server_id, symbols.into_iter().map(Into::into).collect(), ) .await? .map_err(|err| store.data().extension_error(err))?; Ok(labels .into_iter() .map(|label| label.map(Into::into)) .collect()) } .boxed() }) .await? } async fn complete_slash_command_argument( &self, command: SlashCommand, arguments: Vec<String>, ) -> Result<Vec<SlashCommandArgumentCompletion>> { self.call(|extension, store| { async move { let completions = extension .call_complete_slash_command_argument(store, &command.into(), &arguments) .await? .map_err(|err| store.data().extension_error(err))?; Ok(completions.into_iter().map(Into::into).collect()) } .boxed() }) .await? } async fn run_slash_command( &self, command: SlashCommand, arguments: Vec<String>, delegate: Option<Arc<dyn WorktreeDelegate>>, ) -> Result<SlashCommandOutput> { self.call(|extension, store| { async move { let resource = if let Some(delegate) = delegate { Some(store.data_mut().table.push(delegate)?) } else { None }; let output = extension .call_run_slash_command(store, &command.into(), &arguments, resource) .await? .map_err(|err| store.data().extension_error(err))?; Ok(output.into()) } .boxed() }) .await? } async fn context_server_command( &self, context_server_id: Arc<str>, project: Arc<dyn ProjectDelegate>, ) -> Result<Command> { self.call(|extension, store| { async move { let project_resource = store.data_mut().table.push(project)?; let command = extension .call_context_server_command(store, context_server_id.clone(), project_resource) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(command.into()) } .boxed() }) .await? } async fn context_server_configuration( &self, context_server_id: Arc<str>, project: Arc<dyn ProjectDelegate>, ) -> Result<Option<ContextServerConfiguration>> { self.call(|extension, store| { async move { let project_resource = store.data_mut().table.push(project)?; let Some(configuration) = extension .call_context_server_configuration( store, context_server_id.clone(), project_resource, ) .await? .map_err(|err| store.data().extension_error(err))? else { return Ok(None); }; Ok(Some(configuration.try_into()?)) } .boxed() }) .await? } async fn suggest_docs_packages(&self, provider: Arc<str>) -> Result<Vec<String>> { self.call(|extension, store| { async move { let packages = extension .call_suggest_docs_packages(store, provider.as_ref()) .await? .map_err(|err| store.data().extension_error(err))?; Ok(packages) } .boxed() }) .await? } async fn index_docs( &self, provider: Arc<str>, package_name: Arc<str>, kv_store: Arc<dyn KeyValueStoreDelegate>, ) -> Result<()> { self.call(|extension, store| { async move { let kv_store_resource = store.data_mut().table.push(kv_store)?; extension .call_index_docs( store, provider.as_ref(), package_name.as_ref(), kv_store_resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(()) } .boxed() }) .await? } async fn get_dap_binary( &self, dap_name: Arc<str>, config: DebugTaskDefinition, user_installed_path: Option<PathBuf>, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<DebugAdapterBinary> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let dap_binary = extension .call_get_dap_binary(store, dap_name, config, user_installed_path, resource) .await? .map_err(|err| store.data().extension_error(err))?; let dap_binary = dap_binary.try_into()?; Ok(dap_binary) } .boxed() }) .await? } async fn dap_request_kind( &self, dap_name: Arc<str>, config: serde_json::Value, ) -> Result<StartDebuggingRequestArgumentsRequest> { self.call(|extension, store| { async move { let kind = extension .call_dap_request_kind(store, dap_name, config) .await? .map_err(|err| store.data().extension_error(err))?; Ok(kind.into()) } .boxed() }) .await? } async fn dap_config_to_scenario(&self, config: ZedDebugConfig) -> Result<DebugScenario> { self.call(|extension, store| { async move { let kind = extension .call_dap_config_to_scenario(store, config) .await? .map_err(|err| store.data().extension_error(err))?; Ok(kind) } .boxed() }) .await? } async fn dap_locator_create_scenario( &self, locator_name: String, build_config_template: TaskTemplate, resolved_label: String, debug_adapter_name: String, ) -> Result<Option<DebugScenario>> { self.call(|extension, store| { async move { extensionLines 1–500 of 1110. Load more
pub mod wit; use crate::capability_granter::CapabilityGranter; use crate::{ExtensionManifest, ExtensionSettings}; use anyhow::{Context as _, Result, anyhow, bail}; use async_trait::async_trait; use dap::{DebugRequest, StartDebuggingRequestArgumentsRequest}; use extension::{ CodeLabel, Command, Completion, ContextServerConfiguration, DebugAdapterBinary, DebugTaskDefinition, ExtensionCapability, ExtensionHostProxy, KeyValueStoreDelegate, ProjectDelegate, SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate, }; use fs::Fs; use futures::future::LocalBoxFuture; use futures::{ Future, FutureExt, StreamExt as _, channel::{ mpsc::{self, UnboundedSender}, oneshot, }, future::BoxFuture, }; use gpui::{App, AsyncApp, BackgroundExecutor, Task}; use http_client::HttpClient; use language::LanguageName; use lsp::LanguageServerName; use moka::sync::Cache; use node_runtime::NodeRuntime; use release_channel::ReleaseChannel; use semver::Version; use settings::Settings; use std::{ borrow::Cow, path::{Path, PathBuf}, sync::{Arc, LazyLock, OnceLock}, time::Duration, }; use task::{DebugScenario, SpawnInTerminal, TaskTemplate, ZedDebugConfig}; use util::paths::SanitizedPath; use wasmtime::{ CacheStore, Engine, Store, component::{Component, ResourceTable}, }; use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView}; use wit::Extension; pub struct WasmHost { engine: Engine, release_channel: ReleaseChannel, http_client: Arc<dyn HttpClient>, node_runtime: NodeRuntime, pub(crate) proxy: Arc<ExtensionHostProxy>, fs: Arc<dyn Fs>, pub work_dir: PathBuf, /// The capabilities granted to extensions running on the host. pub(crate) granted_capabilities: Vec<ExtensionCapability>, _main_thread_message_task: Task<()>, main_thread_message_tx: mpsc::UnboundedSender<MainThreadCall>, } #[derive(Clone, Debug)] pub struct WasmExtension { tx: UnboundedSender<ExtensionCall>, pub manifest: Arc<ExtensionManifest>, pub work_dir: Arc<Path>, #[allow(unused)] pub zed_api_version: Version, _task: Arc<Task<Result<(), gpui_tokio::JoinError>>>, } impl Drop for WasmExtension { fn drop(&mut self) { self.tx.close_channel(); } } #[async_trait] impl extension::Extension for WasmExtension { fn manifest(&self) -> Arc<ExtensionManifest> { self.manifest.clone() } fn work_dir(&self) -> Arc<Path> { self.work_dir.clone() } async fn language_server_command( &self, language_server_id: LanguageServerName, language_name: LanguageName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Command> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let command = extension .call_language_server_command( store, &language_server_id, &language_name, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; Ok(command.into()) } .boxed() }) .await? } async fn language_server_initialization_options( &self, language_server_id: LanguageServerName, language_name: LanguageName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_initialization_options( store, &language_server_id, &language_name, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn language_server_workspace_configuration( &self, language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_workspace_configuration( store, &language_server_id, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn language_server_initialization_options_schema( &self, language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; extension .call_language_server_initialization_options_schema( store, &language_server_id, resource, ) .await } .boxed() }) .await? } async fn language_server_workspace_configuration_schema( &self, language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; extension .call_language_server_workspace_configuration_schema( store, &language_server_id, resource, ) .await } .boxed() }) .await? } async fn language_server_additional_initialization_options( &self, language_server_id: LanguageServerName, target_language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_additional_initialization_options( store, &language_server_id, &target_language_server_id, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn language_server_additional_workspace_configuration( &self, language_server_id: LanguageServerName, target_language_server_id: LanguageServerName, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<Option<String>> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let options = extension .call_language_server_additional_workspace_configuration( store, &language_server_id, &target_language_server_id, resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(options) } .boxed() }) .await? } async fn labels_for_completions( &self, language_server_id: LanguageServerName, completions: Vec<Completion>, ) -> Result<Vec<Option<CodeLabel>>> { self.call(|extension, store| { async move { let labels = extension .call_labels_for_completions( store, &language_server_id, completions.into_iter().map(Into::into).collect(), ) .await? .map_err(|err| store.data().extension_error(err))?; Ok(labels .into_iter() .map(|label| label.map(Into::into)) .collect()) } .boxed() }) .await? } async fn labels_for_symbols( &self, language_server_id: LanguageServerName, symbols: Vec<Symbol>, ) -> Result<Vec<Option<CodeLabel>>> { self.call(|extension, store| { async move { let labels = extension .call_labels_for_symbols( store, &language_server_id, symbols.into_iter().map(Into::into).collect(), ) .await? .map_err(|err| store.data().extension_error(err))?; Ok(labels .into_iter() .map(|label| label.map(Into::into)) .collect()) } .boxed() }) .await? } async fn complete_slash_command_argument( &self, command: SlashCommand, arguments: Vec<String>, ) -> Result<Vec<SlashCommandArgumentCompletion>> { self.call(|extension, store| { async move { let completions = extension .call_complete_slash_command_argument(store, &command.into(), &arguments) .await? .map_err(|err| store.data().extension_error(err))?; Ok(completions.into_iter().map(Into::into).collect()) } .boxed() }) .await? } async fn run_slash_command( &self, command: SlashCommand, arguments: Vec<String>, delegate: Option<Arc<dyn WorktreeDelegate>>, ) -> Result<SlashCommandOutput> { self.call(|extension, store| { async move { let resource = if let Some(delegate) = delegate { Some(store.data_mut().table.push(delegate)?) } else { None }; let output = extension .call_run_slash_command(store, &command.into(), &arguments, resource) .await? .map_err(|err| store.data().extension_error(err))?; Ok(output.into()) } .boxed() }) .await? } async fn context_server_command( &self, context_server_id: Arc<str>, project: Arc<dyn ProjectDelegate>, ) -> Result<Command> { self.call(|extension, store| { async move { let project_resource = store.data_mut().table.push(project)?; let command = extension .call_context_server_command(store, context_server_id.clone(), project_resource) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(command.into()) } .boxed() }) .await? } async fn context_server_configuration( &self, context_server_id: Arc<str>, project: Arc<dyn ProjectDelegate>, ) -> Result<Option<ContextServerConfiguration>> { self.call(|extension, store| { async move { let project_resource = store.data_mut().table.push(project)?; let Some(configuration) = extension .call_context_server_configuration( store, context_server_id.clone(), project_resource, ) .await? .map_err(|err| store.data().extension_error(err))? else { return Ok(None); }; Ok(Some(configuration.try_into()?)) } .boxed() }) .await? } async fn suggest_docs_packages(&self, provider: Arc<str>) -> Result<Vec<String>> { self.call(|extension, store| { async move { let packages = extension .call_suggest_docs_packages(store, provider.as_ref()) .await? .map_err(|err| store.data().extension_error(err))?; Ok(packages) } .boxed() }) .await? } async fn index_docs( &self, provider: Arc<str>, package_name: Arc<str>, kv_store: Arc<dyn KeyValueStoreDelegate>, ) -> Result<()> { self.call(|extension, store| { async move { let kv_store_resource = store.data_mut().table.push(kv_store)?; extension .call_index_docs( store, provider.as_ref(), package_name.as_ref(), kv_store_resource, ) .await? .map_err(|err| store.data().extension_error(err))?; anyhow::Ok(()) } .boxed() }) .await? } async fn get_dap_binary( &self, dap_name: Arc<str>, config: DebugTaskDefinition, user_installed_path: Option<PathBuf>, worktree: Arc<dyn WorktreeDelegate>, ) -> Result<DebugAdapterBinary> { self.call(|extension, store| { async move { let resource = store.data_mut().table.push(worktree)?; let dap_binary = extension .call_get_dap_binary(store, dap_name, config, user_installed_path, resource) .await? .map_err(|err| store.data().extension_error(err))?; let dap_binary = dap_binary.try_into()?; Ok(dap_binary) } .boxed() }) .await? } async fn dap_request_kind( &self, dap_name: Arc<str>, config: serde_json::Value, ) -> Result<StartDebuggingRequestArgumentsRequest> { self.call(|extension, store| { async move { let kind = extension .call_dap_request_kind(store, dap_name, config) .await? .map_err(|err| store.data().extension_error(err))?; Ok(kind.into()) } .boxed() }) .await? } async fn dap_config_to_scenario(&self, config: ZedDebugConfig) -> Result<DebugScenario> { self.call(|extension, store| { async move { let kind = extension .call_dap_config_to_scenario(store, config) .await? .map_err(|err| store.data().extension_error(err))?; Ok(kind) } .boxed() }) .await? } async fn dap_locator_create_scenario( &self, locator_name: String, build_config_template: TaskTemplate, resolved_label: String, debug_adapter_name: String, ) -> Result<Option<DebugScenario>> { self.call(|extension, store| { async move { extension