Send diagnostic summaries to guests when they join the project
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
d7a78e14ac
commit
2dbee1d914
7 changed files with 217 additions and 66 deletions
|
@ -17,7 +17,7 @@ use rpc::{
|
|||
Connection, ConnectionId, Peer, TypedEnvelope,
|
||||
};
|
||||
use sha1::{Digest as _, Sha1};
|
||||
use std::{any::TypeId, future::Future, mem, sync::Arc, time::Instant};
|
||||
use std::{any::TypeId, future::Future, mem, path::PathBuf, sync::Arc, time::Instant};
|
||||
use store::{Store, Worktree};
|
||||
use surf::StatusCode;
|
||||
use tide::log;
|
||||
|
@ -302,6 +302,11 @@ impl Server {
|
|||
id: *id,
|
||||
root_name: worktree.root_name.clone(),
|
||||
entries: share.entries.values().cloned().collect(),
|
||||
diagnostic_summaries: share
|
||||
.diagnostic_summaries
|
||||
.values()
|
||||
.cloned()
|
||||
.collect(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
@ -473,11 +478,17 @@ impl Server {
|
|||
.map(|entry| (entry.id, entry))
|
||||
.collect();
|
||||
|
||||
let diagnostic_summaries = mem::take(&mut worktree.diagnostic_summaries)
|
||||
.into_iter()
|
||||
.map(|summary| (PathBuf::from(summary.path.clone()), summary))
|
||||
.collect();
|
||||
|
||||
let contact_user_ids = self.state_mut().share_worktree(
|
||||
request.payload.project_id,
|
||||
worktree.id,
|
||||
request.sender_id,
|
||||
entries,
|
||||
diagnostic_summaries,
|
||||
);
|
||||
if let Some(contact_user_ids) = contact_user_ids {
|
||||
self.peer.respond(request.receipt(), proto::Ack {}).await?;
|
||||
|
@ -520,13 +531,23 @@ impl Server {
|
|||
}
|
||||
|
||||
async fn update_diagnostic_summary(
|
||||
self: Arc<Server>,
|
||||
mut self: Arc<Server>,
|
||||
request: TypedEnvelope<proto::UpdateDiagnosticSummary>,
|
||||
) -> tide::Result<()> {
|
||||
let receiver_ids = self
|
||||
.state()
|
||||
.project_connection_ids(request.payload.project_id, request.sender_id)
|
||||
let receiver_ids = request
|
||||
.payload
|
||||
.summary
|
||||
.clone()
|
||||
.and_then(|summary| {
|
||||
self.state_mut().update_diagnostic_summary(
|
||||
request.payload.project_id,
|
||||
request.payload.worktree_id,
|
||||
request.sender_id,
|
||||
summary,
|
||||
)
|
||||
})
|
||||
.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())
|
||||
|
@ -1816,6 +1837,39 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
// Simulate a language server reporting errors for a file.
|
||||
fake_language_server
|
||||
.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
|
||||
uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
|
||||
version: None,
|
||||
diagnostics: vec![lsp::Diagnostic {
|
||||
severity: Some(lsp::DiagnosticSeverity::ERROR),
|
||||
range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
|
||||
message: "message 1".to_string(),
|
||||
..Default::default()
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Wait for server to see the diagnostics update.
|
||||
server
|
||||
.condition(|store| {
|
||||
let worktree = store
|
||||
.project(project_id)
|
||||
.unwrap()
|
||||
.worktrees
|
||||
.get(&worktree_id.to_proto())
|
||||
.unwrap();
|
||||
|
||||
!worktree
|
||||
.share
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.diagnostic_summaries
|
||||
.is_empty()
|
||||
})
|
||||
.await;
|
||||
|
||||
// Join the worktree as client B.
|
||||
let project_b = Project::remote(
|
||||
project_id,
|
||||
|
@ -1828,7 +1882,24 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
// Simulate a language server reporting errors for a file.
|
||||
project_b.read_with(&cx_b, |project, cx| {
|
||||
assert_eq!(
|
||||
project.diagnostic_summaries(cx).collect::<Vec<_>>(),
|
||||
&[(
|
||||
ProjectPath {
|
||||
worktree_id,
|
||||
path: Arc::from(Path::new("a.rs")),
|
||||
},
|
||||
DiagnosticSummary {
|
||||
error_count: 1,
|
||||
warning_count: 0,
|
||||
..Default::default()
|
||||
},
|
||||
)]
|
||||
)
|
||||
});
|
||||
|
||||
// Simulate a language server reporting more errors for a file.
|
||||
fake_language_server
|
||||
.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
|
||||
uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
|
||||
|
@ -1853,6 +1924,7 @@ mod tests {
|
|||
})
|
||||
.await;
|
||||
|
||||
// Client b gets the updated summaries
|
||||
project_b
|
||||
.condition(&cx_b, |project, cx| {
|
||||
project.diagnostic_summaries(cx).collect::<Vec<_>>()
|
||||
|
@ -1870,7 +1942,7 @@ mod tests {
|
|||
})
|
||||
.await;
|
||||
|
||||
// Open the file with the errors.
|
||||
// Open the file with the errors on client B. They should be present.
|
||||
let worktree_b = project_b.update(&mut cx_b, |p, _| p.worktrees()[0].clone());
|
||||
let buffer_b = cx_b
|
||||
.background()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::db::{ChannelId, UserId};
|
||||
use anyhow::anyhow;
|
||||
use collections::{HashMap, HashSet};
|
||||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use rpc::{proto, ConnectionId};
|
||||
use std::collections::hash_map;
|
||||
use std::{collections::hash_map, path::PathBuf};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Store {
|
||||
|
@ -41,6 +41,7 @@ pub struct ProjectShare {
|
|||
|
||||
pub struct WorktreeShare {
|
||||
pub entries: HashMap<u64, proto::Entry>,
|
||||
pub diagnostic_summaries: BTreeMap<PathBuf, proto::DiagnosticSummary>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -385,17 +386,42 @@ impl Store {
|
|||
worktree_id: u64,
|
||||
connection_id: ConnectionId,
|
||||
entries: HashMap<u64, proto::Entry>,
|
||||
diagnostic_summaries: BTreeMap<PathBuf, proto::DiagnosticSummary>,
|
||||
) -> Option<Vec<UserId>> {
|
||||
let project = self.projects.get_mut(&project_id)?;
|
||||
let worktree = project.worktrees.get_mut(&worktree_id)?;
|
||||
if project.host_connection_id == connection_id && project.share.is_some() {
|
||||
worktree.share = Some(WorktreeShare { entries });
|
||||
worktree.share = Some(WorktreeShare {
|
||||
entries,
|
||||
diagnostic_summaries,
|
||||
});
|
||||
Some(project.authorized_user_ids())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_diagnostic_summary(
|
||||
&mut self,
|
||||
project_id: u64,
|
||||
worktree_id: u64,
|
||||
connection_id: ConnectionId,
|
||||
summary: proto::DiagnosticSummary,
|
||||
) -> Option<Vec<ConnectionId>> {
|
||||
let project = self.projects.get_mut(&project_id)?;
|
||||
let worktree = project.worktrees.get_mut(&worktree_id)?;
|
||||
if project.host_connection_id == connection_id {
|
||||
if let Some(share) = worktree.share.as_mut() {
|
||||
share
|
||||
.diagnostic_summaries
|
||||
.insert(summary.path.clone().into(), summary);
|
||||
return Some(project.connection_ids());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn join_project(
|
||||
&mut self,
|
||||
connection_id: ConnectionId,
|
||||
|
@ -497,6 +523,11 @@ impl Store {
|
|||
Some(self.channels.get(&channel_id)?.connection_ids())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn project(&self, project_id: u64) -> Option<&Project> {
|
||||
self.projects.get(&project_id)
|
||||
}
|
||||
|
||||
pub fn read_project(&self, project_id: u64, connection_id: ConnectionId) -> Option<&Project> {
|
||||
let project = self.projects.get(&project_id)?;
|
||||
if project.host_connection_id == connection_id
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue