Refactor LSP restart logic (#2705)

Instead of storing `initialization_options` in every LSP adapter as
before, store previous LSP settings in `Project` entirely.

This way, we can later have use multiple different project
configurations per single LSP with its associated adapter.

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2023-07-11 22:09:40 +03:00 committed by GitHub
commit 5483bd1404
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 37 additions and 39 deletions

View file

@ -369,6 +369,7 @@ mod tests {
editor::init(cx); editor::init(cx);
workspace::init(app_state.clone(), cx); workspace::init(app_state.clone(), cx);
init(cx); init(cx);
Project::init_settings(cx);
app_state app_state
}) })
} }

View file

@ -90,8 +90,7 @@ pub struct LanguageServerName(pub Arc<str>);
/// once at startup, and caches the results. /// once at startup, and caches the results.
pub struct CachedLspAdapter { pub struct CachedLspAdapter {
pub name: LanguageServerName, pub name: LanguageServerName,
initialization_options: Option<Value>, pub initialization_options: Option<Value>,
initialization_overrides: Mutex<Option<Value>>,
pub disk_based_diagnostic_sources: Vec<String>, pub disk_based_diagnostic_sources: Vec<String>,
pub disk_based_diagnostics_progress_token: Option<String>, pub disk_based_diagnostics_progress_token: Option<String>,
pub language_ids: HashMap<String, String>, pub language_ids: HashMap<String, String>,
@ -110,7 +109,6 @@ impl CachedLspAdapter {
Arc::new(CachedLspAdapter { Arc::new(CachedLspAdapter {
name, name,
initialization_options, initialization_options,
initialization_overrides: Mutex::new(None),
disk_based_diagnostic_sources, disk_based_diagnostic_sources,
disk_based_diagnostics_progress_token, disk_based_diagnostics_progress_token,
language_ids, language_ids,
@ -210,30 +208,6 @@ impl CachedLspAdapter {
) -> Option<CodeLabel> { ) -> Option<CodeLabel> {
self.adapter.label_for_symbol(name, kind, language).await self.adapter.label_for_symbol(name, kind, language).await
} }
pub fn update_initialization_overrides(&self, new: Option<&Value>) -> bool {
let mut current = self.initialization_overrides.lock();
if current.as_ref() != new {
*current = new.cloned();
true
} else {
false
}
}
pub fn initialization_options(&self) -> Option<Value> {
let initialization_options = self.initialization_options.as_ref();
let override_options = self.initialization_overrides.lock().clone();
match (initialization_options, override_options) {
(None, override_options) => override_options,
(initialization_options, None) => initialization_options.cloned(),
(Some(initialization_options), Some(override_options)) => {
let mut initialization_options = initialization_options.clone();
merge_json_value_into(override_options, &mut initialization_options);
Some(initialization_options)
}
}
}
} }
pub trait LspAdapterDelegate: Send + Sync { pub trait LspAdapterDelegate: Send + Sync {

View file

@ -50,7 +50,7 @@ use lsp::{
}; };
use lsp_command::*; use lsp_command::*;
use postage::watch; use postage::watch;
use project_settings::ProjectSettings; use project_settings::{LspSettings, ProjectSettings};
use rand::prelude::*; use rand::prelude::*;
use search::SearchQuery; use search::SearchQuery;
use serde::Serialize; use serde::Serialize;
@ -78,8 +78,8 @@ use std::{
use terminals::Terminals; use terminals::Terminals;
use text::Anchor; use text::Anchor;
use util::{ use util::{
debug_panic, defer, http::HttpClient, paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, debug_panic, defer, http::HttpClient, merge_json_value_into,
TryFutureExt as _, paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _,
}; };
pub use fs::*; pub use fs::*;
@ -149,6 +149,7 @@ pub struct Project {
_maintain_workspace_config: Task<()>, _maintain_workspace_config: Task<()>,
terminals: Terminals, terminals: Terminals,
copilot_enabled: bool, copilot_enabled: bool,
current_lsp_settings: HashMap<Arc<str>, LspSettings>,
} }
struct DelayedDebounced { struct DelayedDebounced {
@ -614,6 +615,7 @@ impl Project {
local_handles: Vec::new(), local_handles: Vec::new(),
}, },
copilot_enabled: Copilot::global(cx).is_some(), copilot_enabled: Copilot::global(cx).is_some(),
current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(),
} }
}) })
} }
@ -706,6 +708,7 @@ impl Project {
local_handles: Vec::new(), local_handles: Vec::new(),
}, },
copilot_enabled: Copilot::global(cx).is_some(), copilot_enabled: Copilot::global(cx).is_some(),
current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(),
}; };
for worktree in worktrees { for worktree in worktrees {
let _ = this.add_worktree(&worktree, cx); let _ = this.add_worktree(&worktree, cx);
@ -779,7 +782,9 @@ impl Project {
let mut language_servers_to_stop = Vec::new(); let mut language_servers_to_stop = Vec::new();
let mut language_servers_to_restart = Vec::new(); let mut language_servers_to_restart = Vec::new();
let languages = self.languages.to_vec(); let languages = self.languages.to_vec();
let project_settings = settings::get::<ProjectSettings>(cx).clone();
let new_lsp_settings = settings::get::<ProjectSettings>(cx).lsp.clone();
let current_lsp_settings = &self.current_lsp_settings;
for (worktree_id, started_lsp_name) in self.language_server_ids.keys() { for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
let language = languages.iter().find_map(|l| { let language = languages.iter().find_map(|l| {
let adapter = l let adapter = l
@ -796,16 +801,25 @@ impl Project {
if !language_settings(Some(language), file.as_ref(), cx).enable_language_server { if !language_settings(Some(language), file.as_ref(), cx).enable_language_server {
language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
} else if let Some(worktree) = worktree { } else if let Some(worktree) = worktree {
let new_lsp_settings = project_settings let server_name = &adapter.name.0;
.lsp match (
.get(&adapter.name.0) current_lsp_settings.get(server_name),
.and_then(|s| s.initialization_options.as_ref()); new_lsp_settings.get(server_name),
if adapter.update_initialization_overrides(new_lsp_settings) { ) {
language_servers_to_restart.push((worktree, Arc::clone(language))); (None, None) => {}
(Some(_), None) | (None, Some(_)) => {
language_servers_to_restart.push((worktree, Arc::clone(language)));
}
(Some(current_lsp_settings), Some(new_lsp_settings)) => {
if current_lsp_settings != new_lsp_settings {
language_servers_to_restart.push((worktree, Arc::clone(language)));
}
}
} }
} }
} }
} }
self.current_lsp_settings = new_lsp_settings;
// Stop all newly-disabled language servers. // Stop all newly-disabled language servers.
for (worktree_id, adapter_name) in language_servers_to_stop { for (worktree_id, adapter_name) in language_servers_to_stop {
@ -2545,13 +2559,20 @@ impl Project {
let project_settings = settings::get::<ProjectSettings>(cx); let project_settings = settings::get::<ProjectSettings>(cx);
let lsp = project_settings.lsp.get(&adapter.name.0); let lsp = project_settings.lsp.get(&adapter.name.0);
let override_options = lsp.map(|s| s.initialization_options.clone()).flatten(); let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
adapter.update_initialization_overrides(override_options.as_ref());
let mut initialization_options = adapter.initialization_options.clone();
match (&mut initialization_options, override_options) {
(Some(initialization_options), Some(override_options)) => {
merge_json_value_into(override_options, initialization_options);
}
(None, override_options) => initialization_options = override_options,
_ => {}
}
let server_id = pending_server.server_id; let server_id = pending_server.server_id;
let container_dir = pending_server.container_dir.clone(); let container_dir = pending_server.container_dir.clone();
let state = LanguageServerState::Starting({ let state = LanguageServerState::Starting({
let adapter = adapter.clone(); let adapter = adapter.clone();
let initialization_options = adapter.initialization_options();
let server_name = adapter.name.0.clone(); let server_name = adapter.name.0.clone();
let languages = self.languages.clone(); let languages = self.languages.clone();
let language = language.clone(); let language = language.clone();

View file

@ -907,6 +907,7 @@ mod tests {
let params = cx.update(AppState::test); let params = cx.update(AppState::test);
cx.update(|cx| { cx.update(|cx| {
theme::init((), cx); theme::init((), cx);
Project::init_settings(cx);
language::init(cx); language::init(cx);
}); });

View file

@ -2316,6 +2316,7 @@ mod tests {
cx.set_global(SettingsStore::test(cx)); cx.set_global(SettingsStore::test(cx));
theme::init((), cx); theme::init((), cx);
crate::init_settings(cx); crate::init_settings(cx);
Project::init_settings(cx);
}); });
} }