@@ -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>>,
@@ -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,
})),
@@ -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(())
}
}