@@ -125,6 +125,7 @@ pub enum Event {
DiskBasedDiagnosticsFinished,
DiagnosticsUpdated(ProjectPath),
RemoteIdChanged(Option<u64>),
+ CollaboratorLeft(PeerId),
}
enum LanguageServerEvent {
@@ -3368,6 +3369,7 @@ impl Project {
buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));
}
}
+ cx.emit(Event::CollaboratorLeft(peer_id));
cx.notify();
Ok(())
})
@@ -4259,6 +4259,7 @@ mod tests {
// Client A opens some editors.
let workspace_a = client_a.build_workspace(&project_a, cx_a);
+ let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
let editor_a1 = workspace_a
.update(cx_a, |workspace, cx| {
workspace.open_path((worktree_id, "1.txt"), cx)
@@ -4287,19 +4288,19 @@ mod tests {
.downcast::<Editor>()
.unwrap();
+ let client_a_id = project_b.read_with(cx_b, |project, _| {
+ project.collaborators().values().next().unwrap().peer_id
+ });
+ let client_b_id = project_a.read_with(cx_a, |project, _| {
+ project.collaborators().values().next().unwrap().peer_id
+ });
+
// When client B starts following client A, all visible view states are replicated to client B.
editor_a1.update(cx_a, |editor, cx| editor.select_ranges([0..1], None, cx));
editor_a2.update(cx_a, |editor, cx| editor.select_ranges([2..3], None, cx));
workspace_b
.update(cx_b, |workspace, cx| {
- let leader_id = project_b
- .read(cx)
- .collaborators()
- .values()
- .next()
- .unwrap()
- .peer_id;
- workspace.toggle_follow(&leader_id.into(), cx).unwrap()
+ workspace.toggle_follow(&client_a_id.into(), cx).unwrap()
})
.await
.unwrap();
@@ -4370,6 +4371,33 @@ mod tests {
.id()),
editor_b1.id()
);
+
+ // Client A starts following client B.
+ workspace_a
+ .update(cx_a, |workspace, cx| {
+ workspace.toggle_follow(&client_b_id.into(), cx).unwrap()
+ })
+ .await
+ .unwrap();
+ assert_eq!(
+ workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
+ Some(client_b_id)
+ );
+ assert_eq!(
+ workspace_a.read_with(cx_a, |workspace, cx| workspace
+ .active_item(cx)
+ .unwrap()
+ .id()),
+ editor_a1.id()
+ );
+
+ // Following interrupts when client B disconnects.
+ client_b.disconnect(&cx_b.to_async()).unwrap();
+ cx_a.foreground().run_until_parked();
+ assert_eq!(
+ workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
+ None
+ );
}
#[gpui::test(iterations = 10)]
@@ -667,8 +667,14 @@ impl Workspace {
.detach();
cx.subscribe(¶ms.project, move |this, project, event, cx| {
- if let project::Event::RemoteIdChanged(remote_id) = event {
- this.project_remote_id_changed(*remote_id, cx);
+ match event {
+ project::Event::RemoteIdChanged(remote_id) => {
+ this.project_remote_id_changed(*remote_id, cx);
+ }
+ project::Event::CollaboratorLeft(peer_id) => {
+ this.collaborator_left(*peer_id, cx);
+ }
+ _ => {}
}
if project.read(cx).is_read_only() {
cx.blur();
@@ -1241,6 +1247,20 @@ impl Workspace {
}
}
+ fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
+ self.leader_state.followers.remove(&peer_id);
+ if let Some(states_by_pane) = self.follower_states_by_leader.remove(&peer_id) {
+ for state in states_by_pane.into_values() {
+ for item in state.items_by_leader_view_id.into_values() {
+ if let FollowerItem::Loaded(item) = item {
+ item.set_leader_replica_id(None, cx);
+ }
+ }
+ }
+ }
+ cx.notify();
+ }
+
pub fn toggle_follow(
&mut self,
ToggleFollow(leader_id): &ToggleFollow,