diff --git a/crates/language/src/language_registry.rs b/crates/language/src/language_registry.rs index 196df43e2c..ebdb993c25 100644 --- a/crates/language/src/language_registry.rs +++ b/crates/language/src/language_registry.rs @@ -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, + ordered_lsp_adapters: Vec>, + ) { + 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, + ordered_lsp_adapters: Vec>, + ) { + 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::>(); + + 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], diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 0ef0efb37f..0776f8ca65 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2999,9 +2999,18 @@ impl Project { .join(", ") ); - for adapter in enabled_lsp_adapters { - self.start_language_server(worktree, adapter, language.clone(), cx); + for adapter in &enabled_lsp_adapters { + self.start_language_server(worktree, adapter.clone(), language.clone(), cx); } + + // After starting all the language servers, reorder them to reflect the desired order + // based on the settings. + // + // This is done, in part, to ensure that language servers loaded at different points + // (e.g., native vs extension) still end up in the right order at the end, rather than + // it being based on which language server happened to be loaded in first. + self.languages() + .reorder_language_servers(&language, enabled_lsp_adapters); } fn start_language_server( @@ -10247,8 +10256,10 @@ impl Project { buffer: &Buffer, cx: &AppContext, ) -> Option<(&Arc, &Arc)> { - self.language_servers_for_buffer(buffer, cx) - .find(|s| s.0.is_primary) + // The list of language servers is ordered based on the `language_servers` setting + // for each language, thus we can consider the first one in the list to be the + // primary one. + self.language_servers_for_buffer(buffer, cx).next() } pub fn language_server_for_buffer(