Wait for buffer if it doesn't exist when deserializing a reference

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Nathan Sobo 2022-02-14 15:55:37 -07:00
parent e9250e647b
commit 28ba49b47b
2 changed files with 90 additions and 64 deletions

View file

@ -10,7 +10,7 @@ use futures::Future;
use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
use gpui::{
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task,
WeakModelHandle,
UpgradeModelHandle, WeakModelHandle,
};
use language::{
point_from_lsp,
@ -20,7 +20,7 @@ use language::{
ToLspPosition, ToOffset, ToPointUtf16, Transaction,
};
use lsp::{DiagnosticSeverity, LanguageServer};
use postage::{prelude::Stream, watch};
use postage::{broadcast, prelude::Stream, sink::Sink, watch};
use smol::block_on;
use std::{
convert::TryInto,
@ -47,6 +47,7 @@ pub struct Project {
subscriptions: Vec<client::Subscription>,
language_servers_with_diagnostics_running: isize,
open_buffers: HashMap<usize, OpenBuffer>,
opened_buffer: broadcast::Sender<()>,
loading_buffers: HashMap<
ProjectPath,
postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
@ -221,6 +222,7 @@ impl Project {
remote_id_rx,
_maintain_remote_id_task,
},
opened_buffer: broadcast::channel(1).0,
subscriptions: Vec::new(),
active_entry: None,
languages,
@ -278,6 +280,7 @@ impl Project {
worktrees: Vec::new(),
open_buffers: Default::default(),
loading_buffers: Default::default(),
opened_buffer: broadcast::channel(1).0,
shared_buffers: Default::default(),
active_entry: None,
collaborators,
@ -631,6 +634,7 @@ impl Project {
.await?;
let buffer = response.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await
})
}
@ -1271,29 +1275,27 @@ impl Project {
};
cx.spawn(|this, mut cx| async move {
let response = client.request(request).await?;
this.update(&mut cx, |this, cx| {
let mut definitions = Vec::new();
for definition in response.definitions {
let target_buffer = this.deserialize_buffer(
definition.buffer.ok_or_else(|| anyhow!("missing buffer"))?,
cx,
)?;
let target_start = definition
.target_start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let target_end = definition
.target_end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
definitions.push(Definition {
target_buffer,
target_range: target_start..target_end,
})
}
let mut definitions = Vec::new();
for definition in response.definitions {
let buffer = definition.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
let target_buffer = this
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await?;
let target_start = definition
.target_start
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target start"))?;
let target_end = definition
.target_end
.and_then(deserialize_anchor)
.ok_or_else(|| anyhow!("missing target end"))?;
definitions.push(Definition {
target_buffer,
target_range: target_start..target_end,
})
}
Ok(definitions)
})
Ok(definitions)
})
} else {
Task::ready(Ok(Default::default()))
@ -2531,20 +2533,15 @@ impl Project {
push_to_history: bool,
cx: &mut ModelContext<Self>,
) -> Task<Result<ProjectTransaction>> {
let mut project_transaction = ProjectTransaction::default();
for (buffer, transaction) in message.buffers.into_iter().zip(message.transactions) {
let buffer = match self.deserialize_buffer(buffer, cx) {
Ok(buffer) => buffer,
Err(error) => return Task::ready(Err(error)),
};
let transaction = match language::proto::deserialize_transaction(transaction) {
Ok(transaction) => transaction,
Err(error) => return Task::ready(Err(error)),
};
project_transaction.0.insert(buffer, transaction);
}
cx.spawn_weak(|_, mut cx| async move {
cx.spawn(|this, mut cx| async move {
let mut project_transaction = ProjectTransaction::default();
for (buffer, transaction) in message.buffers.into_iter().zip(message.transactions) {
let buffer = this
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await?;
let transaction = language::proto::deserialize_transaction(transaction)?;
project_transaction.0.insert(buffer, transaction);
}
for (buffer, transaction) in &project_transaction.0 {
buffer
.update(&mut cx, |buffer, _| {
@ -2588,33 +2585,60 @@ impl Project {
&mut self,
buffer: proto::Buffer,
cx: &mut ModelContext<Self>,
) -> Result<ModelHandle<Buffer>> {
match buffer.variant.ok_or_else(|| anyhow!("missing buffer"))? {
proto::buffer::Variant::Id(id) => self
.open_buffers
.get(&(id as usize))
.and_then(|buffer| buffer.upgrade(cx))
.ok_or_else(|| anyhow!("no buffer exists for id {}", id)),
proto::buffer::Variant::State(mut buffer) => {
let mut buffer_worktree = None;
let mut buffer_file = None;
if let Some(file) = buffer.file.take() {
let worktree_id = WorktreeId::from_proto(file.worktree_id);
let worktree = self
.worktree_for_id(worktree_id, cx)
.ok_or_else(|| anyhow!("no worktree found for id {}", file.worktree_id))?;
buffer_file = Some(Box::new(File::from_proto(file, worktree.clone(), cx)?)
as Box<dyn language::File>);
buffer_worktree = Some(worktree);
}
) -> Task<Result<ModelHandle<Buffer>>> {
let replica_id = self.replica_id();
let buffer = cx.add_model(|cx| {
Buffer::from_proto(self.replica_id(), buffer, buffer_file, cx).unwrap()
});
self.register_buffer(&buffer, buffer_worktree.as_ref(), cx)?;
Ok(buffer)
let mut opened_buffer_tx = self.opened_buffer.clone();
let mut opened_buffer_rx = self.opened_buffer.subscribe();
cx.spawn(|this, mut cx| async move {
match buffer.variant.ok_or_else(|| anyhow!("missing buffer"))? {
proto::buffer::Variant::Id(id) => {
let buffer = loop {
let buffer = this.read_with(&cx, |this, cx| {
this.open_buffers
.get(&(id as usize))
.and_then(|buffer| buffer.upgrade(cx))
});
if let Some(buffer) = buffer {
break buffer;
}
opened_buffer_rx
.recv()
.await
.ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
};
Ok(buffer)
}
proto::buffer::Variant::State(mut buffer) => {
let mut buffer_worktree = None;
let mut buffer_file = None;
if let Some(file) = buffer.file.take() {
this.read_with(&cx, |this, cx| {
let worktree_id = WorktreeId::from_proto(file.worktree_id);
let worktree =
this.worktree_for_id(worktree_id, cx).ok_or_else(|| {
anyhow!("no worktree found for id {}", file.worktree_id)
})?;
buffer_file =
Some(Box::new(File::from_proto(file, worktree.clone(), cx)?)
as Box<dyn language::File>);
buffer_worktree = Some(worktree);
Ok::<_, anyhow::Error>(())
})?;
}
let buffer = cx.add_model(|cx| {
Buffer::from_proto(replica_id, buffer, buffer_file, cx).unwrap()
});
this.update(&mut cx, |this, cx| {
this.register_buffer(&buffer, buffer_worktree.as_ref(), cx)
})?;
let _ = opened_buffer_tx.send(()).await;
Ok(buffer)
}
}
}
})
}
async fn handle_close_buffer(
@ -2735,7 +2759,7 @@ impl WorktreeHandle {
}
impl OpenBuffer {
pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<Buffer>> {
match self {
OpenBuffer::Loaded(handle) => handle.upgrade(cx),
OpenBuffer::Operations(_) => None,