Don't lose operations for buffers that are being opened

This commit is contained in:
Antonio Scandurra 2021-07-12 11:59:56 +02:00
parent 1813a3cc91
commit 0f399c6a01

View file

@ -728,14 +728,21 @@ impl Worktree {
#[cfg(feature = "test-support")] #[cfg(feature = "test-support")]
pub fn has_open_buffer(&self, path: impl AsRef<Path>, cx: &AppContext) -> bool { pub fn has_open_buffer(&self, path: impl AsRef<Path>, cx: &AppContext) -> bool {
let open_buffers = match self { let mut open_buffers: Box<dyn Iterator<Item = _>> = match self {
Worktree::Local(worktree) => &worktree.open_buffers, Worktree::Local(worktree) => Box::new(worktree.open_buffers.values()),
Worktree::Remote(worktree) => &worktree.open_buffers, Worktree::Remote(worktree) => {
Box::new(worktree.open_buffers.values().filter_map(|buf| {
if let RemoteBuffer::Loaded(buf) = buf {
Some(buf)
} else {
None
}
}))
}
}; };
let path = path.as_ref(); let path = path.as_ref();
open_buffers open_buffers
.values()
.find(|buffer| { .find(|buffer| {
if let Some(file) = buffer.upgrade(cx).and_then(|buffer| buffer.read(cx).file()) { if let Some(file) = buffer.upgrade(cx).and_then(|buffer| buffer.read(cx).file()) {
file.path.as_ref() == path file.path.as_ref() == path
@ -751,33 +758,43 @@ impl Worktree {
envelope: proto::UpdateBuffer, envelope: proto::UpdateBuffer,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<()> { ) -> Result<()> {
let open_buffers = match self { let buffer_id = envelope.buffer_id as usize;
Worktree::Local(worktree) => &worktree.open_buffers,
Worktree::Remote(worktree) => &worktree.open_buffers,
};
let buffer = open_buffers
.get(&(envelope.buffer_id as usize))
.and_then(|buf| buf.upgrade(&cx));
let buffer = if let Some(buffer) = buffer {
buffer
} else {
return if matches!(self, Worktree::Local(_)) {
Err(anyhow!(
"invalid buffer {} in update buffer message",
envelope.buffer_id
))
} else {
Ok(())
};
};
let ops = envelope let ops = envelope
.operations .operations
.into_iter() .into_iter()
.map(|op| op.try_into()) .map(|op| op.try_into())
.collect::<anyhow::Result<Vec<_>>>()?; .collect::<anyhow::Result<Vec<_>>>()?;
buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
match self {
Worktree::Local(worktree) => {
let buffer = worktree
.open_buffers
.get(&buffer_id)
.and_then(|buf| buf.upgrade(&cx))
.ok_or_else(|| {
anyhow!("invalid buffer {} in update buffer message", buffer_id)
})?;
buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
}
Worktree::Remote(worktree) => match worktree.open_buffers.get_mut(&buffer_id) {
Some(RemoteBuffer::Operations(pending_ops)) => pending_ops.extend(ops),
Some(RemoteBuffer::Loaded(buffer)) => {
if let Some(buffer) = buffer.upgrade(&cx) {
buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
} else {
worktree
.open_buffers
.insert(buffer_id, RemoteBuffer::Operations(ops));
}
}
None => {
worktree
.open_buffers
.insert(buffer_id, RemoteBuffer::Operations(ops));
}
},
}
Ok(()) Ok(())
} }
@ -811,8 +828,8 @@ impl Worktree {
} }
} }
fn poll_snapshot(&mut self, cx: &mut ModelContext<Worktree>) { fn poll_snapshot(&mut self, cx: &mut ModelContext<Self>) {
let update_buffers = match self { match self {
Self::Local(worktree) => { Self::Local(worktree) => {
let poll_interval = worktree.poll_interval; let poll_interval = worktree.poll_interval;
worktree.snapshot = worktree.background_snapshot.lock().clone(); worktree.snapshot = worktree.background_snapshot.lock().clone();
@ -832,93 +849,98 @@ impl Worktree {
.detach(); .detach();
worktree.poll_scheduled = true; worktree.poll_scheduled = true;
} }
false
} else { } else {
true self.update_open_buffers(cx);
} }
} }
Self::Remote(worktree) => { Self::Remote(worktree) => {
worktree.snapshot = worktree.snapshot_rx.borrow().clone(); worktree.snapshot = worktree.snapshot_rx.borrow().clone();
true self.update_open_buffers(cx);
} }
}; };
if update_buffers { cx.notify();
let mut buffers_to_delete = Vec::new(); }
for (buffer_id, buffer) in self.open_buffers() {
if let Some(buffer) = buffer.upgrade(&cx) {
buffer.update(cx, |buffer, cx| {
let buffer_is_clean = !buffer.is_dirty();
if let Some(file) = buffer.file_mut() { fn update_open_buffers(&mut self, cx: &mut ModelContext<Self>) {
let mut file_changed = false; let open_buffers: Box<dyn Iterator<Item = _>> = match &self {
Self::Local(worktree) => Box::new(worktree.open_buffers.iter()),
Self::Remote(worktree) => {
Box::new(worktree.open_buffers.iter().filter_map(|(id, buf)| {
if let RemoteBuffer::Loaded(buf) = buf {
Some((id, buf))
} else {
None
}
}))
}
};
if let Some(entry) = file let mut buffers_to_delete = Vec::new();
.entry_id for (buffer_id, buffer) in open_buffers {
.and_then(|entry_id| self.entry_for_id(entry_id)) if let Some(buffer) = buffer.upgrade(&cx) {
{ buffer.update(cx, |buffer, cx| {
if entry.path != file.path { let buffer_is_clean = !buffer.is_dirty();
file.path = entry.path.clone();
file_changed = true;
}
if entry.mtime != file.mtime { if let Some(file) = buffer.file_mut() {
file.mtime = entry.mtime; let mut file_changed = false;
file_changed = true;
if let Some(worktree) = self.as_local() { if let Some(entry) = file
if buffer_is_clean { .entry_id
let abs_path = worktree.absolutize(&file.path); .and_then(|entry_id| self.entry_for_id(entry_id))
refresh_buffer(abs_path, &worktree.fs, cx); {
} if entry.path != file.path {
} file.path = entry.path.clone();
} file_changed = true;
} else if let Some(entry) = self.entry_for_path(&file.path) { }
file.entry_id = Some(entry.id);
if entry.mtime != file.mtime {
file.mtime = entry.mtime; file.mtime = entry.mtime;
file_changed = true;
if let Some(worktree) = self.as_local() { if let Some(worktree) = self.as_local() {
if buffer_is_clean { if buffer_is_clean {
let abs_path = worktree.absolutize(&file.path); let abs_path = worktree.absolutize(&file.path);
refresh_buffer(abs_path, &worktree.fs, cx); refresh_buffer(abs_path, &worktree.fs, cx);
} }
} }
file_changed = true; }
} else if !file.is_deleted() { } else if let Some(entry) = self.entry_for_path(&file.path) {
file.entry_id = Some(entry.id);
file.mtime = entry.mtime;
if let Some(worktree) = self.as_local() {
if buffer_is_clean { if buffer_is_clean {
cx.emit(editor::buffer::Event::Dirtied); let abs_path = worktree.absolutize(&file.path);
refresh_buffer(abs_path, &worktree.fs, cx);
} }
file.entry_id = None;
file_changed = true;
} }
file_changed = true;
if file_changed { } else if !file.is_deleted() {
cx.emit(editor::buffer::Event::FileHandleChanged); if buffer_is_clean {
cx.emit(editor::buffer::Event::Dirtied);
} }
file.entry_id = None;
file_changed = true;
} }
});
} else { if file_changed {
buffers_to_delete.push(*buffer_id); cx.emit(editor::buffer::Event::FileHandleChanged);
}
}
});
} else {
buffers_to_delete.push(*buffer_id);
}
}
for buffer_id in buffers_to_delete {
match self {
Self::Local(worktree) => {
worktree.open_buffers.remove(&buffer_id);
}
Self::Remote(worktree) => {
worktree.open_buffers.remove(&buffer_id);
} }
} }
for buffer_id in buffers_to_delete {
self.open_buffers_mut().remove(&buffer_id);
}
}
cx.notify();
}
fn open_buffers(&self) -> &HashMap<usize, WeakModelHandle<Buffer>> {
match self {
Self::Local(worktree) => &worktree.open_buffers,
Self::Remote(worktree) => &worktree.open_buffers,
}
}
fn open_buffers_mut(&mut self) -> &mut HashMap<usize, WeakModelHandle<Buffer>> {
match self {
Self::Local(worktree) => &mut worktree.open_buffers,
Self::Remote(worktree) => &mut worktree.open_buffers,
} }
} }
} }
@ -1334,7 +1356,7 @@ pub struct RemoteWorktree {
rpc: rpc::Client, rpc: rpc::Client,
updates_tx: postage::mpsc::Sender<proto::UpdateWorktree>, updates_tx: postage::mpsc::Sender<proto::UpdateWorktree>,
replica_id: ReplicaId, replica_id: ReplicaId,
open_buffers: HashMap<usize, WeakModelHandle<Buffer>>, open_buffers: HashMap<usize, RemoteBuffer>,
peers: HashMap<PeerId, ReplicaId>, peers: HashMap<PeerId, ReplicaId>,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
} }
@ -1381,15 +1403,20 @@ impl RemoteWorktree {
}) })
.await?; .await?;
let remote_buffer = response.buffer.ok_or_else(|| anyhow!("empty buffer"))?; let remote_buffer = response.buffer.ok_or_else(|| anyhow!("empty buffer"))?;
let buffer_id = remote_buffer.id; let buffer_id = remote_buffer.id as usize;
let buffer = cx.add_model(|cx| { let buffer = cx.add_model(|cx| {
Buffer::from_proto(replica_id, remote_buffer, Some(file), language, cx).unwrap() Buffer::from_proto(replica_id, remote_buffer, Some(file), language, cx).unwrap()
}); });
this.update(&mut cx, |this, _| { this.update(&mut cx, |this, cx| {
let this = this.as_remote_mut().unwrap(); let this = this.as_remote_mut().unwrap();
this.open_buffers if let Some(RemoteBuffer::Operations(pending_ops)) = this
.insert(buffer_id as usize, buffer.downgrade()); .open_buffers
}); .insert(buffer_id, RemoteBuffer::Loaded(buffer.downgrade()))
{
buffer.update(cx, |buf, cx| buf.apply_ops(pending_ops, cx))?;
}
Result::<_, anyhow::Error>::Ok(())
})?;
Ok(buffer) Ok(buffer)
} }
}) })
@ -1431,6 +1458,20 @@ impl RemoteWorktree {
} }
} }
enum RemoteBuffer {
Operations(Vec<Operation>),
Loaded(WeakModelHandle<Buffer>),
}
impl RemoteBuffer {
fn upgrade(&self, cx: impl AsRef<AppContext>) -> Option<ModelHandle<Buffer>> {
match self {
Self::Operations(_) => None,
Self::Loaded(buffer) => buffer.upgrade(cx),
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct Snapshot { pub struct Snapshot {
id: usize, id: usize,
@ -2864,8 +2905,6 @@ mod remote {
rpc: &rpc::Client, rpc: &rpc::Client,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
eprintln!("got update buffer message {:?}", envelope.payload);
let message = envelope.payload; let message = envelope.payload;
rpc.state rpc.state
.read() .read()