context servers: Show configuration modal when extension is installed (#29309)

WIP

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
This commit is contained in:
Bennet Bo Fenner 2025-05-01 20:02:14 +02:00 committed by GitHub
parent bffa53d706
commit 24eb039752
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 1866 additions and 437 deletions

View file

@ -431,6 +431,13 @@ impl ExtensionStore {
.filter_map(|extension| extension.dev.then_some(&extension.manifest))
}
pub fn extension_manifest_for_id(&self, extension_id: &str) -> Option<&Arc<ExtensionManifest>> {
self.extension_index
.extensions
.get(extension_id)
.map(|extension| &extension.manifest)
}
/// Returns the names of themes provided by extensions.
pub fn extension_themes<'a>(
&'a self,
@ -744,8 +751,18 @@ impl ExtensionStore {
.await;
if let ExtensionOperation::Install = operation {
this.update( cx, |_, cx| {
cx.emit(Event::ExtensionInstalled(extension_id));
this.update( cx, |this, cx| {
cx.emit(Event::ExtensionInstalled(extension_id.clone()));
if let Some(events) = ExtensionEvents::try_global(cx) {
if let Some(manifest) = this.extension_manifest_for_id(&extension_id) {
events.update(cx, |this, cx| {
this.emit(
extension::Event::ExtensionInstalled(manifest.clone()),
cx,
)
});
}
}
})
.ok();
}
@ -935,6 +952,17 @@ impl ExtensionStore {
.await?;
this.update(cx, |this, cx| this.reload(None, cx))?.await;
this.update(cx, |this, cx| {
cx.emit(Event::ExtensionInstalled(extension_id.clone()));
if let Some(events) = ExtensionEvents::try_global(cx) {
if let Some(manifest) = this.extension_manifest_for_id(&extension_id) {
events.update(cx, |this, cx| {
this.emit(extension::Event::ExtensionInstalled(manifest.clone()), cx)
});
}
}
})?;
Ok(())
})
}

View file

@ -4,8 +4,9 @@ use crate::ExtensionManifest;
use anyhow::{Context as _, Result, anyhow, bail};
use async_trait::async_trait;
use extension::{
CodeLabel, Command, Completion, ExtensionHostProxy, KeyValueStoreDelegate, ProjectDelegate,
SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, Symbol, WorktreeDelegate,
CodeLabel, Command, Completion, ContextServerConfiguration, ExtensionHostProxy,
KeyValueStoreDelegate, ProjectDelegate, SlashCommand, SlashCommandArgumentCompletion,
SlashCommandOutput, Symbol, WorktreeDelegate,
};
use fs::{Fs, normalize_path};
use futures::future::LocalBoxFuture;
@ -306,6 +307,33 @@ impl extension::Extension for WasmExtension {
.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| anyhow!("{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 {

View file

@ -25,6 +25,7 @@ use wasmtime::{
pub use latest::CodeLabelSpanLiteral;
pub use latest::{
CodeLabel, CodeLabelSpan, Command, ExtensionProject, Range, SlashCommand,
zed::extension::context_server::ContextServerConfiguration,
zed::extension::lsp::{
Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
},
@ -726,6 +727,29 @@ impl Extension {
}
}
pub async fn call_context_server_configuration(
&self,
store: &mut Store<WasmState>,
context_server_id: Arc<str>,
project: Resource<ExtensionProject>,
) -> Result<Result<Option<ContextServerConfiguration>, String>> {
match self {
Extension::V0_5_0(ext) => {
ext.call_context_server_configuration(store, &context_server_id, project)
.await
}
Extension::V0_0_1(_)
| Extension::V0_0_4(_)
| Extension::V0_0_6(_)
| Extension::V0_1_0(_)
| Extension::V0_2_0(_)
| Extension::V0_3_0(_)
| Extension::V0_4_0(_) => Err(anyhow!(
"`context_server_configuration` not available prior to v0.5.0"
)),
}
}
pub async fn call_suggest_docs_packages(
&self,
store: &mut Store<WasmState>,

View file

@ -247,6 +247,21 @@ impl From<SlashCommandArgumentCompletion> for extension::SlashCommandArgumentCom
}
}
impl TryFrom<ContextServerConfiguration> for extension::ContextServerConfiguration {
type Error = anyhow::Error;
fn try_from(value: ContextServerConfiguration) -> Result<Self, Self::Error> {
let settings_schema: serde_json::Value = serde_json::from_str(&value.settings_schema)
.context("Failed to parse settings_schema")?;
Ok(Self {
installation_instructions: value.installation_instructions,
default_settings: value.default_settings,
settings_schema,
})
}
}
impl HostKeyValueStore for WasmState {
async fn insert(
&mut self,
@ -610,6 +625,9 @@ impl process::Host for WasmState {
#[async_trait]
impl slash_command::Host for WasmState {}
#[async_trait]
impl context_server::Host for WasmState {}
impl ExtensionImports for WasmState {
async fn get_settings(
&mut self,