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
This commit is contained in:
Marshall Bowers 2024-11-13 12:24:27 -05:00 committed by GitHub
parent f96b29ca54
commit a04c2ecff7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 93 additions and 76 deletions

View file

@ -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<String>;
async fn which(&self, binary_name: String) -> Option<String>;
async fn shell_env(&self) -> Vec<(String, String)>;
}
pub trait KeyValueStoreDelegate: Send + Sync + 'static {
fn insert(&self, key: String, docs: String) -> Task<Result<()>>;
}

View file

@ -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<dyn LspAdapterDelegate>);
#[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()
}
}
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(

View file

@ -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<WasmState>,
language_server_id: &LanguageServerName,
config: &LanguageServerConfig,
resource: Resource<Arc<dyn LspAdapterDelegate>>,
resource: Resource<Arc<dyn WorktreeDelegate>>,
) -> Result<Result<Command, String>> {
match self {
Extension::V020(ext) => {
@ -183,7 +182,7 @@ impl Extension {
store: &mut Store<WasmState>,
language_server_id: &LanguageServerName,
config: &LanguageServerConfig,
resource: Resource<Arc<dyn LspAdapterDelegate>>,
resource: Resource<Arc<dyn WorktreeDelegate>>,
) -> Result<Result<Option<String>, String>> {
match self {
Extension::V020(ext) => {
@ -229,7 +228,7 @@ impl Extension {
&self,
store: &mut Store<WasmState>,
language_server_id: &LanguageServerName,
resource: Resource<Arc<dyn LspAdapterDelegate>>,
resource: Resource<Arc<dyn WorktreeDelegate>>,
) -> Result<Result<Option<String>, String>> {
match self {
Extension::V020(ext) => {
@ -366,7 +365,7 @@ impl Extension {
store: &mut Store<WasmState>,
command: &SlashCommand,
arguments: &[String],
resource: Option<Resource<Arc<dyn LspAdapterDelegate>>>,
resource: Option<Resource<Arc<dyn WorktreeDelegate>>>,
) -> Result<Result<SlashCommandOutput, String>> {
match self {
Extension::V020(ext) => {

View file

@ -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<dyn LspAdapterDelegate>;
pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
pub fn linker() -> &'static Linker<WasmState> {
static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
@ -62,7 +63,7 @@ impl From<Command> for latest::Command {
impl HostWorktree for WasmState {
async fn read_text_file(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
path: String,
) -> wasmtime::Result<Result<String, String>> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<EnvVars> {
latest::HostWorktree::shell_env(self, delegate).await
}
async fn which(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
binary_name: String,
) -> wasmtime::Result<Option<String>> {
latest::HostWorktree::which(self, delegate, binary_name).await

View file

@ -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<dyn LspAdapterDelegate>;
pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
pub fn linker() -> &'static Linker<WasmState> {
static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
@ -71,7 +71,7 @@ impl From<Command> for latest::Command {
impl HostWorktree for WasmState {
async fn read_text_file(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
path: String,
) -> wasmtime::Result<Result<String, String>> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<EnvVars> {
latest::HostWorktree::shell_env(self, delegate).await
}
async fn which(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
binary_name: String,
) -> wasmtime::Result<Option<String>> {
latest::HostWorktree::which(self, delegate, binary_name).await

View file

@ -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<dyn LspAdapterDelegate>;
pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
pub fn linker() -> &'static Linker<WasmState> {
static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
@ -113,23 +113,20 @@ impl From<CodeLabel> for latest::CodeLabel {
#[async_trait]
impl HostWorktree for WasmState {
async fn id(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
) -> wasmtime::Result<u64> {
async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
latest::HostWorktree::id(self, delegate).await
}
async fn root_path(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<String> {
latest::HostWorktree::root_path(self, delegate).await
}
async fn read_text_file(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
path: String,
) -> wasmtime::Result<Result<String, String>> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<EnvVars> {
latest::HostWorktree::shell_env(self, delegate).await
}
async fn which(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
binary_name: String,
) -> wasmtime::Result<Option<String>> {
latest::HostWorktree::which(self, delegate, binary_name).await

View file

@ -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<dyn LspAdapterDelegate>;
pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
pub type ExtensionHttpResponseStream = Arc<Mutex<::http_client::Response<AsyncBody>>>;
@ -251,52 +249,38 @@ impl HostKeyValueStore for WasmState {
#[async_trait]
impl HostWorktree for WasmState {
async fn id(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
) -> wasmtime::Result<u64> {
let delegate = self.table.get(&delegate)?;
Ok(delegate.worktree_id().to_proto())
async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
latest::HostWorktree::id(self, delegate).await
}
async fn root_path(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<String> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
path: String,
) -> wasmtime::Result<Result<String, String>> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<EnvVars> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
binary_name: String,
) -> wasmtime::Result<Option<String>> {
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<Worktree>) -> Result<()> {

View file

@ -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<dyn LspAdapterDelegate>;
pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
pub type ExtensionHttpResponseStream = Arc<Mutex<::http_client::Response<AsyncBody>>>;
@ -93,25 +90,22 @@ impl HostProject for WasmState {
#[async_trait]
impl HostWorktree for WasmState {
async fn id(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
) -> wasmtime::Result<u64> {
async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
let delegate = self.table.get(&delegate)?;
Ok(delegate.worktree_id().to_proto())
Ok(delegate.id())
}
async fn root_path(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<String> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
path: String,
) -> wasmtime::Result<Result<String, String>> {
let delegate = self.table.get(&delegate)?;
@ -123,7 +117,7 @@ impl HostWorktree for WasmState {
async fn shell_env(
&mut self,
delegate: Resource<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
) -> wasmtime::Result<EnvVars> {
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<Arc<dyn LspAdapterDelegate>>,
delegate: Resource<Arc<dyn WorktreeDelegate>>,
binary_name: String,
) -> wasmtime::Result<Option<String>> {
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<Worktree>) -> Result<()> {

View file

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