extension: Add support for additional_workspace_configuration and additional_initialization_options (#27407)

Closes #22410

With this PR extensions can provide additional workspace configuration
for other LSP Adapters. This allows extensions like Astro, Svelte, Vue,
etc to provide plugins for vtsls typescript server, fixing issues like:
https://github.com/zed-industries/zed/issues/4577,
https://github.com/zed-industries/zed/issues/21697,
https://github.com/zed-industries/zed/issues/26901#issuecomment-2737485096

Todo:

- [x] Test case when extension is installed, does vtsls workspace config
refreshes?

Before:
<img width="450" alt="image"
src="https://github.com/user-attachments/assets/f242167c-5264-44ab-b5a7-8c90eb75c6a1"
/>

After:
<img width="450" alt="image"
src="https://github.com/user-attachments/assets/6a5f1afe-a0e1-4f64-8a95-919b0bf97614"
/>

Release Notes:

- N/A
This commit is contained in:
Smit Barmase 2025-03-25 18:23:59 +05:30 committed by GitHub
parent 9468b9699e
commit 8f1023360d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 360 additions and 19 deletions

View file

@ -251,17 +251,21 @@ impl LocalLspStore {
let toolchains = this.update(cx, |this, cx| this.toolchain_store(cx))?;
let language_server = pending_server.await?;
let workspace_config = adapter
.adapter
.clone()
.workspace_configuration(fs.as_ref(), &delegate, toolchains.clone(), cx)
.await?;
let workspace_config = Self::workspace_configuration_for_adapter(
adapter.adapter.clone(),
fs.as_ref(),
&delegate,
toolchains.clone(),
cx,
)
.await?;
let mut initialization_options = adapter
.adapter
.clone()
.initialization_options(fs.as_ref(), &(delegate))
.await?;
let mut initialization_options = Self::initialization_options_for_adapter(
adapter.adapter.clone(),
fs.as_ref(),
&delegate,
)
.await?;
match (&mut initialization_options, override_options) {
(Some(initialization_options), Some(override_options)) => {
@ -478,9 +482,16 @@ impl LocalLspStore {
async move {
let toolchains =
this.update(&mut cx, |this, cx| this.toolchain_store(cx))?;
let workspace_config = adapter
.workspace_configuration(fs.as_ref(), &delegate, toolchains, &mut cx)
.await?;
let workspace_config = Self::workspace_configuration_for_adapter(
adapter.clone(),
fs.as_ref(),
&delegate,
toolchains.clone(),
&mut cx,
)
.await?;
Ok(params
.items
.into_iter()
@ -3225,6 +3236,67 @@ impl LocalLspStore {
self.rebuild_watched_paths(language_server_id, cx);
}
async fn initialization_options_for_adapter(
adapter: Arc<dyn LspAdapter>,
fs: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>,
) -> Result<Option<serde_json::Value>> {
let Some(mut initialization_config) =
adapter.clone().initialization_options(fs, delegate).await?
else {
return Ok(None);
};
for other_adapter in delegate.registered_lsp_adapters() {
if other_adapter.name() == adapter.name() {
continue;
}
if let Ok(Some(target_config)) = other_adapter
.clone()
.additional_initialization_options(adapter.name(), fs, delegate)
.await
{
merge_json_value_into(target_config.clone(), &mut initialization_config);
}
}
Ok(Some(initialization_config))
}
async fn workspace_configuration_for_adapter(
adapter: Arc<dyn LspAdapter>,
fs: &dyn Fs,
delegate: &Arc<dyn LspAdapterDelegate>,
toolchains: Arc<dyn LanguageToolchainStore>,
cx: &mut AsyncApp,
) -> Result<serde_json::Value> {
let mut workspace_config = adapter
.clone()
.workspace_configuration(fs, delegate, toolchains.clone(), cx)
.await?;
for other_adapter in delegate.registered_lsp_adapters() {
if other_adapter.name() == adapter.name() {
continue;
}
if let Ok(Some(target_config)) = other_adapter
.clone()
.additional_workspace_configuration(
adapter.name(),
fs,
delegate,
toolchains.clone(),
cx,
)
.await
{
merge_json_value_into(target_config.clone(), &mut workspace_config);
}
}
Ok(workspace_config)
}
}
#[derive(Debug)]
@ -3764,8 +3836,8 @@ impl LspStore {
for (adapter, server, delegate) in servers {
adapter.clear_zed_json_schema_cache().await;
let Some(json_workspace_config) = adapter
.workspace_configuration(
let Some(json_workspace_config) = LocalLspStore::workspace_configuration_for_adapter(
adapter,
fs.as_ref(),
&delegate,
toolchain_store.clone(),
@ -6065,10 +6137,15 @@ impl LspStore {
let toolchain_store = this.update(cx, |this, cx| this.toolchain_store(cx)).ok()?;
for (adapter, server, delegate) in servers {
let settings = adapter
.workspace_configuration(fs.as_ref(), &delegate, toolchain_store.clone(), cx)
.await
.ok()?;
let settings = LocalLspStore::workspace_configuration_for_adapter(
adapter,
fs.as_ref(),
&delegate,
toolchain_store.clone(),
cx,
)
.await
.ok()?;
server
.notify::<lsp::notification::DidChangeConfiguration>(
@ -9811,6 +9888,14 @@ impl LspAdapterDelegate for LocalLspAdapterDelegate {
.update_lsp_status(server_name, status);
}
fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>> {
self.language_registry
.all_lsp_adapters()
.into_iter()
.map(|adapter| adapter.adapter.clone() as Arc<dyn LspAdapter>)
.collect()
}
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>> {
let dir = self.language_registry.language_server_download_dir(name)?;