Improve context server lifecycle management (#20622)
This optimizes and fixes bugs in our logic for maintaining a set of running context servers, based on the combination of the user's `context_servers` settings and their installed extensions. Release Notes: - N/A --------- Co-authored-by: Marshall <marshall@zed.dev> Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
parent
6e477bbf56
commit
d3d408d47d
16 changed files with 278 additions and 408 deletions
|
@ -17,7 +17,6 @@ test-support = []
|
|||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
assistant_slash_command.workspace = true
|
||||
async-trait.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
context_servers.workspace = true
|
||||
|
@ -31,6 +30,7 @@ fuzzy.workspace = true
|
|||
gpui.workspace = true
|
||||
indexed_docs.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
num-format.workspace = true
|
||||
picker.workspace = true
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use context_servers::manager::{NativeContextServer, ServerCommand, ServerConfig};
|
||||
use context_servers::protocol::InitializedContextServerProtocol;
|
||||
use context_servers::ContextServer;
|
||||
use extension_host::wasm_host::{ExtensionProject, WasmExtension, WasmHost};
|
||||
use futures::{Future, FutureExt};
|
||||
use gpui::{AsyncAppContext, Model};
|
||||
use project::Project;
|
||||
use wasmtime_wasi::WasiView as _;
|
||||
|
||||
pub struct ExtensionContextServer {
|
||||
#[allow(unused)]
|
||||
pub(crate) extension: WasmExtension,
|
||||
#[allow(unused)]
|
||||
pub(crate) host: Arc<WasmHost>,
|
||||
id: Arc<str>,
|
||||
context_server: Arc<NativeContextServer>,
|
||||
}
|
||||
|
||||
impl ExtensionContextServer {
|
||||
pub async fn new(
|
||||
extension: WasmExtension,
|
||||
host: Arc<WasmHost>,
|
||||
id: Arc<str>,
|
||||
project: Model<Project>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Self> {
|
||||
let extension_project = project.update(&mut cx, |project, cx| ExtensionProject {
|
||||
worktree_ids: project
|
||||
.visible_worktrees(cx)
|
||||
.map(|worktree| worktree.read(cx).id().to_proto())
|
||||
.collect(),
|
||||
})?;
|
||||
let command = extension
|
||||
.call({
|
||||
let id = id.clone();
|
||||
|extension, store| {
|
||||
async move {
|
||||
let project = store.data_mut().table().push(extension_project)?;
|
||||
let command = extension
|
||||
.call_context_server_command(store, id.clone(), project)
|
||||
.await?
|
||||
.map_err(|e| anyhow!("{}", e))?;
|
||||
anyhow::Ok(command)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
let config = Arc::new(ServerConfig {
|
||||
settings: None,
|
||||
command: Some(ServerCommand {
|
||||
path: command.command,
|
||||
args: command.args,
|
||||
env: Some(command.env.into_iter().collect()),
|
||||
}),
|
||||
});
|
||||
|
||||
anyhow::Ok(Self {
|
||||
extension,
|
||||
host,
|
||||
id: id.clone(),
|
||||
context_server: Arc::new(NativeContextServer::new(id, config)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl ContextServer for ExtensionContextServer {
|
||||
fn id(&self) -> Arc<str> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn config(&self) -> Arc<ServerConfig> {
|
||||
self.context_server.config()
|
||||
}
|
||||
|
||||
fn client(&self) -> Option<Arc<InitializedContextServerProtocol>> {
|
||||
self.context_server.client()
|
||||
}
|
||||
|
||||
fn start<'a>(
|
||||
self: Arc<Self>,
|
||||
cx: &'a AsyncAppContext,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<()>>>> {
|
||||
self.context_server.clone().start(cx)
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<()> {
|
||||
self.context_server.stop()
|
||||
}
|
||||
}
|
|
@ -1,19 +1,21 @@
|
|||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use assistant_slash_command::{ExtensionSlashCommand, SlashCommandRegistry};
|
||||
use context_servers::manager::ServerCommand;
|
||||
use context_servers::ContextServerFactoryRegistry;
|
||||
use db::smol::future::FutureExt as _;
|
||||
use extension::Extension;
|
||||
use extension_host::wasm_host::ExtensionProject;
|
||||
use extension_host::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host};
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, BackgroundExecutor, Task};
|
||||
use gpui::{AppContext, BackgroundExecutor, Model, Task};
|
||||
use indexed_docs::{ExtensionIndexedDocsProvider, IndexedDocsRegistry, ProviderId};
|
||||
use language::{LanguageRegistry, LanguageServerBinaryStatus, LoadedLanguage};
|
||||
use snippet_provider::SnippetRegistry;
|
||||
use theme::{ThemeRegistry, ThemeSettings};
|
||||
use ui::SharedString;
|
||||
|
||||
use crate::extension_context_server::ExtensionContextServer;
|
||||
use wasmtime_wasi::WasiView as _;
|
||||
|
||||
pub struct ConcreteExtensionRegistrationHooks {
|
||||
slash_command_registry: Arc<SlashCommandRegistry>,
|
||||
|
@ -21,7 +23,7 @@ pub struct ConcreteExtensionRegistrationHooks {
|
|||
indexed_docs_registry: Arc<IndexedDocsRegistry>,
|
||||
snippet_registry: Arc<SnippetRegistry>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
context_server_factory_registry: Arc<ContextServerFactoryRegistry>,
|
||||
context_server_factory_registry: Model<ContextServerFactoryRegistry>,
|
||||
executor: BackgroundExecutor,
|
||||
}
|
||||
|
||||
|
@ -32,7 +34,7 @@ impl ConcreteExtensionRegistrationHooks {
|
|||
indexed_docs_registry: Arc<IndexedDocsRegistry>,
|
||||
snippet_registry: Arc<SnippetRegistry>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
context_server_factory_registry: Arc<ContextServerFactoryRegistry>,
|
||||
context_server_factory_registry: Model<ContextServerFactoryRegistry>,
|
||||
cx: &AppContext,
|
||||
) -> Arc<dyn extension_host::ExtensionRegistrationHooks> {
|
||||
Arc::new(Self {
|
||||
|
@ -71,25 +73,66 @@ impl extension_host::ExtensionRegistrationHooks for ConcreteExtensionRegistratio
|
|||
&self,
|
||||
id: Arc<str>,
|
||||
extension: wasm_host::WasmExtension,
|
||||
host: Arc<wasm_host::WasmHost>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
self.context_server_factory_registry
|
||||
.register_server_factory(
|
||||
id.clone(),
|
||||
Arc::new({
|
||||
move |project, cx| {
|
||||
let id = id.clone();
|
||||
let extension = extension.clone();
|
||||
let host = host.clone();
|
||||
cx.spawn(|cx| async move {
|
||||
let context_server =
|
||||
ExtensionContextServer::new(extension, host, id, project, cx)
|
||||
.update(cx, |registry, _| {
|
||||
registry.register_server_factory(
|
||||
id.clone(),
|
||||
Arc::new({
|
||||
move |project, cx| {
|
||||
log::info!(
|
||||
"loading command for context server {id} from extension {}",
|
||||
extension.manifest.id
|
||||
);
|
||||
|
||||
let id = id.clone();
|
||||
let extension = extension.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let extension_project =
|
||||
project.update(&mut cx, |project, cx| ExtensionProject {
|
||||
worktree_ids: project
|
||||
.visible_worktrees(cx)
|
||||
.map(|worktree| worktree.read(cx).id().to_proto())
|
||||
.collect(),
|
||||
})?;
|
||||
|
||||
let command = extension
|
||||
.call({
|
||||
let id = id.clone();
|
||||
|extension, store| {
|
||||
async move {
|
||||
let project = store
|
||||
.data_mut()
|
||||
.table()
|
||||
.push(extension_project)?;
|
||||
let command = extension
|
||||
.call_context_server_command(
|
||||
store,
|
||||
id.clone(),
|
||||
project,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| anyhow!("{}", e))?;
|
||||
anyhow::Ok(command)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
anyhow::Ok(Arc::new(context_server) as _)
|
||||
})
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
log::info!("loaded command for context server {id}: {command:?}");
|
||||
|
||||
Ok(ServerCommand {
|
||||
path: command.command,
|
||||
args: command.args,
|
||||
env: Some(command.env.into_iter().collect()),
|
||||
})
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn register_docs_provider(&self, extension: Arc<dyn Extension>, provider_id: Arc<str>) {
|
||||
|
|
|
@ -268,7 +268,7 @@ async fn test_extension_store(cx: &mut TestAppContext) {
|
|||
let slash_command_registry = SlashCommandRegistry::new();
|
||||
let indexed_docs_registry = Arc::new(IndexedDocsRegistry::new(cx.executor()));
|
||||
let snippet_registry = Arc::new(SnippetRegistry::new());
|
||||
let context_server_factory_registry = ContextServerFactoryRegistry::new();
|
||||
let context_server_factory_registry = cx.new_model(|_| ContextServerFactoryRegistry::new());
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
|
||||
let store = cx.new_model(|cx| {
|
||||
|
@ -508,7 +508,7 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) {
|
|||
let slash_command_registry = SlashCommandRegistry::new();
|
||||
let indexed_docs_registry = Arc::new(IndexedDocsRegistry::new(cx.executor()));
|
||||
let snippet_registry = Arc::new(SnippetRegistry::new());
|
||||
let context_server_factory_registry = ContextServerFactoryRegistry::new();
|
||||
let context_server_factory_registry = cx.new_model(|_| ContextServerFactoryRegistry::new());
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
|
||||
let mut status_updates = language_registry.language_server_binary_statuses();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
mod components;
|
||||
mod extension_context_server;
|
||||
mod extension_registration_hooks;
|
||||
mod extension_suggest;
|
||||
mod extension_version_selector;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue