mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 10:40:54 +00:00
WIP
This commit is contained in:
parent
eda06ee408
commit
0fdaa1d715
10 changed files with 245 additions and 58 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1629,6 +1629,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
|
"rpc",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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(¶ms.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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue