Detailed changes
@@ -84,7 +84,7 @@ use std::{
pub use sum_tree::Bias;
use theme::{DiagnosticStyle, Theme};
use util::{post_inc, ResultExt, TryFutureExt};
-use workspace::{ItemNavHistory, Workspace, WorkspaceId};
+use workspace::{ItemNavHistory, ViewId, Workspace, WorkspaceId};
use crate::git::diff_hunk_to_display;
@@ -467,6 +467,7 @@ pub struct Editor {
keymap_context_layers: BTreeMap<TypeId, gpui::keymap::Context>,
input_enabled: bool,
leader_replica_id: Option<u16>,
+ remote_id: Option<ViewId>,
hover_state: HoverState,
link_go_to_definition_state: LinkGoToDefinitionState,
_subscriptions: Vec<Subscription>,
@@ -1108,6 +1109,7 @@ impl Editor {
keymap_context_layers: Default::default(),
input_enabled: true,
leader_replica_id: None,
+ remote_id: None,
hover_state: Default::default(),
link_go_to_definition_state: Default::default(),
_subscriptions: vec![
@@ -3,6 +3,7 @@ use std::{cell::RefCell, rc::Rc, time::Instant};
use drag_and_drop::DragAndDrop;
use futures::StreamExt;
use indoc::indoc;
+use rpc::PeerId;
use unindent::Unindent;
use super::*;
@@ -24,7 +25,7 @@ use util::{
};
use workspace::{
item::{FollowableItem, ItemHandle},
- NavigationEntry, Pane,
+ NavigationEntry, Pane, ViewId,
};
#[gpui::test]
@@ -5123,7 +5124,16 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
let follower_1 = cx
.update(|cx| {
- Editor::from_state_proto(pane.clone(), project.clone(), &mut state_message, cx)
+ Editor::from_state_proto(
+ pane.clone(),
+ project.clone(),
+ ViewId {
+ creator: PeerId(0),
+ id: 0,
+ },
+ &mut state_message,
+ cx,
+ )
})
.unwrap()
.await
@@ -5209,7 +5219,16 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
let follower_2 = cx
.update(|cx| {
- Editor::from_state_proto(pane.clone(), project.clone(), &mut state_message, cx)
+ Editor::from_state_proto(
+ pane.clone(),
+ project.clone(),
+ ViewId {
+ creator: PeerId(0),
+ id: 0,
+ },
+ &mut state_message,
+ cx,
+ )
})
.unwrap()
.await
@@ -28,24 +28,32 @@ use std::{
};
use text::Selection;
use util::{ResultExt, TryFutureExt};
+use workspace::item::FollowableItemHandle;
use workspace::{
item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
- ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, Workspace, WorkspaceId,
+ ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, ViewId, Workspace,
+ WorkspaceId,
};
pub const MAX_TAB_TITLE_LEN: usize = 24;
impl FollowableItem for Editor {
+ fn remote_id(&self) -> Option<ViewId> {
+ self.remote_id
+ }
+
fn from_state_proto(
pane: ViewHandle<workspace::Pane>,
project: ModelHandle<Project>,
+ remote_id: ViewId,
state: &mut Option<proto::view::Variant>,
cx: &mut MutableAppContext,
) -> Option<Task<Result<ViewHandle<Self>>>> {
let Some(proto::view::Variant::Editor(_)) = state else { return None };
let Some(proto::view::Variant::Editor(state)) = state.take() else { unreachable!() };
+ let client = project.read(cx).client();
let replica_id = project.read(cx).replica_id();
let buffer_ids = state
.excerpts
@@ -63,13 +71,13 @@ impl FollowableItem for Editor {
let mut buffers = futures::future::try_join_all(buffers).await?;
let editor = pane.read_with(&cx, |pane, cx| {
let mut editors = pane.items_of_type::<Self>();
- if state.singleton && buffers.len() == 1 {
- editors.find(|editor| {
- editor.read(cx).buffer.read(cx).as_singleton().as_ref() == Some(&buffers[0])
- })
- } else {
- None
- }
+ editors.find(|editor| {
+ editor.remote_id(&client, cx) == Some(remote_id)
+ || state.singleton
+ && buffers.len() == 1
+ && editor.read(cx).buffer.read(cx).as_singleton().as_ref()
+ == Some(&buffers[0])
+ })
});
let editor = editor.unwrap_or_else(|| {
@@ -112,6 +120,7 @@ impl FollowableItem for Editor {
});
editor.update(&mut cx, |editor, cx| {
+ editor.remote_id = Some(remote_id);
let buffer = editor.buffer.read(cx).read(cx);
let selections = state
.selections
@@ -796,7 +796,7 @@ message Follow {
}
message FollowResponse {
- optional uint64 active_view_id = 1;
+ optional ViewId active_view_id = 1;
repeated View views = 2;
}
@@ -824,13 +824,18 @@ message GetPrivateUserInfoResponse {
// Entities
+message ViewId {
+ uint32 creator = 1;
+ uint64 id = 2;
+}
+
message UpdateActiveView {
- optional uint64 id = 1;
+ optional ViewId id = 1;
optional uint32 leader_id = 2;
}
message UpdateView {
- uint64 id = 1;
+ ViewId id = 1;
optional uint32 leader_id = 2;
oneof variant {
@@ -848,7 +853,7 @@ message UpdateView {
}
message View {
- uint64 id = 1;
+ ViewId id = 1;
optional uint32 leader_id = 2;
oneof variant {
@@ -5,12 +5,15 @@ use std::{
fmt,
path::PathBuf,
rc::Rc,
- sync::atomic::{AtomicBool, Ordering},
+ sync::{
+ atomic::{AtomicBool, Ordering},
+ Arc,
+ },
time::Duration,
};
use anyhow::Result;
-use client::proto;
+use client::{proto, Client};
use gpui::{
AnyViewHandle, AppContext, ElementBox, ModelHandle, MutableAppContext, Task, View, ViewContext,
ViewHandle, WeakViewHandle,
@@ -23,7 +26,8 @@ use util::ResultExt;
use crate::{
pane, persistence::model::ItemId, searchable::SearchableItemHandle, DelayedDebouncedEditAction,
- FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
+ FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace,
+ WorkspaceId,
};
#[derive(Eq, PartialEq, Hash)]
@@ -278,7 +282,9 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
if let Some(message) = followed_item.to_state_proto(cx) {
workspace.update_followers(
proto::update_followers::Variant::CreateView(proto::View {
- id: followed_item.id() as u64,
+ id: followed_item
+ .remote_id(&workspace.client, cx)
+ .map(|id| id.to_proto()),
variant: Some(message),
leader_id: workspace.leader_for_pane(&pane).map(|id| id.0),
}),
@@ -332,7 +338,9 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
this.update_followers(
proto::update_followers::Variant::UpdateView(
proto::UpdateView {
- id: item.id() as u64,
+ id: item
+ .remote_id(&this.client, cx)
+ .map(|id| id.to_proto()),
variant: pending_update.borrow_mut().take(),
leader_id: leader_id.map(|id| id.0),
},
@@ -584,10 +592,12 @@ pub trait ProjectItem: Item {
}
pub trait FollowableItem: Item {
+ fn remote_id(&self) -> Option<ViewId>;
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
fn from_state_proto(
pane: ViewHandle<Pane>,
project: ModelHandle<Project>,
+ id: ViewId,
state: &mut Option<proto::view::Variant>,
cx: &mut MutableAppContext,
) -> Option<Task<Result<ViewHandle<Self>>>>;
@@ -609,6 +619,7 @@ pub trait FollowableItem: Item {
}
pub trait FollowableItemHandle: ItemHandle {
+ fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId>;
fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut MutableAppContext);
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
fn add_event_to_update_proto(
@@ -627,6 +638,15 @@ pub trait FollowableItemHandle: ItemHandle {
}
impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
+ fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId> {
+ self.read(cx).remote_id().or_else(|| {
+ client.peer_id().map(|creator| ViewId {
+ creator,
+ id: self.id() as u64,
+ })
+ })
+ }
+
fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut MutableAppContext) {
self.update(cx, |this, cx| {
this.set_leader_replica_id(leader_replica_id, cx)
@@ -318,6 +318,7 @@ pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
type FollowableItemBuilder = fn(
ViewHandle<Pane>,
ModelHandle<Project>,
+ ViewId,
&mut Option<proto::view::Variant>,
&mut MutableAppContext,
) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>>;
@@ -333,8 +334,8 @@ pub fn register_followable_item<I: FollowableItem>(cx: &mut MutableAppContext) {
builders.insert(
TypeId::of::<I>(),
(
- |pane, project, state, cx| {
- I::from_state_proto(pane, project, state, cx).map(|task| {
+ |pane, project, id, state, cx| {
+ I::from_state_proto(pane, project, id, state, cx).map(|task| {
cx.foreground()
.spawn(async move { Ok(Box::new(task.await?) as Box<_>) })
})
@@ -496,6 +497,12 @@ pub struct Workspace {
_observe_current_user: Task<()>,
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ViewId {
+ pub creator: PeerId,
+ pub id: u64,
+}
+
#[derive(Default)]
struct LeaderState {
followers: HashSet<PeerId>,
@@ -505,8 +512,8 @@ type FollowerStatesByLeader = HashMap<PeerId, HashMap<ViewHandle<Pane>, Follower
#[derive(Default)]
struct FollowerState {
- active_view_id: Option<u64>,
- items_by_leader_view_id: HashMap<u64, Box<dyn FollowableItemHandle>>,
+ active_view_id: Option<ViewId>,
+ items_by_leader_view_id: HashMap<ViewId, Box<dyn FollowableItemHandle>>,
}
impl Workspace {
@@ -1454,7 +1461,11 @@ impl Workspace {
self.update_followers(
proto::update_followers::Variant::UpdateActiveView(proto::UpdateActiveView {
- id: self.active_item(cx).map(|item| item.id() as u64),
+ id: self.active_item(cx).and_then(|item| {
+ item.to_followable_item_handle(cx)?
+ .remote_id(&self.client, cx)
+ .map(|id| id.to_proto())
+ }),
leader_id: self.leader_for_pane(&pane).map(|id| id.0),
}),
cx,
@@ -1643,7 +1654,7 @@ impl Workspace {
.get_mut(&leader_id)
.and_then(|states_by_pane| states_by_pane.get_mut(&pane))
.ok_or_else(|| anyhow!("following interrupted"))?;
- state.active_view_id = response.active_view_id;
+ state.active_view_id = response.active_view_id.map(ViewId::from_proto);
Ok::<_, anyhow::Error>(())
})?;
Self::add_views_from_leader(
@@ -1891,14 +1902,18 @@ impl Workspace {
mut cx: AsyncAppContext,
) -> Result<proto::FollowResponse> {
this.update(&mut cx, |this, cx| {
+ let client = &this.client;
this.leader_state
.followers
.insert(envelope.original_sender_id()?);
- let active_view_id = this
- .active_item(cx)
- .and_then(|i| i.to_followable_item_handle(cx))
- .map(|i| i.id() as u64);
+ let active_view_id = this.active_item(cx).and_then(|i| {
+ Some(
+ i.to_followable_item_handle(cx)?
+ .remote_id(client, cx)?
+ .to_proto(),
+ )
+ });
Ok(proto::FollowResponse {
active_view_id,
views: this
@@ -1909,11 +1924,11 @@ impl Workspace {
pane.read(cx).items().filter_map({
let cx = &cx;
move |item| {
- let id = item.id() as u64;
let item = item.to_followable_item_handle(cx)?;
+ let id = item.remote_id(client, cx)?.to_proto();
let variant = item.to_state_proto(cx)?;
Some(proto::View {
- id,
+ id: Some(id),
leader_id,
variant: Some(variant),
})
@@ -1964,7 +1979,8 @@ impl Workspace {
this.update(cx, |this, _| {
if let Some(state) = this.follower_states_by_leader.get_mut(&leader_id) {
for state in state.values_mut() {
- state.active_view_id = update_active_view.id;
+ state.active_view_id =
+ update_active_view.id.clone().map(ViewId::from_proto);
}
}
});
@@ -1973,12 +1989,18 @@ impl Workspace {
let variant = update_view
.variant
.ok_or_else(|| anyhow!("missing update view variant"))?;
+ let id = update_view
+ .id
+ .ok_or_else(|| anyhow!("missing update view id"))?;
let mut tasks = Vec::new();
this.update(cx, |this, cx| {
let project = this.project.clone();
if let Some(state) = this.follower_states_by_leader.get_mut(&leader_id) {
for state in state.values_mut() {
- if let Some(item) = state.items_by_leader_view_id.get(&update_view.id) {
+ if let Some(item) = state
+ .items_by_leader_view_id
+ .get(&ViewId::from_proto(id.clone()))
+ {
tasks.push(item.apply_update_proto(&project, variant.clone(), cx));
}
}
@@ -2031,16 +2053,19 @@ impl Workspace {
let mut item_tasks = Vec::new();
let mut leader_view_ids = Vec::new();
for view in &views {
+ let Some(id) = &view.id else { continue };
+ let id = ViewId::from_proto(id.clone());
let mut variant = view.variant.clone();
if variant.is_none() {
Err(anyhow!("missing variant"))?;
}
for build_item in &item_builders {
- let task =
- cx.update(|cx| build_item(pane.clone(), project.clone(), &mut variant, cx));
+ let task = cx.update(|cx| {
+ build_item(pane.clone(), project.clone(), id, &mut variant, cx)
+ });
if let Some(task) = task {
item_tasks.push(task);
- leader_view_ids.push(view.id);
+ leader_view_ids.push(id);
break;
} else {
assert!(variant.is_some());
@@ -2561,6 +2586,22 @@ impl View for Workspace {
}
}
+impl ViewId {
+ pub(crate) fn from_proto(message: proto::ViewId) -> Self {
+ Self {
+ creator: PeerId(message.creator),
+ id: message.id,
+ }
+ }
+
+ pub(crate) fn to_proto(&self) -> proto::ViewId {
+ proto::ViewId {
+ creator: self.creator.0,
+ id: self.id,
+ }
+ }
+}
+
pub trait WorkspaceHandle {
fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
}