diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index ae54b9879b..072738fa77 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -3,11 +3,13 @@ use gpui::{ }; use postage::watch; use project::Project; +use std::fmt::Write; use workspace::{Settings, StatusItemView}; pub struct DiagnosticSummary { settings: watch::Receiver, summary: project::DiagnosticSummary, + in_progress: bool, } impl DiagnosticSummary { @@ -16,16 +18,26 @@ impl DiagnosticSummary { settings: watch::Receiver, cx: &mut ViewContext, ) -> Self { - cx.subscribe(project, |this, project, event, cx| { - if let project::Event::DiskBasedDiagnosticsUpdated { .. } = event { + cx.subscribe(project, |this, project, event, cx| match event { + project::Event::DiskBasedDiagnosticsUpdated { .. } => { this.summary = project.read(cx).diagnostic_summary(cx); cx.notify(); } + project::Event::DiskBasedDiagnosticsStarted => { + this.in_progress = true; + cx.notify(); + } + project::Event::DiskBasedDiagnosticsFinished => { + this.in_progress = false; + cx.notify(); + } + _ => {} }) .detach(); Self { settings, summary: project.read(cx).diagnostic_summary(cx), + in_progress: project.read(cx).is_running_disk_based_diagnostics(), } } } @@ -43,17 +55,21 @@ impl View for DiagnosticSummary { enum Tag {} let theme = &self.settings.borrow().theme.project_diagnostics; + let mut message = String::new(); + if self.in_progress { + message.push_str("Checking... "); + } + write!( + message, + "Errors: {}, Warnings: {}", + self.summary.error_count, self.summary.warning_count + ) + .unwrap(); MouseEventHandler::new::(0, cx, |_, _| { - Label::new( - format!( - "Errors: {}, Warnings: {}", - self.summary.error_count, self.summary.warning_count - ), - theme.status_bar_item.text.clone(), - ) - .contained() - .with_style(theme.status_bar_item.container) - .boxed() + Label::new(message, theme.status_bar_item.text.clone()) + .contained() + .with_style(theme.status_bar_item.container) + .boxed() }) .with_cursor_style(CursorStyle::PointingHand) .on_click(|cx| cx.dispatch_action(crate::Deploy)) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c1da5aa842..62ecf94b7e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -33,6 +33,7 @@ pub struct Project { client_state: ProjectClientState, collaborators: HashMap, subscriptions: Vec, + pending_disk_based_diagnostics: isize, } enum ProjectClientState { @@ -60,7 +61,9 @@ pub struct Collaborator { pub enum Event { ActiveEntryChanged(Option), WorktreeRemoved(WorktreeId), + DiskBasedDiagnosticsStarted, DiskBasedDiagnosticsUpdated { worktree_id: WorktreeId }, + DiskBasedDiagnosticsFinished, DiagnosticsUpdated(ProjectPath), } @@ -187,6 +190,7 @@ impl Project { client, user_store, fs, + pending_disk_based_diagnostics: 0, } }) } @@ -259,6 +263,11 @@ impl Project { cx, Self::handle_update_diagnostic_summary, ), + client.subscribe_to_entity( + remote_id, + cx, + Self::handle_disk_based_diagnostics_updating, + ), client.subscribe_to_entity( remote_id, cx, @@ -273,6 +282,7 @@ impl Project { remote_id, replica_id, }, + pending_disk_based_diagnostics: 0, }; for worktree in worktrees { this.add_worktree(worktree, cx); @@ -506,17 +516,29 @@ impl Project { fn add_worktree(&mut self, worktree: ModelHandle, cx: &mut ModelContext) { cx.observe(&worktree, |_, _, cx| cx.notify()).detach(); - cx.subscribe(&worktree, |_, worktree, event, cx| match event { + cx.subscribe(&worktree, move |this, worktree, event, cx| match event { worktree::Event::DiagnosticsUpdated(path) => { cx.emit(Event::DiagnosticsUpdated(ProjectPath { worktree_id: worktree.read(cx).id(), path: path.clone(), })); } + worktree::Event::DiskBasedDiagnosticsUpdating => { + if this.pending_disk_based_diagnostics == 0 { + cx.emit(Event::DiskBasedDiagnosticsStarted); + } + this.pending_disk_based_diagnostics += 1; + } worktree::Event::DiskBasedDiagnosticsUpdated => { + this.pending_disk_based_diagnostics -= 1; cx.emit(Event::DiskBasedDiagnosticsUpdated { worktree_id: worktree.read(cx).id(), }); + if this.pending_disk_based_diagnostics == 0 { + if this.pending_disk_based_diagnostics == 0 { + cx.emit(Event::DiskBasedDiagnosticsFinished); + } + } } }) .detach(); @@ -539,6 +561,10 @@ impl Project { } } + pub fn is_running_disk_based_diagnostics(&self) -> bool { + self.pending_disk_based_diagnostics > 0 + } + pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary { let mut summary = DiagnosticSummary::default(); for (_, path_summary) in self.diagnostic_summaries(cx) { @@ -716,6 +742,24 @@ impl Project { Ok(()) } + fn handle_disk_based_diagnostics_updating( + &mut self, + envelope: TypedEnvelope, + _: Arc, + cx: &mut ModelContext, + ) -> Result<()> { + let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id); + if let Some(worktree) = self.worktree_for_id(worktree_id, cx) { + worktree.update(cx, |worktree, cx| { + worktree + .as_remote() + .unwrap() + .disk_based_diagnostics_updating(cx); + }); + } + Ok(()) + } + fn handle_disk_based_diagnostics_updated( &mut self, envelope: TypedEnvelope, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 350908d0d5..46caf8cd93 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -67,6 +67,7 @@ pub enum Worktree { #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { + DiskBasedDiagnosticsUpdating, DiskBasedDiagnosticsUpdated, DiagnosticsUpdated(Arc), } @@ -1133,6 +1134,11 @@ impl LocalWorktree { .log_err() .flatten() { + enum DiagnosticProgress { + Updating, + Updated, + } + let disk_based_sources = language .disk_based_diagnostic_sources() .cloned() @@ -1155,10 +1161,21 @@ impl LocalWorktree { while let Ok(diagnostics) = diagnostics_rx.recv().await { if let Some(handle) = cx.read(|cx| this.upgrade(cx)) { handle.update(&mut cx, |this, cx| { + if !has_disk_based_diagnostic_progress_token { + smol::block_on( + disk_based_diagnostics_done_tx + .send(DiagnosticProgress::Updating), + ) + .ok(); + } this.update_diagnostics(diagnostics, &disk_based_sources, cx) .log_err(); if !has_disk_based_diagnostic_progress_token { - smol::block_on(disk_based_diagnostics_done_tx.send(())).ok(); + smol::block_on( + disk_based_diagnostics_done_tx + .send(DiagnosticProgress::Updated), + ) + .ok(); } }) } else { @@ -1181,13 +1198,23 @@ impl LocalWorktree { match params.value { lsp::ProgressParamsValue::WorkDone(progress) => match progress { lsp::WorkDoneProgress::Begin(_) => { + if pending_disk_based_diagnostics == 0 { + smol::block_on( + disk_based_diagnostics_done_tx + .send(DiagnosticProgress::Updating), + ) + .ok(); + } pending_disk_based_diagnostics += 1; } lsp::WorkDoneProgress::End(_) => { pending_disk_based_diagnostics -= 1; if pending_disk_based_diagnostics == 0 { - smol::block_on(disk_based_diagnostics_done_tx.send(())) - .ok(); + smol::block_on( + disk_based_diagnostics_done_tx + .send(DiagnosticProgress::Updated), + ) + .ok(); } } _ => {} @@ -1198,21 +1225,41 @@ impl LocalWorktree { .detach(); let rpc = self.client.clone(); cx.spawn_weak(|this, mut cx| async move { - while let Ok(()) = disk_based_diagnostics_done_rx.recv().await { + while let Ok(progress) = disk_based_diagnostics_done_rx.recv().await { if let Some(handle) = cx.read(|cx| this.upgrade(cx)) { - let message = handle.update(&mut cx, |this, cx| { - cx.emit(Event::DiskBasedDiagnosticsUpdated); - let this = this.as_local().unwrap(); - this.share - .as_ref() - .map(|share| proto::DiskBasedDiagnosticsUpdated { - project_id: share.project_id, - worktree_id: this.id().to_proto(), - }) - }); + match progress { + DiagnosticProgress::Updating => { + let message = handle.update(&mut cx, |this, cx| { + cx.emit(Event::DiskBasedDiagnosticsUpdating); + let this = this.as_local().unwrap(); + this.share.as_ref().map(|share| { + proto::DiskBasedDiagnosticsUpdating { + project_id: share.project_id, + worktree_id: this.id().to_proto(), + } + }) + }); - if let Some(message) = message { - rpc.send(message).await.log_err(); + if let Some(message) = message { + rpc.send(message).await.log_err(); + } + } + DiagnosticProgress::Updated => { + let message = handle.update(&mut cx, |this, cx| { + cx.emit(Event::DiskBasedDiagnosticsUpdated); + let this = this.as_local().unwrap(); + this.share.as_ref().map(|share| { + proto::DiskBasedDiagnosticsUpdated { + project_id: share.project_id, + worktree_id: this.id().to_proto(), + } + }) + }); + + if let Some(message) = message { + rpc.send(message).await.log_err(); + } + } } } else { break; @@ -1691,6 +1738,10 @@ impl RemoteWorktree { } } + pub fn disk_based_diagnostics_updating(&self, cx: &mut ModelContext) { + cx.emit(Event::DiskBasedDiagnosticsUpdating); + } + pub fn disk_based_diagnostics_updated(&self, cx: &mut ModelContext) { cx.emit(Event::DiskBasedDiagnosticsUpdated); } @@ -3848,6 +3899,11 @@ mod tests { let mut events = subscribe(&tree, &mut cx); fake_server.start_progress(&progress_token).await; + assert_eq!( + events.next().await.unwrap(), + Event::DiskBasedDiagnosticsUpdating + ); + fake_server.start_progress(&progress_token).await; fake_server.end_progress(&progress_token).await; fake_server.start_progress(&progress_token).await; @@ -3864,18 +3920,17 @@ mod tests { }], }) .await; - - let event = events.next().await.unwrap(); assert_eq!( - event, + events.next().await.unwrap(), Event::DiagnosticsUpdated(Arc::from(Path::new("a.rs"))) ); fake_server.end_progress(&progress_token).await; fake_server.end_progress(&progress_token).await; - - let event = events.next().await.unwrap(); - assert_eq!(event, Event::DiskBasedDiagnosticsUpdated); + assert_eq!( + events.next().await.unwrap(), + Event::DiskBasedDiagnosticsUpdated + ); let buffer = tree .update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx)) diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 669ffdbd7e..f6300c4495 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -26,30 +26,31 @@ message Envelope { ShareWorktree share_worktree = 19; UpdateWorktree update_worktree = 20; UpdateDiagnosticSummary update_diagnostic_summary = 21; - DiskBasedDiagnosticsUpdated disk_based_diagnostics_updated = 22; + DiskBasedDiagnosticsUpdating disk_based_diagnostics_updating = 22; + DiskBasedDiagnosticsUpdated disk_based_diagnostics_updated = 23; - OpenBuffer open_buffer = 23; - OpenBufferResponse open_buffer_response = 24; - CloseBuffer close_buffer = 25; - UpdateBuffer update_buffer = 26; - SaveBuffer save_buffer = 27; - BufferSaved buffer_saved = 28; + OpenBuffer open_buffer = 24; + OpenBufferResponse open_buffer_response = 25; + CloseBuffer close_buffer = 26; + UpdateBuffer update_buffer = 27; + SaveBuffer save_buffer = 28; + BufferSaved buffer_saved = 29; - GetChannels get_channels = 29; - GetChannelsResponse get_channels_response = 30; - JoinChannel join_channel = 31; - JoinChannelResponse join_channel_response = 32; - LeaveChannel leave_channel = 33; - SendChannelMessage send_channel_message = 34; - SendChannelMessageResponse send_channel_message_response = 35; - ChannelMessageSent channel_message_sent = 36; - GetChannelMessages get_channel_messages = 37; - GetChannelMessagesResponse get_channel_messages_response = 38; + GetChannels get_channels = 30; + GetChannelsResponse get_channels_response = 31; + JoinChannel join_channel = 32; + JoinChannelResponse join_channel_response = 33; + LeaveChannel leave_channel = 34; + SendChannelMessage send_channel_message = 35; + SendChannelMessageResponse send_channel_message_response = 36; + ChannelMessageSent channel_message_sent = 37; + GetChannelMessages get_channel_messages = 38; + GetChannelMessagesResponse get_channel_messages_response = 39; - UpdateContacts update_contacts = 39; + UpdateContacts update_contacts = 40; - GetUsers get_users = 40; - GetUsersResponse get_users_response = 41; + GetUsers get_users = 41; + GetUsersResponse get_users_response = 42; } } @@ -181,6 +182,11 @@ message DiagnosticSummary { uint32 hint_count = 7; } +message DiskBasedDiagnosticsUpdating { + uint64 project_id = 1; + uint64 worktree_id = 2; +} + message DiskBasedDiagnosticsUpdated { uint64 project_id = 1; uint64 worktree_id = 2; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 2cabdc9218..91abc2523d 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -126,6 +126,7 @@ messages!( ChannelMessageSent, CloseBuffer, DiskBasedDiagnosticsUpdated, + DiskBasedDiagnosticsUpdating, Error, GetChannelMessages, GetChannelMessagesResponse, @@ -183,6 +184,7 @@ entity_messages!( BufferSaved, CloseBuffer, DiskBasedDiagnosticsUpdated, + DiskBasedDiagnosticsUpdating, JoinProject, LeaveProject, OpenBuffer, diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index 510b701ddd..76698c3a19 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -72,6 +72,7 @@ impl Server { .add_handler(Server::share_worktree) .add_handler(Server::update_worktree) .add_handler(Server::update_diagnostic_summary) + .add_handler(Server::disk_based_diagnostics_updating) .add_handler(Server::disk_based_diagnostics_updated) .add_handler(Server::open_buffer) .add_handler(Server::close_buffer) @@ -556,6 +557,22 @@ impl Server { Ok(()) } + async fn disk_based_diagnostics_updating( + self: Arc, + request: TypedEnvelope, + ) -> tide::Result<()> { + let receiver_ids = self + .state() + .project_connection_ids(request.payload.project_id, request.sender_id) + .ok_or_else(|| anyhow!(NO_SUCH_PROJECT))?; + broadcast(request.sender_id, receiver_ids, |connection_id| { + self.peer + .forward_send(request.sender_id, connection_id, request.payload.clone()) + }) + .await?; + Ok(()) + } + async fn disk_based_diagnostics_updated( self: Arc, request: TypedEnvelope,