Display a "Checking..." message when running disk-based diagnostics

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2022-01-07 15:03:19 +01:00
parent e39be35e17
commit cf62d26ed8
6 changed files with 195 additions and 55 deletions

View file

@ -3,11 +3,13 @@ use gpui::{
}; };
use postage::watch; use postage::watch;
use project::Project; use project::Project;
use std::fmt::Write;
use workspace::{Settings, StatusItemView}; use workspace::{Settings, StatusItemView};
pub struct DiagnosticSummary { pub struct DiagnosticSummary {
settings: watch::Receiver<Settings>, settings: watch::Receiver<Settings>,
summary: project::DiagnosticSummary, summary: project::DiagnosticSummary,
in_progress: bool,
} }
impl DiagnosticSummary { impl DiagnosticSummary {
@ -16,16 +18,26 @@ impl DiagnosticSummary {
settings: watch::Receiver<Settings>, settings: watch::Receiver<Settings>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
cx.subscribe(project, |this, project, event, cx| { cx.subscribe(project, |this, project, event, cx| match event {
if let project::Event::DiskBasedDiagnosticsUpdated { .. } = event { project::Event::DiskBasedDiagnosticsUpdated { .. } => {
this.summary = project.read(cx).diagnostic_summary(cx); this.summary = project.read(cx).diagnostic_summary(cx);
cx.notify(); cx.notify();
} }
project::Event::DiskBasedDiagnosticsStarted => {
this.in_progress = true;
cx.notify();
}
project::Event::DiskBasedDiagnosticsFinished => {
this.in_progress = false;
cx.notify();
}
_ => {}
}) })
.detach(); .detach();
Self { Self {
settings, settings,
summary: project.read(cx).diagnostic_summary(cx), summary: project.read(cx).diagnostic_summary(cx),
in_progress: project.read(cx).is_running_disk_based_diagnostics(),
} }
} }
} }
@ -43,14 +55,18 @@ impl View for DiagnosticSummary {
enum Tag {} enum Tag {}
let theme = &self.settings.borrow().theme.project_diagnostics; let theme = &self.settings.borrow().theme.project_diagnostics;
MouseEventHandler::new::<Tag, _, _, _>(0, cx, |_, _| { let mut message = String::new();
Label::new( if self.in_progress {
format!( message.push_str("Checking... ");
}
write!(
message,
"Errors: {}, Warnings: {}", "Errors: {}, Warnings: {}",
self.summary.error_count, self.summary.warning_count self.summary.error_count, self.summary.warning_count
),
theme.status_bar_item.text.clone(),
) )
.unwrap();
MouseEventHandler::new::<Tag, _, _, _>(0, cx, |_, _| {
Label::new(message, theme.status_bar_item.text.clone())
.contained() .contained()
.with_style(theme.status_bar_item.container) .with_style(theme.status_bar_item.container)
.boxed() .boxed()

View file

@ -33,6 +33,7 @@ pub struct Project {
client_state: ProjectClientState, client_state: ProjectClientState,
collaborators: HashMap<PeerId, Collaborator>, collaborators: HashMap<PeerId, Collaborator>,
subscriptions: Vec<client::Subscription>, subscriptions: Vec<client::Subscription>,
pending_disk_based_diagnostics: isize,
} }
enum ProjectClientState { enum ProjectClientState {
@ -60,7 +61,9 @@ pub struct Collaborator {
pub enum Event { pub enum Event {
ActiveEntryChanged(Option<ProjectEntry>), ActiveEntryChanged(Option<ProjectEntry>),
WorktreeRemoved(WorktreeId), WorktreeRemoved(WorktreeId),
DiskBasedDiagnosticsStarted,
DiskBasedDiagnosticsUpdated { worktree_id: WorktreeId }, DiskBasedDiagnosticsUpdated { worktree_id: WorktreeId },
DiskBasedDiagnosticsFinished,
DiagnosticsUpdated(ProjectPath), DiagnosticsUpdated(ProjectPath),
} }
@ -187,6 +190,7 @@ impl Project {
client, client,
user_store, user_store,
fs, fs,
pending_disk_based_diagnostics: 0,
} }
}) })
} }
@ -259,6 +263,11 @@ impl Project {
cx, cx,
Self::handle_update_diagnostic_summary, Self::handle_update_diagnostic_summary,
), ),
client.subscribe_to_entity(
remote_id,
cx,
Self::handle_disk_based_diagnostics_updating,
),
client.subscribe_to_entity( client.subscribe_to_entity(
remote_id, remote_id,
cx, cx,
@ -273,6 +282,7 @@ impl Project {
remote_id, remote_id,
replica_id, replica_id,
}, },
pending_disk_based_diagnostics: 0,
}; };
for worktree in worktrees { for worktree in worktrees {
this.add_worktree(worktree, cx); this.add_worktree(worktree, cx);
@ -506,17 +516,29 @@ impl Project {
fn add_worktree(&mut self, worktree: ModelHandle<Worktree>, cx: &mut ModelContext<Self>) { fn add_worktree(&mut self, worktree: ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
cx.observe(&worktree, |_, _, cx| cx.notify()).detach(); 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) => { worktree::Event::DiagnosticsUpdated(path) => {
cx.emit(Event::DiagnosticsUpdated(ProjectPath { cx.emit(Event::DiagnosticsUpdated(ProjectPath {
worktree_id: worktree.read(cx).id(), worktree_id: worktree.read(cx).id(),
path: path.clone(), 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 => { worktree::Event::DiskBasedDiagnosticsUpdated => {
this.pending_disk_based_diagnostics -= 1;
cx.emit(Event::DiskBasedDiagnosticsUpdated { cx.emit(Event::DiskBasedDiagnosticsUpdated {
worktree_id: worktree.read(cx).id(), 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(); .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 { pub fn diagnostic_summary(&self, cx: &AppContext) -> DiagnosticSummary {
let mut summary = DiagnosticSummary::default(); let mut summary = DiagnosticSummary::default();
for (_, path_summary) in self.diagnostic_summaries(cx) { for (_, path_summary) in self.diagnostic_summaries(cx) {
@ -716,6 +742,24 @@ impl Project {
Ok(()) Ok(())
} }
fn handle_disk_based_diagnostics_updating(
&mut self,
envelope: TypedEnvelope<proto::DiskBasedDiagnosticsUpdating>,
_: Arc<Client>,
cx: &mut ModelContext<Self>,
) -> 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( fn handle_disk_based_diagnostics_updated(
&mut self, &mut self,
envelope: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>, envelope: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>,

View file

@ -67,6 +67,7 @@ pub enum Worktree {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum Event { pub enum Event {
DiskBasedDiagnosticsUpdating,
DiskBasedDiagnosticsUpdated, DiskBasedDiagnosticsUpdated,
DiagnosticsUpdated(Arc<Path>), DiagnosticsUpdated(Arc<Path>),
} }
@ -1133,6 +1134,11 @@ impl LocalWorktree {
.log_err() .log_err()
.flatten() .flatten()
{ {
enum DiagnosticProgress {
Updating,
Updated,
}
let disk_based_sources = language let disk_based_sources = language
.disk_based_diagnostic_sources() .disk_based_diagnostic_sources()
.cloned() .cloned()
@ -1155,10 +1161,21 @@ impl LocalWorktree {
while let Ok(diagnostics) = diagnostics_rx.recv().await { while let Ok(diagnostics) = diagnostics_rx.recv().await {
if let Some(handle) = cx.read(|cx| this.upgrade(cx)) { if let Some(handle) = cx.read(|cx| this.upgrade(cx)) {
handle.update(&mut cx, |this, 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) this.update_diagnostics(diagnostics, &disk_based_sources, cx)
.log_err(); .log_err();
if !has_disk_based_diagnostic_progress_token { 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 { } else {
@ -1181,12 +1198,22 @@ impl LocalWorktree {
match params.value { match params.value {
lsp::ProgressParamsValue::WorkDone(progress) => match progress { lsp::ProgressParamsValue::WorkDone(progress) => match progress {
lsp::WorkDoneProgress::Begin(_) => { 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; pending_disk_based_diagnostics += 1;
} }
lsp::WorkDoneProgress::End(_) => { lsp::WorkDoneProgress::End(_) => {
pending_disk_based_diagnostics -= 1; pending_disk_based_diagnostics -= 1;
if pending_disk_based_diagnostics == 0 { if pending_disk_based_diagnostics == 0 {
smol::block_on(disk_based_diagnostics_done_tx.send(())) smol::block_on(
disk_based_diagnostics_done_tx
.send(DiagnosticProgress::Updated),
)
.ok(); .ok();
} }
} }
@ -1198,22 +1225,42 @@ impl LocalWorktree {
.detach(); .detach();
let rpc = self.client.clone(); let rpc = self.client.clone();
cx.spawn_weak(|this, mut cx| async move { 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)) { if let Some(handle) = cx.read(|cx| this.upgrade(cx)) {
match progress {
DiagnosticProgress::Updating => {
let message = handle.update(&mut cx, |this, cx| { let message = handle.update(&mut cx, |this, cx| {
cx.emit(Event::DiskBasedDiagnosticsUpdated); cx.emit(Event::DiskBasedDiagnosticsUpdating);
let this = this.as_local().unwrap(); let this = this.as_local().unwrap();
this.share this.share.as_ref().map(|share| {
.as_ref() proto::DiskBasedDiagnosticsUpdating {
.map(|share| proto::DiskBasedDiagnosticsUpdated {
project_id: share.project_id, project_id: share.project_id,
worktree_id: this.id().to_proto(), worktree_id: this.id().to_proto(),
}
}) })
}); });
if let Some(message) = message { if let Some(message) = message {
rpc.send(message).await.log_err(); 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 { } else {
break; break;
} }
@ -1691,6 +1738,10 @@ impl RemoteWorktree {
} }
} }
pub fn disk_based_diagnostics_updating(&self, cx: &mut ModelContext<Worktree>) {
cx.emit(Event::DiskBasedDiagnosticsUpdating);
}
pub fn disk_based_diagnostics_updated(&self, cx: &mut ModelContext<Worktree>) { pub fn disk_based_diagnostics_updated(&self, cx: &mut ModelContext<Worktree>) {
cx.emit(Event::DiskBasedDiagnosticsUpdated); cx.emit(Event::DiskBasedDiagnosticsUpdated);
} }
@ -3848,6 +3899,11 @@ mod tests {
let mut events = subscribe(&tree, &mut cx); let mut events = subscribe(&tree, &mut cx);
fake_server.start_progress(&progress_token).await; fake_server.start_progress(&progress_token).await;
assert_eq!(
events.next().await.unwrap(),
Event::DiskBasedDiagnosticsUpdating
);
fake_server.start_progress(&progress_token).await; fake_server.start_progress(&progress_token).await;
fake_server.end_progress(&progress_token).await; fake_server.end_progress(&progress_token).await;
fake_server.start_progress(&progress_token).await; fake_server.start_progress(&progress_token).await;
@ -3864,18 +3920,17 @@ mod tests {
}], }],
}) })
.await; .await;
let event = events.next().await.unwrap();
assert_eq!( assert_eq!(
event, events.next().await.unwrap(),
Event::DiagnosticsUpdated(Arc::from(Path::new("a.rs"))) Event::DiagnosticsUpdated(Arc::from(Path::new("a.rs")))
); );
fake_server.end_progress(&progress_token).await; fake_server.end_progress(&progress_token).await;
fake_server.end_progress(&progress_token).await; fake_server.end_progress(&progress_token).await;
assert_eq!(
let event = events.next().await.unwrap(); events.next().await.unwrap(),
assert_eq!(event, Event::DiskBasedDiagnosticsUpdated); Event::DiskBasedDiagnosticsUpdated
);
let buffer = tree let buffer = tree
.update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx)) .update(&mut cx, |tree, cx| tree.open_buffer("a.rs", cx))

View file

@ -26,30 +26,31 @@ message Envelope {
ShareWorktree share_worktree = 19; ShareWorktree share_worktree = 19;
UpdateWorktree update_worktree = 20; UpdateWorktree update_worktree = 20;
UpdateDiagnosticSummary update_diagnostic_summary = 21; 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; OpenBuffer open_buffer = 24;
OpenBufferResponse open_buffer_response = 24; OpenBufferResponse open_buffer_response = 25;
CloseBuffer close_buffer = 25; CloseBuffer close_buffer = 26;
UpdateBuffer update_buffer = 26; UpdateBuffer update_buffer = 27;
SaveBuffer save_buffer = 27; SaveBuffer save_buffer = 28;
BufferSaved buffer_saved = 28; BufferSaved buffer_saved = 29;
GetChannels get_channels = 29; GetChannels get_channels = 30;
GetChannelsResponse get_channels_response = 30; GetChannelsResponse get_channels_response = 31;
JoinChannel join_channel = 31; JoinChannel join_channel = 32;
JoinChannelResponse join_channel_response = 32; JoinChannelResponse join_channel_response = 33;
LeaveChannel leave_channel = 33; LeaveChannel leave_channel = 34;
SendChannelMessage send_channel_message = 34; SendChannelMessage send_channel_message = 35;
SendChannelMessageResponse send_channel_message_response = 35; SendChannelMessageResponse send_channel_message_response = 36;
ChannelMessageSent channel_message_sent = 36; ChannelMessageSent channel_message_sent = 37;
GetChannelMessages get_channel_messages = 37; GetChannelMessages get_channel_messages = 38;
GetChannelMessagesResponse get_channel_messages_response = 38; GetChannelMessagesResponse get_channel_messages_response = 39;
UpdateContacts update_contacts = 39; UpdateContacts update_contacts = 40;
GetUsers get_users = 40; GetUsers get_users = 41;
GetUsersResponse get_users_response = 41; GetUsersResponse get_users_response = 42;
} }
} }
@ -181,6 +182,11 @@ message DiagnosticSummary {
uint32 hint_count = 7; uint32 hint_count = 7;
} }
message DiskBasedDiagnosticsUpdating {
uint64 project_id = 1;
uint64 worktree_id = 2;
}
message DiskBasedDiagnosticsUpdated { message DiskBasedDiagnosticsUpdated {
uint64 project_id = 1; uint64 project_id = 1;
uint64 worktree_id = 2; uint64 worktree_id = 2;

View file

@ -126,6 +126,7 @@ messages!(
ChannelMessageSent, ChannelMessageSent,
CloseBuffer, CloseBuffer,
DiskBasedDiagnosticsUpdated, DiskBasedDiagnosticsUpdated,
DiskBasedDiagnosticsUpdating,
Error, Error,
GetChannelMessages, GetChannelMessages,
GetChannelMessagesResponse, GetChannelMessagesResponse,
@ -183,6 +184,7 @@ entity_messages!(
BufferSaved, BufferSaved,
CloseBuffer, CloseBuffer,
DiskBasedDiagnosticsUpdated, DiskBasedDiagnosticsUpdated,
DiskBasedDiagnosticsUpdating,
JoinProject, JoinProject,
LeaveProject, LeaveProject,
OpenBuffer, OpenBuffer,

View file

@ -72,6 +72,7 @@ impl Server {
.add_handler(Server::share_worktree) .add_handler(Server::share_worktree)
.add_handler(Server::update_worktree) .add_handler(Server::update_worktree)
.add_handler(Server::update_diagnostic_summary) .add_handler(Server::update_diagnostic_summary)
.add_handler(Server::disk_based_diagnostics_updating)
.add_handler(Server::disk_based_diagnostics_updated) .add_handler(Server::disk_based_diagnostics_updated)
.add_handler(Server::open_buffer) .add_handler(Server::open_buffer)
.add_handler(Server::close_buffer) .add_handler(Server::close_buffer)
@ -556,6 +557,22 @@ impl Server {
Ok(()) Ok(())
} }
async fn disk_based_diagnostics_updating(
self: Arc<Server>,
request: TypedEnvelope<proto::DiskBasedDiagnosticsUpdating>,
) -> 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( async fn disk_based_diagnostics_updated(
self: Arc<Server>, self: Arc<Server>,
request: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>, request: TypedEnvelope<proto::DiskBasedDiagnosticsUpdated>,