From baf0da2701e2a86eeeb16aa427948581b6ae9abe Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 13 Nov 2024 12:24:27 -0500 Subject: [PATCH] Decouple extension `Worktree` resource from `LspAdapterDelegate` (#20611) This PR decouples the extension `Worktree` resource from the `LspAdapterDelegate`. We now have a `WorktreeDelegate` trait that corresponds to the methods on the resource. We then create a `WorktreeDelegateAdapter` that can wrap an `LspAdapterDelegate` and implement the `WorktreeDelegate` trait. Release Notes: - N/A --- crates/extension/src/extension.rs | 11 ++++- .../src/extension_lsp_adapter.rs | 33 +++++++++++++++ crates/extension_host/src/wasm_host/wit.rs | 11 +++-- .../src/wasm_host/wit/since_v0_0_1.rs | 11 ++--- .../src/wasm_host/wit/since_v0_0_4.rs | 10 ++--- .../src/wasm_host/wit/since_v0_0_6.rs | 17 ++++---- .../src/wasm_host/wit/since_v0_1_0.rs | 42 ++++++------------- .../src/wasm_host/wit/since_v0_2_0.rs | 31 +++++--------- .../src/extension_slash_command.rs | 3 ++ 9 files changed, 93 insertions(+), 76 deletions(-) diff --git a/crates/extension/src/extension.rs b/crates/extension/src/extension.rs index 098b7be2257b6548db0f6eeebf2ff9466fac6461..19267e16ac70825233694cd02b92f5e080c244ec 100644 --- a/crates/extension/src/extension.rs +++ b/crates/extension/src/extension.rs @@ -1,7 +1,7 @@ pub mod extension_builder; mod extension_manifest; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; use anyhow::{anyhow, bail, Context as _, Result}; @@ -11,6 +11,15 @@ use semantic_version::SemanticVersion; pub use crate::extension_manifest::*; +#[async_trait] +pub trait WorktreeDelegate: Send + Sync + 'static { + fn id(&self) -> u64; + fn root_path(&self) -> String; + async fn read_text_file(&self, path: PathBuf) -> Result; + async fn which(&self, binary_name: String) -> Option; + async fn shell_env(&self) -> Vec<(String, String)>; +} + pub trait KeyValueStoreDelegate: Send + Sync + 'static { fn insert(&self, key: String, docs: String) -> Task>; } diff --git a/crates/extension_host/src/extension_lsp_adapter.rs b/crates/extension_host/src/extension_lsp_adapter.rs index b15fa5adc404e4127235080b94147ac0c7ba7455..8f0d0f82e3ee4be5a8c9d5d7cef761a2ca3e43ec 100644 --- a/crates/extension_host/src/extension_lsp_adapter.rs +++ b/crates/extension_host/src/extension_lsp_adapter.rs @@ -5,6 +5,7 @@ use crate::wasm_host::{ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use collections::HashMap; +use extension::WorktreeDelegate; use futures::{Future, FutureExt}; use gpui::AsyncAppContext; use language::{ @@ -18,6 +19,35 @@ use std::{any::Any, path::PathBuf, pin::Pin, sync::Arc}; use util::{maybe, ResultExt}; use wasmtime_wasi::WasiView as _; +/// An adapter that allows an [`LspAdapterDelegate`] to be used as a [`WorktreeDelegate`]. +pub struct WorktreeDelegateAdapter(pub Arc); + +#[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 { + self.0.read_text_file(path).await + } + + async fn which(&self, binary_name: String) -> Option { + 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() + } +} + pub struct ExtensionLspAdapter { pub(crate) extension: WasmExtension, pub(crate) language_server_id: LanguageServerName, @@ -45,6 +75,7 @@ impl LspAdapter for ExtensionLspAdapter { let this = self.clone(); |extension, store| { async move { + let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let resource = store.data_mut().table().push(delegate)?; let command = extension .call_language_server_command( @@ -166,6 +197,7 @@ impl LspAdapter for ExtensionLspAdapter { let this = self.clone(); |extension, store| { async move { + let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let resource = store.data_mut().table().push(delegate)?; let options = extension .call_language_server_initialization_options( @@ -204,6 +236,7 @@ impl LspAdapter for ExtensionLspAdapter { let this = self.clone(); |extension, store| { async move { + let delegate = Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; let resource = store.data_mut().table().push(delegate)?; let options = extension .call_language_server_workspace_configuration( diff --git a/crates/extension_host/src/wasm_host/wit.rs b/crates/extension_host/src/wasm_host/wit.rs index bec26cb41d292a3b90244dbde29fb2418b26752e..77e28b824fbe8fc29d2d3f85576b2d0badd5b7b8 100644 --- a/crates/extension_host/src/wasm_host/wit.rs +++ b/crates/extension_host/src/wasm_host/wit.rs @@ -3,14 +3,13 @@ mod since_v0_0_4; mod since_v0_0_6; mod since_v0_1_0; mod since_v0_2_0; -use extension::KeyValueStoreDelegate; +use extension::{KeyValueStoreDelegate, WorktreeDelegate}; use lsp::LanguageServerName; use release_channel::ReleaseChannel; use since_v0_2_0 as latest; use super::{wasm_engine, WasmState}; use anyhow::{anyhow, Context, Result}; -use language::LspAdapterDelegate; use semantic_version::SemanticVersion; use std::{ops::RangeInclusive, sync::Arc}; use wasmtime::{ @@ -152,7 +151,7 @@ impl Extension { store: &mut Store, language_server_id: &LanguageServerName, config: &LanguageServerConfig, - resource: Resource>, + resource: Resource>, ) -> Result> { match self { Extension::V020(ext) => { @@ -183,7 +182,7 @@ impl Extension { store: &mut Store, language_server_id: &LanguageServerName, config: &LanguageServerConfig, - resource: Resource>, + resource: Resource>, ) -> Result, String>> { match self { Extension::V020(ext) => { @@ -229,7 +228,7 @@ impl Extension { &self, store: &mut Store, language_server_id: &LanguageServerName, - resource: Resource>, + resource: Resource>, ) -> Result, String>> { match self { Extension::V020(ext) => { @@ -366,7 +365,7 @@ impl Extension { store: &mut Store, command: &SlashCommand, arguments: &[String], - resource: Option>>, + resource: Option>>, ) -> Result> { match self { Extension::V020(ext) => { diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_0_1.rs b/crates/extension_host/src/wasm_host/wit/since_v0_0_1.rs index 3493d2a74dc3528b07f5ec50e9b0a51bde900f5b..bd1770de38a3ee25171373f6c929dced7b3182ce 100644 --- a/crates/extension_host/src/wasm_host/wit/since_v0_0_1.rs +++ b/crates/extension_host/src/wasm_host/wit/since_v0_0_1.rs @@ -3,7 +3,8 @@ use crate::wasm_host::wit::since_v0_0_4; use crate::wasm_host::WasmState; use anyhow::Result; use async_trait::async_trait; -use language::{LanguageServerBinaryStatus, LspAdapterDelegate}; +use extension::WorktreeDelegate; +use language::LanguageServerBinaryStatus; use semantic_version::SemanticVersion; use std::sync::{Arc, OnceLock}; use wasmtime::component::{Linker, Resource}; @@ -21,7 +22,7 @@ wasmtime::component::bindgen!({ }, }); -pub type ExtensionWorktree = Arc; +pub type ExtensionWorktree = Arc; pub fn linker() -> &'static Linker { static LINKER: OnceLock> = OnceLock::new(); @@ -62,7 +63,7 @@ impl From for latest::Command { impl HostWorktree for WasmState { async fn read_text_file( &mut self, - delegate: Resource>, + delegate: Resource>, path: String, ) -> wasmtime::Result> { latest::HostWorktree::read_text_file(self, delegate, path).await @@ -70,14 +71,14 @@ impl HostWorktree for WasmState { async fn shell_env( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { latest::HostWorktree::shell_env(self, delegate).await } async fn which( &mut self, - delegate: Resource>, + delegate: Resource>, binary_name: String, ) -> wasmtime::Result> { latest::HostWorktree::which(self, delegate, binary_name).await diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_0_4.rs b/crates/extension_host/src/wasm_host/wit/since_v0_0_4.rs index eb52d64bf0b2fd74fba6ec987163379c12833982..b88a444f01c379d288ef0fda3c7441a343b278f1 100644 --- a/crates/extension_host/src/wasm_host/wit/since_v0_0_4.rs +++ b/crates/extension_host/src/wasm_host/wit/since_v0_0_4.rs @@ -2,7 +2,7 @@ use super::latest; use crate::wasm_host::WasmState; use anyhow::Result; use async_trait::async_trait; -use language::LspAdapterDelegate; +use extension::WorktreeDelegate; use semantic_version::SemanticVersion; use std::sync::{Arc, OnceLock}; use wasmtime::component::{Linker, Resource}; @@ -20,7 +20,7 @@ wasmtime::component::bindgen!({ }, }); -pub type ExtensionWorktree = Arc; +pub type ExtensionWorktree = Arc; pub fn linker() -> &'static Linker { static LINKER: OnceLock> = OnceLock::new(); @@ -71,7 +71,7 @@ impl From for latest::Command { impl HostWorktree for WasmState { async fn read_text_file( &mut self, - delegate: Resource>, + delegate: Resource>, path: String, ) -> wasmtime::Result> { latest::HostWorktree::read_text_file(self, delegate, path).await @@ -79,14 +79,14 @@ impl HostWorktree for WasmState { async fn shell_env( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { latest::HostWorktree::shell_env(self, delegate).await } async fn which( &mut self, - delegate: Resource>, + delegate: Resource>, binary_name: String, ) -> wasmtime::Result> { latest::HostWorktree::which(self, delegate, binary_name).await diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_0_6.rs b/crates/extension_host/src/wasm_host/wit/since_v0_0_6.rs index 18c74258e7b4ff16ad12b9fc72dc0f25fdb75a0d..68681936a472014682a1d1d1ebda6c4411beb897 100644 --- a/crates/extension_host/src/wasm_host/wit/since_v0_0_6.rs +++ b/crates/extension_host/src/wasm_host/wit/since_v0_0_6.rs @@ -2,7 +2,7 @@ use super::{latest, since_v0_1_0}; use crate::wasm_host::WasmState; use anyhow::Result; use async_trait::async_trait; -use language::LspAdapterDelegate; +use extension::WorktreeDelegate; use semantic_version::SemanticVersion; use std::sync::{Arc, OnceLock}; use wasmtime::component::{Linker, Resource}; @@ -26,7 +26,7 @@ mod settings { include!(concat!(env!("OUT_DIR"), "/since_v0.0.6/settings.rs")); } -pub type ExtensionWorktree = Arc; +pub type ExtensionWorktree = Arc; pub fn linker() -> &'static Linker { static LINKER: OnceLock> = OnceLock::new(); @@ -113,23 +113,20 @@ impl From for latest::CodeLabel { #[async_trait] impl HostWorktree for WasmState { - async fn id( - &mut self, - delegate: Resource>, - ) -> wasmtime::Result { + async fn id(&mut self, delegate: Resource>) -> wasmtime::Result { latest::HostWorktree::id(self, delegate).await } async fn root_path( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { latest::HostWorktree::root_path(self, delegate).await } async fn read_text_file( &mut self, - delegate: Resource>, + delegate: Resource>, path: String, ) -> wasmtime::Result> { latest::HostWorktree::read_text_file(self, delegate, path).await @@ -137,14 +134,14 @@ impl HostWorktree for WasmState { async fn shell_env( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { latest::HostWorktree::shell_env(self, delegate).await } async fn which( &mut self, - delegate: Resource>, + delegate: Resource>, binary_name: String, ) -> wasmtime::Result> { latest::HostWorktree::which(self, delegate, binary_name).await diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs b/crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs index f707133d17e21bf8120250f160a09ff585e7e8d0..ff48a2fdaac37a66a0e36aaacec361fbfe152b4e 100644 --- a/crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs +++ b/crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs @@ -5,13 +5,11 @@ use anyhow::{anyhow, bail, Context, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; use async_trait::async_trait; -use extension::KeyValueStoreDelegate; +use extension::{KeyValueStoreDelegate, WorktreeDelegate}; use futures::{io::BufReader, FutureExt as _}; use futures::{lock::Mutex, AsyncReadExt}; use language::LanguageName; -use language::{ - language_settings::AllLanguageSettings, LanguageServerBinaryStatus, LspAdapterDelegate, -}; +use language::{language_settings::AllLanguageSettings, LanguageServerBinaryStatus}; use project::project_settings::ProjectSettings; use semantic_version::SemanticVersion; use std::{ @@ -47,7 +45,7 @@ mod settings { include!(concat!(env!("OUT_DIR"), "/since_v0.1.0/settings.rs")); } -pub type ExtensionWorktree = Arc; +pub type ExtensionWorktree = Arc; pub type ExtensionKeyValueStore = Arc; pub type ExtensionHttpResponseStream = Arc>>; @@ -251,52 +249,38 @@ impl HostKeyValueStore for WasmState { #[async_trait] impl HostWorktree for WasmState { - async fn id( - &mut self, - delegate: Resource>, - ) -> wasmtime::Result { - let delegate = self.table.get(&delegate)?; - Ok(delegate.worktree_id().to_proto()) + async fn id(&mut self, delegate: Resource>) -> wasmtime::Result { + latest::HostWorktree::id(self, delegate).await } async fn root_path( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { - let delegate = self.table.get(&delegate)?; - Ok(delegate.worktree_root_path().to_string_lossy().to_string()) + latest::HostWorktree::root_path(self, delegate).await } async fn read_text_file( &mut self, - delegate: Resource>, + delegate: Resource>, path: String, ) -> wasmtime::Result> { - let delegate = self.table.get(&delegate)?; - Ok(delegate - .read_text_file(path.into()) - .await - .map_err(|error| error.to_string())) + latest::HostWorktree::read_text_file(self, delegate, path).await } async fn shell_env( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { - let delegate = self.table.get(&delegate)?; - Ok(delegate.shell_env().await.into_iter().collect()) + latest::HostWorktree::shell_env(self, delegate).await } async fn which( &mut self, - delegate: Resource>, + delegate: Resource>, binary_name: String, ) -> wasmtime::Result> { - let delegate = self.table.get(&delegate)?; - Ok(delegate - .which(binary_name.as_ref()) - .await - .map(|path| path.to_string_lossy().to_string())) + latest::HostWorktree::which(self, delegate, binary_name).await } fn drop(&mut self, _worktree: Resource) -> Result<()> { diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs b/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs index 752392f2f288cf180de0f17bbdb52de9adf5581f..09df9aa8b69105518e64772e3f730cf3d076b52a 100644 --- a/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs +++ b/crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs @@ -6,13 +6,10 @@ use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; use async_trait::async_trait; use context_servers::manager::ContextServerSettings; -use extension::KeyValueStoreDelegate; +use extension::{KeyValueStoreDelegate, WorktreeDelegate}; use futures::{io::BufReader, FutureExt as _}; use futures::{lock::Mutex, AsyncReadExt}; -use language::{ - language_settings::AllLanguageSettings, LanguageName, LanguageServerBinaryStatus, - LspAdapterDelegate, -}; +use language::{language_settings::AllLanguageSettings, LanguageName, LanguageServerBinaryStatus}; use project::project_settings::ProjectSettings; use semantic_version::SemanticVersion; use std::{ @@ -44,7 +41,7 @@ mod settings { include!(concat!(env!("OUT_DIR"), "/since_v0.2.0/settings.rs")); } -pub type ExtensionWorktree = Arc; +pub type ExtensionWorktree = Arc; pub type ExtensionKeyValueStore = Arc; pub type ExtensionHttpResponseStream = Arc>>; @@ -93,25 +90,22 @@ impl HostProject for WasmState { #[async_trait] impl HostWorktree for WasmState { - async fn id( - &mut self, - delegate: Resource>, - ) -> wasmtime::Result { + async fn id(&mut self, delegate: Resource>) -> wasmtime::Result { let delegate = self.table.get(&delegate)?; - Ok(delegate.worktree_id().to_proto()) + Ok(delegate.id()) } async fn root_path( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { let delegate = self.table.get(&delegate)?; - Ok(delegate.worktree_root_path().to_string_lossy().to_string()) + Ok(delegate.root_path()) } async fn read_text_file( &mut self, - delegate: Resource>, + delegate: Resource>, path: String, ) -> wasmtime::Result> { let delegate = self.table.get(&delegate)?; @@ -123,7 +117,7 @@ impl HostWorktree for WasmState { async fn shell_env( &mut self, - delegate: Resource>, + delegate: Resource>, ) -> wasmtime::Result { let delegate = self.table.get(&delegate)?; Ok(delegate.shell_env().await.into_iter().collect()) @@ -131,14 +125,11 @@ impl HostWorktree for WasmState { async fn which( &mut self, - delegate: Resource>, + delegate: Resource>, binary_name: String, ) -> wasmtime::Result> { let delegate = self.table.get(&delegate)?; - Ok(delegate - .which(binary_name.as_ref()) - .await - .map(|path| path.to_string_lossy().to_string())) + Ok(delegate.which(binary_name).await) } fn drop(&mut self, _worktree: Resource) -> Result<()> { diff --git a/crates/extensions_ui/src/extension_slash_command.rs b/crates/extensions_ui/src/extension_slash_command.rs index 28e13163d42ba71315b75d60794ab6ec2692613a..92c6b148c8637a51484512f7f5df44602cb0dad4 100644 --- a/crates/extensions_ui/src/extension_slash_command.rs +++ b/crates/extensions_ui/src/extension_slash_command.rs @@ -5,6 +5,7 @@ use assistant_slash_command::{ ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection, SlashCommandResult, }; +use extension_host::extension_lsp_adapter::WorktreeDelegateAdapter; use futures::FutureExt as _; use gpui::{Task, WeakView, WindowContext}; use language::{BufferSnapshot, LspAdapterDelegate}; @@ -97,6 +98,8 @@ impl SlashCommand for ExtensionSlashCommand { move |extension, store| { async move { let resource = if let Some(delegate) = delegate { + let delegate = + Arc::new(WorktreeDelegateAdapter(delegate.clone())) as _; Some(store.data_mut().table().push(delegate)?) } else { None