This commit is contained in:
Max Brunsfeld 2022-03-17 17:53:49 -07:00
parent eda06ee408
commit 0fdaa1d715
10 changed files with 245 additions and 58 deletions

1
Cargo.lock generated
View file

@ -1629,6 +1629,7 @@ dependencies = [
"postage", "postage",
"project", "project",
"rand 0.8.3", "rand 0.8.3",
"rpc",
"serde", "serde",
"smallvec", "smallvec",
"smol", "smol",

View file

@ -181,7 +181,7 @@ impl Entity for Channel {
impl Channel { impl Channel {
pub fn init(rpc: &Arc<Client>) { pub fn init(rpc: &Arc<Client>) {
rpc.add_entity_message_handler(Self::handle_message_sent); rpc.add_model_message_handler(Self::handle_message_sent);
} }
pub fn new( pub fn new(

View file

@ -13,8 +13,8 @@ use async_tungstenite::tungstenite::{
}; };
use futures::{future::LocalBoxFuture, FutureExt, StreamExt}; use futures::{future::LocalBoxFuture, FutureExt, StreamExt};
use gpui::{ use gpui::{
action, AnyModelHandle, AnyWeakModelHandle, AsyncAppContext, Entity, ModelContext, ModelHandle, action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AsyncAppContext,
MutableAppContext, Task, Entity, ModelContext, ModelHandle, MutableAppContext, Task, View, ViewContext, ViewHandle,
}; };
use http::HttpClient; use http::HttpClient;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -139,16 +139,16 @@ struct ClientState {
entity_id_extractors: HashMap<TypeId, Box<dyn Send + Sync + Fn(&dyn AnyTypedEnvelope) -> u64>>, entity_id_extractors: HashMap<TypeId, Box<dyn Send + Sync + Fn(&dyn AnyTypedEnvelope) -> u64>>,
_reconnect_task: Option<Task<()>>, _reconnect_task: Option<Task<()>>,
reconnect_interval: Duration, reconnect_interval: Duration,
models_by_entity_type_and_remote_id: HashMap<(TypeId, u64), AnyWeakModelHandle>, entities_by_type_and_remote_id: HashMap<(TypeId, u64), AnyWeakEntityHandle>,
models_by_message_type: HashMap<TypeId, AnyWeakModelHandle>, models_by_message_type: HashMap<TypeId, AnyWeakModelHandle>,
model_types_by_message_type: HashMap<TypeId, TypeId>, entity_types_by_message_type: HashMap<TypeId, TypeId>,
message_handlers: HashMap< message_handlers: HashMap<
TypeId, TypeId,
Arc< Arc<
dyn Send dyn Send
+ Sync + Sync
+ Fn( + Fn(
AnyModelHandle, AnyEntityHandle,
Box<dyn AnyTypedEnvelope>, Box<dyn AnyTypedEnvelope>,
AsyncAppContext, AsyncAppContext,
) -> LocalBoxFuture<'static, Result<()>>, ) -> LocalBoxFuture<'static, Result<()>>,
@ -156,6 +156,16 @@ struct ClientState {
>, >,
} }
enum AnyWeakEntityHandle {
Model(AnyWeakModelHandle),
View(AnyWeakViewHandle),
}
enum AnyEntityHandle {
Model(AnyModelHandle),
View(AnyViewHandle),
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Credentials { pub struct Credentials {
pub user_id: u64, pub user_id: u64,
@ -171,8 +181,8 @@ impl Default for ClientState {
_reconnect_task: None, _reconnect_task: None,
reconnect_interval: Duration::from_secs(5), reconnect_interval: Duration::from_secs(5),
models_by_message_type: Default::default(), models_by_message_type: Default::default(),
models_by_entity_type_and_remote_id: Default::default(), entities_by_type_and_remote_id: Default::default(),
model_types_by_message_type: Default::default(), entity_types_by_message_type: Default::default(),
message_handlers: Default::default(), message_handlers: Default::default(),
} }
} }
@ -195,13 +205,13 @@ impl Drop for Subscription {
Subscription::Entity { client, id } => { Subscription::Entity { client, id } => {
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
let mut state = client.state.write(); let mut state = client.state.write();
let _ = state.models_by_entity_type_and_remote_id.remove(id); let _ = state.entities_by_type_and_remote_id.remove(id);
} }
} }
Subscription::Message { client, id } => { Subscription::Message { client, id } => {
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
let mut state = client.state.write(); let mut state = client.state.write();
let _ = state.model_types_by_message_type.remove(id); let _ = state.entity_types_by_message_type.remove(id);
let _ = state.message_handlers.remove(id); let _ = state.message_handlers.remove(id);
} }
} }
@ -239,7 +249,7 @@ impl Client {
state._reconnect_task.take(); state._reconnect_task.take();
state.message_handlers.clear(); state.message_handlers.clear();
state.models_by_message_type.clear(); state.models_by_message_type.clear();
state.models_by_entity_type_and_remote_id.clear(); state.entities_by_type_and_remote_id.clear();
state.entity_id_extractors.clear(); state.entity_id_extractors.clear();
self.peer.reset(); self.peer.reset();
} }
@ -313,6 +323,23 @@ impl Client {
} }
} }
pub fn add_view_for_remote_entity<T: View>(
self: &Arc<Self>,
remote_id: u64,
cx: &mut ViewContext<T>,
) -> Subscription {
let handle = AnyViewHandle::from(cx.handle());
let mut state = self.state.write();
let id = (TypeId::of::<T>(), remote_id);
state
.entities_by_type_and_remote_id
.insert(id, AnyWeakEntityHandle::View(handle.downgrade()));
Subscription::Entity {
client: Arc::downgrade(self),
id,
}
}
pub fn add_model_for_remote_entity<T: Entity>( pub fn add_model_for_remote_entity<T: Entity>(
self: &Arc<Self>, self: &Arc<Self>,
remote_id: u64, remote_id: u64,
@ -322,8 +349,8 @@ impl Client {
let mut state = self.state.write(); let mut state = self.state.write();
let id = (TypeId::of::<T>(), remote_id); let id = (TypeId::of::<T>(), remote_id);
state state
.models_by_entity_type_and_remote_id .entities_by_type_and_remote_id
.insert(id, handle.downgrade()); .insert(id, AnyWeakEntityHandle::Model(handle.downgrade()));
Subscription::Entity { Subscription::Entity {
client: Arc::downgrade(self), client: Arc::downgrade(self),
id, id,
@ -355,6 +382,11 @@ impl Client {
let prev_handler = state.message_handlers.insert( let prev_handler = state.message_handlers.insert(
message_type_id, message_type_id,
Arc::new(move |handle, envelope, cx| { Arc::new(move |handle, envelope, cx| {
let handle = if let AnyEntityHandle::Model(handle) = handle {
handle
} else {
unreachable!();
};
let model = handle.downcast::<E>().unwrap(); let model = handle.downcast::<E>().unwrap();
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap(); let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
@ -374,7 +406,60 @@ impl Client {
} }
} }
pub fn add_entity_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H) pub fn add_view_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
where
M: EntityMessage,
E: View,
H: 'static
+ Send
+ Sync
+ Fn(ViewHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<()>>,
{
let entity_type_id = TypeId::of::<E>();
let message_type_id = TypeId::of::<M>();
let client = Arc::downgrade(self);
let mut state = self.state.write();
state
.entity_types_by_message_type
.insert(message_type_id, entity_type_id);
state
.entity_id_extractors
.entry(message_type_id)
.or_insert_with(|| {
Box::new(|envelope| {
let envelope = envelope
.as_any()
.downcast_ref::<TypedEnvelope<M>>()
.unwrap();
envelope.payload.remote_entity_id()
})
});
let prev_handler = state.message_handlers.insert(
message_type_id,
Arc::new(move |handle, envelope, cx| {
let handle = if let AnyEntityHandle::View(handle) = handle {
handle
} else {
unreachable!();
};
let model = handle.downcast::<E>().unwrap();
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
if let Some(client) = client.upgrade() {
handler(model, *envelope, client.clone(), cx).boxed_local()
} else {
async move { Ok(()) }.boxed_local()
}
}),
);
if prev_handler.is_some() {
panic!("registered handler for the same message twice");
}
}
pub fn add_model_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
where where
M: EntityMessage, M: EntityMessage,
E: Entity, E: Entity,
@ -390,7 +475,7 @@ impl Client {
let client = Arc::downgrade(self); let client = Arc::downgrade(self);
let mut state = self.state.write(); let mut state = self.state.write();
state state
.model_types_by_message_type .entity_types_by_message_type
.insert(message_type_id, model_type_id); .insert(message_type_id, model_type_id);
state state
.entity_id_extractors .entity_id_extractors
@ -408,9 +493,15 @@ impl Client {
let prev_handler = state.message_handlers.insert( let prev_handler = state.message_handlers.insert(
message_type_id, message_type_id,
Arc::new(move |handle, envelope, cx| { Arc::new(move |handle, envelope, cx| {
let model = handle.downcast::<E>().unwrap();
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
if let Some(client) = client.upgrade() { if let Some(client) = client.upgrade() {
let model = handle.downcast::<E>().unwrap();
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
let handle = if let AnyEntityHandle::Model(handle) = handle {
handle
} else {
unreachable!();
};
handler(model, *envelope, client.clone(), cx).boxed_local() handler(model, *envelope, client.clone(), cx).boxed_local()
} else { } else {
async move { Ok(()) }.boxed_local() async move { Ok(()) }.boxed_local()
@ -432,7 +523,7 @@ impl Client {
+ Fn(ModelHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F, + Fn(ModelHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<M::Response>>, F: 'static + Future<Output = Result<M::Response>>,
{ {
self.add_entity_message_handler(move |model, envelope, client, cx| { self.add_model_message_handler(move |model, envelope, client, cx| {
let receipt = envelope.receipt(); let receipt = envelope.receipt();
let response = handler(model, envelope, client.clone(), cx); let response = handler(model, envelope, client.clone(), cx);
async move { async move {
@ -561,24 +652,26 @@ impl Client {
.models_by_message_type .models_by_message_type
.get(&payload_type_id) .get(&payload_type_id)
.and_then(|model| model.upgrade(&cx)) .and_then(|model| model.upgrade(&cx))
.map(AnyEntityHandle::Model)
.or_else(|| { .or_else(|| {
let model_type_id = let entity_type_id =
*state.model_types_by_message_type.get(&payload_type_id)?; *state.entity_types_by_message_type.get(&payload_type_id)?;
let entity_id = state let entity_id = state
.entity_id_extractors .entity_id_extractors
.get(&message.payload_type_id()) .get(&message.payload_type_id())
.map(|extract_entity_id| { .map(|extract_entity_id| {
(extract_entity_id)(message.as_ref()) (extract_entity_id)(message.as_ref())
})?; })?;
let model = state
.models_by_entity_type_and_remote_id let entity = state
.get(&(model_type_id, entity_id))?; .entities_by_type_and_remote_id
if let Some(model) = model.upgrade(&cx) { .get(&(entity_type_id, entity_id))?;
Some(model) if let Some(entity) = entity.upgrade(&cx) {
Some(entity)
} else { } else {
state state
.models_by_entity_type_and_remote_id .entities_by_type_and_remote_id
.remove(&(model_type_id, entity_id)); .remove(&(entity_type_id, entity_id));
None None
} }
}); });
@ -891,6 +984,15 @@ impl Client {
} }
} }
impl AnyWeakEntityHandle {
fn upgrade(&self, cx: &AsyncAppContext) -> Option<AnyEntityHandle> {
match self {
AnyWeakEntityHandle::Model(handle) => handle.upgrade(cx).map(AnyEntityHandle::Model),
AnyWeakEntityHandle::View(handle) => handle.upgrade(cx).map(AnyEntityHandle::View),
}
}
}
fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credentials> { fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credentials> {
if IMPERSONATE_LOGIN.is_some() { if IMPERSONATE_LOGIN.is_some() {
return None; return None;
@ -994,7 +1096,7 @@ mod tests {
let (done_tx1, mut done_rx1) = smol::channel::unbounded(); let (done_tx1, mut done_rx1) = smol::channel::unbounded();
let (done_tx2, mut done_rx2) = smol::channel::unbounded(); let (done_tx2, mut done_rx2) = smol::channel::unbounded();
client.add_entity_message_handler( client.add_model_message_handler(
move |model: ModelHandle<Model>, _: TypedEnvelope<proto::UnshareProject>, _, cx| { move |model: ModelHandle<Model>, _: TypedEnvelope<proto::UnshareProject>, _, cx| {
match model.read_with(&cx, |model, _| model.id) { match model.read_with(&cx, |model, _| model.id) {
1 => done_tx1.try_send(()).unwrap(), 1 => done_tx1.try_send(()).unwrap(),

View file

@ -27,6 +27,7 @@ gpui = { path = "../gpui" }
language = { path = "../language" } language = { path = "../language" }
lsp = { path = "../lsp" } lsp = { path = "../lsp" }
project = { path = "../project" } project = { path = "../project" }
rpc = { path = "../rpc" }
snippet = { path = "../snippet" } snippet = { path = "../snippet" }
sum_tree = { path = "../sum_tree" } sum_tree = { path = "../sum_tree" }
theme = { path = "../theme" } theme = { path = "../theme" }

View file

@ -6,13 +6,34 @@ use gpui::{
}; };
use language::{Bias, Buffer, Diagnostic, File as _}; use language::{Bias, Buffer, Diagnostic, File as _};
use project::{File, Project, ProjectEntryId, ProjectPath}; use project::{File, Project, ProjectEntryId, ProjectPath};
use std::fmt::Write; use rpc::proto;
use std::path::PathBuf; use std::{fmt::Write, path::PathBuf};
use text::{Point, Selection}; use text::{Point, Selection};
use util::ResultExt; use util::ResultExt;
use workspace::{Item, ItemHandle, ItemNavHistory, ProjectItem, Settings, StatusItemView}; use workspace::{
FollowedItem, Item, ItemHandle, ItemNavHistory, ProjectItem, Settings, StatusItemView,
};
impl FollowedItem for Editor {
fn for_state_message(
pane: ViewHandle<workspace::Pane>,
project: ModelHandle<Project>,
state: &mut Option<proto::view::Variant>,
cx: &mut gpui::MutableAppContext,
) -> Option<Task<Result<Box<dyn ItemHandle>>>> {
todo!()
}
fn to_state_message(&self, cx: &mut gpui::MutableAppContext) -> proto::view::Variant {
todo!()
}
}
impl Item for Editor { impl Item for Editor {
fn as_followed(&self) -> Option<&dyn FollowedItem> {
Some(self)
}
fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) { fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) {
if let Some(data) = data.downcast_ref::<NavigationData>() { if let Some(data) = data.downcast_ref::<NavigationData>() {
let buffer = self.buffer.read(cx).read(cx); let buffer = self.buffer.read(cx).read(cx);

View file

@ -124,6 +124,7 @@ pub enum Event {
DiskBasedDiagnosticsUpdated, DiskBasedDiagnosticsUpdated,
DiskBasedDiagnosticsFinished, DiskBasedDiagnosticsFinished,
DiagnosticsUpdated(ProjectPath), DiagnosticsUpdated(ProjectPath),
RemoteIdChanged(Option<u64>),
} }
enum LanguageServerEvent { enum LanguageServerEvent {
@ -253,19 +254,19 @@ impl ProjectEntryId {
impl Project { impl Project {
pub fn init(client: &Arc<Client>) { pub fn init(client: &Arc<Client>) {
client.add_entity_message_handler(Self::handle_add_collaborator); client.add_model_message_handler(Self::handle_add_collaborator);
client.add_entity_message_handler(Self::handle_buffer_reloaded); client.add_model_message_handler(Self::handle_buffer_reloaded);
client.add_entity_message_handler(Self::handle_buffer_saved); client.add_model_message_handler(Self::handle_buffer_saved);
client.add_entity_message_handler(Self::handle_start_language_server); client.add_model_message_handler(Self::handle_start_language_server);
client.add_entity_message_handler(Self::handle_update_language_server); client.add_model_message_handler(Self::handle_update_language_server);
client.add_entity_message_handler(Self::handle_remove_collaborator); client.add_model_message_handler(Self::handle_remove_collaborator);
client.add_entity_message_handler(Self::handle_register_worktree); client.add_model_message_handler(Self::handle_register_worktree);
client.add_entity_message_handler(Self::handle_unregister_worktree); client.add_model_message_handler(Self::handle_unregister_worktree);
client.add_entity_message_handler(Self::handle_unshare_project); client.add_model_message_handler(Self::handle_unshare_project);
client.add_entity_message_handler(Self::handle_update_buffer_file); client.add_model_message_handler(Self::handle_update_buffer_file);
client.add_entity_message_handler(Self::handle_update_buffer); client.add_model_message_handler(Self::handle_update_buffer);
client.add_entity_message_handler(Self::handle_update_diagnostic_summary); client.add_model_message_handler(Self::handle_update_diagnostic_summary);
client.add_entity_message_handler(Self::handle_update_worktree); client.add_model_message_handler(Self::handle_update_worktree);
client.add_entity_request_handler(Self::handle_apply_additional_edits_for_completion); client.add_entity_request_handler(Self::handle_apply_additional_edits_for_completion);
client.add_entity_request_handler(Self::handle_apply_code_action); client.add_entity_request_handler(Self::handle_apply_code_action);
client.add_entity_request_handler(Self::handle_format_buffers); client.add_entity_request_handler(Self::handle_format_buffers);
@ -566,6 +567,7 @@ impl Project {
self.subscriptions self.subscriptions
.push(self.client.add_model_for_remote_entity(remote_id, cx)); .push(self.client.add_model_for_remote_entity(remote_id, cx));
} }
cx.emit(Event::RemoteIdChanged(remote_id))
} }
pub fn remote_id(&self) -> Option<u64> { pub fn remote_id(&self) -> Option<u64> {

View file

@ -4563,6 +4563,9 @@ mod tests {
Channel::init(&client); Channel::init(&client);
Project::init(&client); Project::init(&client);
cx.update(|cx| {
workspace::init(&client, cx);
});
let peer_id = PeerId(connection_id_rx.next().await.unwrap().0); let peer_id = PeerId(connection_id_rx.next().await.unwrap().0);
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));

View file

@ -7,7 +7,9 @@ pub mod sidebar;
mod status_bar; mod status_bar;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use client::{proto, Authenticate, ChannelList, Client, PeerId, User, UserStore}; use client::{
proto, Authenticate, ChannelList, Client, PeerId, Subscription, TypedEnvelope, User, UserStore,
};
use clock::ReplicaId; use clock::ReplicaId;
use collections::HashMap; use collections::HashMap;
use gpui::{ use gpui::{
@ -18,9 +20,9 @@ use gpui::{
json::{self, to_string_pretty, ToJson}, json::{self, to_string_pretty, ToJson},
keymap::Binding, keymap::Binding,
platform::{CursorStyle, WindowOptions}, platform::{CursorStyle, WindowOptions},
AnyModelHandle, AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelHandle, AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Entity, ImageData,
MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext, ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View,
ViewHandle, WeakViewHandle, ViewContext, ViewHandle, WeakViewHandle,
}; };
use language::LanguageRegistry; use language::LanguageRegistry;
use log::error; use log::error;
@ -64,7 +66,7 @@ action!(JoinProject, JoinProjectParams);
action!(Save); action!(Save);
action!(DebugElements); action!(DebugElements);
pub fn init(cx: &mut MutableAppContext) { pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
pane::init(cx); pane::init(cx);
menu::init(cx); menu::init(cx);
@ -108,6 +110,9 @@ pub fn init(cx: &mut MutableAppContext) {
None, None,
), ),
]); ]);
client.add_entity_request_handler(Workspace::handle_follow);
client.add_model_message_handler(Workspace::handle_unfollow);
} }
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) { pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
@ -119,7 +124,7 @@ pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
}); });
} }
pub fn register_followed_item<I: FollowedItem>(cx: &mut MutableAppContext) { pub fn register_followed_item<I: FollowedItem + Item>(cx: &mut MutableAppContext) {
cx.update_default_global(|builders: &mut FollowedItemBuilders, _| { cx.update_default_global(|builders: &mut FollowedItemBuilders, _| {
builders.push(I::for_state_message) builders.push(I::for_state_message)
}); });
@ -153,6 +158,9 @@ pub struct JoinProjectParams {
} }
pub trait Item: View { pub trait Item: View {
fn as_followed(&self) -> Option<&dyn FollowedItem> {
None
}
fn deactivated(&mut self, _: &mut ViewContext<Self>) {} fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {} fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox; fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
@ -217,15 +225,17 @@ pub trait ProjectItem: Item {
) -> Self; ) -> Self;
} }
pub trait FollowedItem: Item { pub trait FollowedItem {
type UpdateMessage;
fn for_state_message( fn for_state_message(
pane: ViewHandle<Pane>, pane: ViewHandle<Pane>,
project: ModelHandle<Project>, project: ModelHandle<Project>,
state: &mut Option<proto::view::Variant>, state: &mut Option<proto::view::Variant>,
cx: &mut MutableAppContext, cx: &mut MutableAppContext,
) -> Option<Task<Result<Box<dyn ItemHandle>>>>; ) -> Option<Task<Result<Box<dyn ItemHandle>>>>
where
Self: Sized;
fn to_state_message(&self, cx: &mut MutableAppContext) -> proto::view::Variant;
} }
pub trait ItemHandle: 'static { pub trait ItemHandle: 'static {
@ -459,6 +469,7 @@ pub struct Workspace {
weak_self: WeakViewHandle<Self>, weak_self: WeakViewHandle<Self>,
client: Arc<Client>, client: Arc<Client>,
user_store: ModelHandle<client::UserStore>, user_store: ModelHandle<client::UserStore>,
remote_entity_subscription: Option<Subscription>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
modal: Option<AnyViewHandle>, modal: Option<AnyViewHandle>,
center: PaneGroup, center: PaneGroup,
@ -481,6 +492,17 @@ impl Workspace {
}) })
.detach(); .detach();
cx.subscribe(&params.project, move |this, project, event, cx| {
if let project::Event::RemoteIdChanged(remote_id) = event {
this.project_remote_id_changed(*remote_id, cx);
}
if project.read(cx).is_read_only() {
cx.blur();
}
cx.notify()
})
.detach();
let pane = cx.add_view(|_| Pane::new()); let pane = cx.add_view(|_| Pane::new());
let pane_id = pane.id(); let pane_id = pane.id();
cx.observe(&pane, move |me, _, cx| { cx.observe(&pane, move |me, _, cx| {
@ -517,7 +539,7 @@ impl Workspace {
cx.emit_global(WorkspaceCreated(weak_self.clone())); cx.emit_global(WorkspaceCreated(weak_self.clone()));
Workspace { let mut this = Workspace {
modal: None, modal: None,
weak_self, weak_self,
center: PaneGroup::new(pane.clone()), center: PaneGroup::new(pane.clone()),
@ -525,13 +547,16 @@ impl Workspace {
active_pane: pane.clone(), active_pane: pane.clone(),
status_bar, status_bar,
client: params.client.clone(), client: params.client.clone(),
remote_entity_subscription: None,
user_store: params.user_store.clone(), user_store: params.user_store.clone(),
fs: params.fs.clone(), fs: params.fs.clone(),
left_sidebar: Sidebar::new(Side::Left), left_sidebar: Sidebar::new(Side::Left),
right_sidebar: Sidebar::new(Side::Right), right_sidebar: Sidebar::new(Side::Right),
project: params.project.clone(), project: params.project.clone(),
_observe_current_user, _observe_current_user,
} };
this.project_remote_id_changed(this.project.read(cx).remote_id(), cx);
this
} }
pub fn weak_handle(&self) -> WeakViewHandle<Self> { pub fn weak_handle(&self) -> WeakViewHandle<Self> {
@ -1008,6 +1033,15 @@ impl Workspace {
}); });
} }
fn project_remote_id_changed(&mut self, remote_id: Option<u64>, cx: &mut ViewContext<Self>) {
if let Some(remote_id) = remote_id {
self.remote_entity_subscription =
Some(self.client.add_view_for_remote_entity(remote_id, cx));
} else {
self.remote_entity_subscription.take();
}
}
pub fn follow(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Task<Result<()>> { pub fn follow(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
if let Some(project_id) = self.project.read(cx).remote_id() { if let Some(project_id) = self.project.read(cx).remote_id() {
let request = self.client.request(proto::Follow { let request = self.client.request(proto::Follow {
@ -1271,6 +1305,29 @@ impl Workspace {
None None
} }
} }
// RPC handlers
async fn handle_follow(
this: ViewHandle<Self>,
envelope: TypedEnvelope<proto::Follow>,
_: Arc<Client>,
cx: AsyncAppContext,
) -> Result<proto::FollowResponse> {
Ok(proto::FollowResponse {
current_view_id: 0,
views: Default::default(),
})
}
async fn handle_unfollow(
this: ViewHandle<Self>,
envelope: TypedEnvelope<proto::Unfollow>,
_: Arc<Client>,
cx: AsyncAppContext,
) -> Result<()> {
Ok(())
}
} }
impl Entity for Workspace { impl Entity for Workspace {

View file

@ -69,7 +69,7 @@ fn main() {
project::Project::init(&client); project::Project::init(&client);
client::Channel::init(&client); client::Channel::init(&client);
client::init(client.clone(), cx); client::init(client.clone(), cx);
workspace::init(cx); workspace::init(&client, cx);
editor::init(cx); editor::init(cx);
go_to_line::init(cx); go_to_line::init(cx);
file_finder::init(cx); file_finder::init(cx);

View file

@ -252,7 +252,7 @@ mod tests {
async fn test_new_empty_workspace(cx: &mut TestAppContext) { async fn test_new_empty_workspace(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state); let app_state = cx.update(test_app_state);
cx.update(|cx| { cx.update(|cx| {
workspace::init(cx); workspace::init(&app_state.client, cx);
}); });
cx.dispatch_global_action(workspace::OpenNew(app_state.clone())); cx.dispatch_global_action(workspace::OpenNew(app_state.clone()));
let window_id = *cx.window_ids().first().unwrap(); let window_id = *cx.window_ids().first().unwrap();