diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a35b8dc79ad073c407dcea7a212a97cf70b64b17..dd4a17c13a0685b45fb7e0a738b71f6c91db2233 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -51,8 +51,7 @@ pub struct Project { languages: Arc, language_servers: HashMap<(WorktreeId, Arc), Arc>, started_language_servers: HashMap<(WorktreeId, Arc), Task>>>, - pending_language_server_work: BTreeMap<(usize, String), LanguageServerProgress>, - language_server_names: HashMap, + language_server_statuses: BTreeMap, next_language_server_id: usize, client: Arc, user_store: ModelHandle, @@ -131,6 +130,12 @@ enum LanguageServerEvent { DiagnosticsUpdate(lsp::PublishDiagnosticsParams), } +pub struct LanguageServerStatus { + pub name: String, + pub pending_work: BTreeMap, + pending_diagnostic_updates: isize, +} + #[derive(Clone, Default)] pub struct LanguageServerProgress { pub message: Option, @@ -326,8 +331,7 @@ impl Project { language_servers_with_diagnostics_running: 0, language_servers: Default::default(), started_language_servers: Default::default(), - pending_language_server_work: Default::default(), - language_server_names: Default::default(), + language_server_statuses: Default::default(), next_language_server_id: 0, nonce: StdRng::from_entropy().gen(), } @@ -398,11 +402,19 @@ impl Project { language_servers_with_diagnostics_running: 0, language_servers: Default::default(), started_language_servers: Default::default(), - pending_language_server_work: Default::default(), - language_server_names: response + language_server_statuses: response .language_servers .into_iter() - .map(|s| (s.id as usize, s.name)) + .map(|server| { + ( + server.id as usize, + LanguageServerStatus { + name: server.name, + pending_work: Default::default(), + pending_diagnostic_updates: 0, + }, + ) + }) .collect(), next_language_server_id: 0, opened_buffers: Default::default(), @@ -1274,8 +1286,14 @@ impl Project { this.update(&mut cx, |this, cx| { this.language_servers .insert(key.clone(), language_server.clone()); - this.language_server_names - .insert(server_id, language_server.name().to_string()); + this.language_server_statuses.insert( + server_id, + LanguageServerStatus { + name: language_server.name().to_string(), + pending_work: Default::default(), + pending_diagnostic_updates: 0, + }, + ); if let Some(project_id) = this.remote_id() { this.client @@ -1359,16 +1377,26 @@ impl Project { cx: &mut ModelContext, ) { let disk_diagnostics_token = language.disk_based_diagnostics_progress_token(); + let language_server_status = + if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) { + status + } else { + return; + }; + match event { LanguageServerEvent::WorkStart { token } => { if Some(&token) == disk_diagnostics_token { - self.disk_based_diagnostics_started(cx); - self.broadcast_language_server_update( - language_server_id, - proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating( - proto::LspDiskBasedDiagnosticsUpdating {}, - ), - ); + language_server_status.pending_diagnostic_updates += 1; + if language_server_status.pending_diagnostic_updates == 1 { + self.disk_based_diagnostics_started(cx); + self.broadcast_language_server_update( + language_server_id, + proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating( + proto::LspDiskBasedDiagnosticsUpdating {}, + ), + ); + } } else { self.on_lsp_work_start(language_server_id, token.clone(), cx); self.broadcast_language_server_update( @@ -1401,13 +1429,16 @@ impl Project { } LanguageServerEvent::WorkEnd { token } => { if Some(&token) == disk_diagnostics_token { - self.disk_based_diagnostics_finished(cx); - self.broadcast_language_server_update( - language_server_id, - proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated( - proto::LspDiskBasedDiagnosticsUpdated {}, - ), - ); + language_server_status.pending_diagnostic_updates -= 1; + if language_server_status.pending_diagnostic_updates == 0 { + self.disk_based_diagnostics_finished(cx); + self.broadcast_language_server_update( + language_server_id, + proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated( + proto::LspDiskBasedDiagnosticsUpdated {}, + ), + ); + } } else { self.on_lsp_work_end(language_server_id, token.clone(), cx); self.broadcast_language_server_update( @@ -1457,14 +1488,16 @@ impl Project { token: String, cx: &mut ModelContext, ) { - self.pending_language_server_work.insert( - (language_server_id, token), - LanguageServerProgress { - message: None, - percentage: None, - }, - ); - cx.notify(); + if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) { + status.pending_work.insert( + token, + LanguageServerProgress { + message: None, + percentage: None, + }, + ); + cx.notify(); + } } fn on_lsp_work_progress( @@ -1474,9 +1507,10 @@ impl Project { progress: LanguageServerProgress, cx: &mut ModelContext, ) { - self.pending_language_server_work - .insert((language_server_id, token), progress); - cx.notify(); + if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) { + status.pending_work.insert(token, progress); + cx.notify(); + } } fn on_lsp_work_end( @@ -1485,9 +1519,10 @@ impl Project { token: String, cx: &mut ModelContext, ) { - self.pending_language_server_work - .remove(&(language_server_id, token)); - cx.notify(); + if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) { + status.pending_work.remove(&token); + cx.notify(); + } } fn broadcast_language_server_update( @@ -1506,15 +1541,8 @@ impl Project { } } - pub fn pending_language_server_work( - &self, - ) -> impl Iterator { - self.pending_language_server_work.iter().filter_map( - |((language_server_id, token), progress)| { - let name = self.language_server_names.get(language_server_id)?; - Some((name.as_str(), token.as_str(), progress)) - }, - ) + pub fn language_server_statuses(&self) -> impl Iterator { + self.language_server_statuses.values() } pub fn update_diagnostics( @@ -3266,8 +3294,14 @@ impl Project { .server .ok_or_else(|| anyhow!("invalid server"))?; this.update(&mut cx, |this, cx| { - this.language_server_names - .insert(server.id as usize, server.name); + this.language_server_statuses.insert( + server.id as usize, + LanguageServerStatus { + name: server.name, + pending_work: Default::default(), + pending_diagnostic_updates: 0, + }, + ); cx.notify(); }); Ok(()) diff --git a/crates/workspace/src/lsp_status.rs b/crates/workspace/src/lsp_status.rs index e2976824b5215d979c1cc01884784f6ce8382cb7..6907b02948c1022e4ce13851cfacb2b7bf40d161 100644 --- a/crates/workspace/src/lsp_status.rs +++ b/crates/workspace/src/lsp_status.rs @@ -1,12 +1,13 @@ use crate::{ItemViewHandle, Settings, StatusItemView}; use futures::StreamExt; +use gpui::AppContext; use gpui::{ action, elements::*, platform::CursorStyle, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, }; use language::{LanguageRegistry, LanguageServerBinaryStatus}; use postage::watch; -use project::Project; +use project::{LanguageServerProgress, Project}; use std::fmt::Write; use std::sync::Arc; @@ -81,6 +82,27 @@ impl LspStatus { self.failed.clear(); cx.notify(); } + + fn pending_language_server_work<'a>( + &self, + cx: &'a AppContext, + ) -> impl Iterator { + self.project + .read(cx) + .language_server_statuses() + .filter_map(|status| { + if status.pending_work.is_empty() { + None + } else { + Some( + status.pending_work.iter().map(|(token, progress)| { + (status.name.as_str(), token.as_str(), progress) + }), + ) + } + }) + .flatten() + } } impl Entity for LspStatus { @@ -95,7 +117,7 @@ impl View for LspStatus { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { let theme = &self.settings_rx.borrow().theme; - let mut pending_work = self.project.read(cx).pending_language_server_work(); + let mut pending_work = self.pending_language_server_work(cx); if let Some((lang_server_name, progress_token, progress)) = pending_work.next() { let mut message = lang_server_name.to_string();