Restructure RPC state to also keep track of remote worktrees on guests

This commit is contained in:
Antonio Scandurra
2021-06-28 15:35:36 +02:00
parent e72b4ae03d
commit 65aa9733d7
6 changed files with 133 additions and 40 deletions
+5 -1
View File
@@ -16,6 +16,7 @@ message Envelope {
OpenBufferResponse open_buffer_response = 11;
CloseBuffer close_buffer = 12;
UpdateBuffer update_buffer = 13;
RemoveGuest remove_guest = 14;
}
}
@@ -49,11 +50,14 @@ message OpenWorktreeResponse {
message AddGuest {
uint64 worktree_id = 1;
User user = 2;
uint32 replica_id = 2;
User user = 3;
}
message RemoveGuest {
uint64 worktree_id = 1;
uint32 peer_id = 2;
uint32 replica_id = 3;
}
message OpenBuffer {
+1 -1
View File
@@ -29,7 +29,7 @@ type BoxedReader = Pin<Box<dyn AsyncRead + 'static + Send>>;
pub struct ConnectionId(u32);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct PeerId(u32);
pub struct PeerId(pub u32);
struct Connection {
writer: Mutex<MessageStream<BoxedWriter>>,
+1
View File
@@ -74,6 +74,7 @@ request_message!(OpenWorktree, OpenWorktreeResponse);
request_message!(OpenBuffer, OpenBufferResponse);
message!(CloseBuffer);
message!(UpdateBuffer);
message!(RemoveGuest);
/// A stream of protobuf messages.
pub struct MessageStream<T> {
+6
View File
@@ -1398,6 +1398,12 @@ impl Buffer {
self.operations.push(operation);
}
pub fn peer_left(&mut self, replica_id: ReplicaId, cx: &mut ModelContext<Self>) {
self.selections
.retain(|set_id, _| set_id.replica_id != replica_id);
cx.notify();
}
pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
let was_dirty = self.is_dirty(cx.as_ref());
let old_version = self.version.clone();
+22 -3
View File
@@ -2,7 +2,7 @@ use super::util::SurfResultExt as _;
use crate::{editor::Buffer, language::LanguageRegistry, worktree::Worktree};
use anyhow::{anyhow, Context, Result};
use gpui::executor::Background;
use gpui::{AsyncAppContext, ModelHandle, Task};
use gpui::{AsyncAppContext, ModelHandle, Task, WeakModelHandle};
use lazy_static::lazy_static;
use postage::prelude::Stream;
use smol::lock::Mutex;
@@ -29,18 +29,37 @@ pub struct Client {
pub struct ClientState {
connection_id: Option<ConnectionId>,
pub shared_worktrees: HashMap<u64, ModelHandle<Worktree>>,
pub remote_worktrees: HashMap<u64, WeakModelHandle<Worktree>>,
pub shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
pub language_registry: Arc<LanguageRegistry>,
}
impl ClientState {
pub fn remote_worktree(
&mut self,
id: u64,
cx: &mut AsyncAppContext,
) -> Result<ModelHandle<Worktree>> {
if let Some(worktree) = self.remote_worktrees.get(&id) {
if let Some(worktree) = cx.read(|cx| worktree.upgrade(cx)) {
Ok(worktree)
} else {
self.remote_worktrees.remove(&id);
Err(anyhow!("worktree {} was dropped", id))
}
} else {
Err(anyhow!("worktree {} does not exist", id))
}
}
}
impl Client {
pub fn new(language_registry: Arc<LanguageRegistry>) -> Self {
Self {
peer: Peer::new(),
state: Arc::new(Mutex::new(ClientState {
connection_id: None,
shared_worktrees: Default::default(),
remote_worktrees: Default::default(),
shared_buffers: Default::default(),
language_registry,
})),
+98 -35
View File
@@ -47,6 +47,7 @@ pub fn init(cx: &mut MutableAppContext, rpc: rpc::Client) {
rpc.on_message(remote::open_buffer, cx);
rpc.on_message(remote::close_buffer, cx);
rpc.on_message(remote::update_buffer, cx);
rpc.on_message(remote::remove_guest, cx);
}
#[derive(Clone, Debug)]
@@ -88,17 +89,23 @@ impl Worktree {
let replica_id = open_worktree_response
.replica_id
.ok_or_else(|| anyhow!("empty replica id"))?;
Ok(cx.update(|cx| {
let worktree = cx.update(|cx| {
cx.add_model(|cx| {
Worktree::Remote(RemoteWorktree::new(
id,
worktree_message,
rpc,
rpc.clone(),
replica_id as ReplicaId,
cx,
))
})
}))
});
rpc.state
.lock()
.await
.remote_worktrees
.insert(id, worktree.downgrade());
Ok(worktree)
}
pub fn as_local(&self) -> Option<&LocalWorktree> {
@@ -165,6 +172,30 @@ impl Worktree {
.is_some()
}
pub fn buffer(&self, id: u64, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
let open_buffers = match self {
Worktree::Local(worktree) => &worktree.open_buffers,
Worktree::Remote(worktree) => &worktree.open_buffers,
};
open_buffers
.get(&(id as usize))
.and_then(|buffer| buffer.upgrade(cx))
}
pub fn buffers<'a>(
&'a self,
cx: &'a AppContext,
) -> impl 'a + Iterator<Item = ModelHandle<Buffer>> {
let open_buffers = match self {
Worktree::Local(worktree) => &worktree.open_buffers,
Worktree::Remote(worktree) => &worktree.open_buffers,
};
open_buffers
.values()
.filter_map(move |buffer| buffer.upgrade(cx))
}
pub fn save(
&self,
path: &Path,
@@ -520,8 +551,8 @@ impl LocalWorktree {
rpc.state
.lock()
.await
.shared_worktrees
.insert(share_response.worktree_id, handle);
.remote_worktrees
.insert(share_response.worktree_id, handle.downgrade());
log::info!("sharing worktree {:?}", share_response);
@@ -1784,10 +1815,9 @@ impl<'a> Iterator for ChildEntriesIter<'a> {
}
mod remote {
use std::convert::TryInto;
use super::*;
use crate::rpc::TypedEnvelope;
use std::convert::TryInto;
pub async fn open_buffer(
envelope: TypedEnvelope<proto::OpenBuffer>,
@@ -1801,8 +1831,9 @@ mod remote {
let mut state = rpc.state.lock().await;
let worktree = state
.shared_worktrees
.remote_worktrees
.get(&message.worktree_id)
.and_then(|worktree| cx.read(|cx| worktree.upgrade(cx)))
.ok_or_else(|| anyhow!("worktree {} not found", message.worktree_id))?
.clone();
@@ -1853,36 +1884,68 @@ mod remote {
rpc: &rpc::Client,
cx: &mut AsyncAppContext,
) -> anyhow::Result<()> {
let peer_id = envelope
.original_sender_id
.ok_or_else(|| anyhow!("missing original sender id"))?;
let message = envelope.payload;
if let Some(buffer) = rpc
.state
.lock()
.await
.shared_buffers
.get(&peer_id)
.and_then(|buffers| buffers.get(&message.buffer_id))
.cloned()
{
if let Err(error) = buffer.update(cx, |buffer, cx| {
let ops = message
.operations
.into_iter()
.map(|op| op.try_into())
.collect::<anyhow::Result<Vec<_>>>()?;
buffer.apply_ops(ops, cx)?;
Ok::<(), anyhow::Error>(())
}) {
log::error!("error applying buffer operations {}", error);
let mut state = rpc.state.lock().await;
match state.remote_worktree(message.worktree_id, cx) {
Ok(worktree) => {
if let Some(buffer) =
worktree.read_with(cx, |w, cx| w.buffer(message.buffer_id, cx))
{
if let Err(error) = buffer.update(cx, |buffer, cx| {
let ops = message
.operations
.into_iter()
.map(|op| op.try_into())
.collect::<anyhow::Result<Vec<_>>>()?;
buffer.apply_ops(ops, cx)?;
Ok::<(), anyhow::Error>(())
}) {
log::error!("error applying buffer operations {}", error);
}
} else {
log::error!(
"invalid buffer {} in update buffer message",
message.buffer_id
);
}
}
} else {
log::error!(
"invalid buffer {} in update buffer message",
message.buffer_id
);
Err(error) => log::error!("{}", error),
}
Ok(())
}
pub async fn remove_guest(
envelope: TypedEnvelope<proto::RemoveGuest>,
rpc: &rpc::Client,
cx: &mut AsyncAppContext,
) -> anyhow::Result<()> {
let peer_id = envelope.original_sender_id.unwrap();
let message = envelope.payload;
let mut state = rpc.state.lock().await;
match state.remote_worktree(message.worktree_id, cx) {
Ok(worktree) => {
let mut peer_buffers = state.shared_buffers.get_mut(&peer_id);
let buffers =
worktree.read_with(cx, |worktree, cx| worktree.buffers(cx).collect::<Vec<_>>());
for buffer in buffers {
buffer.update(cx, |buffer, cx| {
buffer.peer_left(message.replica_id as ReplicaId, cx);
if let Some(peer_buffers) = &mut peer_buffers {
peer_buffers.remove(&buffer.remote_id());
}
});
}
if let Some(peer_buffers) = peer_buffers {
if peer_buffers.is_empty() {
state.shared_buffers.remove(&peer_id);
}
}
}
Err(error) => log::error!("{}", error),
}
Ok(())
}
}