Respect the language_servers setting's order when determining the primary language server (#15624)

This PR updates how we determine the "primary" language server for a
buffer to make it respect the order specified by the `language_servers`
setting.

Previously we were relying on the language servers to be registered in
the right order in order to select the primary one effectively.

However, in my testing I observed some cases where a native language
server (e.g., `tailwindcss-language-server`) could end up first in the
list of language servers despite not being first in the
`language_servers` setting.

While this wasn't a problem for the Tailwind or ESLint language servers
on account of them being defined natively with the designation of
"secondary" language servers, this could cause problems with
extension-based language servers.

To remedy this, every time we start up language servers we reorder the
list of language servers for a given language to reflect the order in
the `language_servers` setting. This ordering then allows us to treat
the first language server in the list as the "primary" one.

Related issues:

- https://github.com/zed-industries/zed/issues/15023
- https://github.com/zed-industries/zed/issues/15279

Release Notes:

- The ordering of language servers will now respect the order in the
`language_servers` setting.
- The first language server in this list will be used as the primary
language server.
This commit is contained in:
Marshall Bowers 2024-08-01 11:58:23 -04:00 committed by GitHub
parent 0b175ac66e
commit 3bd9a3f478
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 5 deletions

View file

@ -7,7 +7,7 @@ use crate::{
LanguageServerName, LspAdapter, LspAdapterDelegate, PLAIN_TEXT,
};
use anyhow::{anyhow, Context as _, Result};
use collections::{hash_map, HashMap};
use collections::{hash_map, HashMap, HashSet};
use futures::TryFutureExt;
use futures::{
channel::{mpsc, oneshot},
@ -188,6 +188,22 @@ impl LanguageRegistry {
self.state.write().reload();
}
/// Reorders the list of language servers for the given language.
///
/// Uses the provided list of ordered [`CachedLspAdapters`] as the desired order.
///
/// Any existing language servers not present in `ordered_lsp_adapters` will be
/// appended to the end.
pub fn reorder_language_servers(
&self,
language: &Arc<Language>,
ordered_lsp_adapters: Vec<Arc<CachedLspAdapter>>,
) {
self.state
.write()
.reorder_language_servers(language, ordered_lsp_adapters);
}
/// Removes the specified languages and grammars from the registry.
pub fn remove_languages(
&self,
@ -920,6 +936,36 @@ impl LanguageRegistryState {
*self.subscription.0.borrow_mut() = ();
}
/// Reorders the list of language servers for the given language.
///
/// Uses the provided list of ordered [`CachedLspAdapters`] as the desired order.
///
/// Any existing language servers not present in `ordered_lsp_adapters` will be
/// appended to the end.
fn reorder_language_servers(
&mut self,
language: &Arc<Language>,
ordered_lsp_adapters: Vec<Arc<CachedLspAdapter>>,
) {
let Some(lsp_adapters) = self.lsp_adapters.get_mut(&language.config.name) else {
return;
};
let ordered_lsp_adapter_ids = ordered_lsp_adapters
.iter()
.map(|lsp_adapter| lsp_adapter.name.clone())
.collect::<HashSet<_>>();
let mut new_lsp_adapters = ordered_lsp_adapters;
for adapter in lsp_adapters.iter() {
if !ordered_lsp_adapter_ids.contains(&adapter.name) {
new_lsp_adapters.push(adapter.clone());
}
}
*lsp_adapters = new_lsp_adapters;
}
fn remove_languages(
&mut self,
languages_to_remove: &[Arc<str>],