Respond to proto::OpenBuffer requests

This commit is contained in:
Antonio Scandurra
2021-06-23 16:20:02 +02:00
parent c5e08b6548
commit 02321af08a
9 changed files with 260 additions and 123 deletions
+22 -27
View File
@@ -12,11 +12,9 @@ message Envelope {
ShareWorktreeResponse share_worktree_response = 7;
OpenWorktree open_worktree = 8;
OpenWorktreeResponse open_worktree_response = 9;
OpenFile open_file = 10;
OpenFileResponse open_file_response = 11;
CloseFile close_file = 12;
OpenBuffer open_buffer = 13;
OpenBufferResponse open_buffer_response = 14;
OpenBuffer open_buffer = 10;
OpenBufferResponse open_buffer_response = 11;
CloseBuffer close_buffer = 12;
}
}
@@ -57,28 +55,18 @@ message RemoveGuest {
uint64 worktree_id = 1;
}
message OpenFile {
message OpenBuffer {
uint64 worktree_id = 1;
string path = 2;
}
message OpenFileResponse {
uint64 id = 1;
uint64 mtime = 2;
}
message CloseFile {
uint64 worktree_id = 1;
uint64 id = 2;
}
message OpenBuffer {
uint64 worktree_id = 1;
uint64 id = 2;
}
message OpenBufferResponse {
Buffer buffer = 1;
uint64 buffer_id = 1;
Buffer buffer = 2;
}
message CloseBuffer {
uint64 id = 1;
}
message User {
@@ -116,15 +104,22 @@ message Operation {
uint32 local_timestamp = 2;
uint32 lamport_timestamp = 3;
repeated VectorClockEntry version = 4;
repeated Range ranges = 5;
optional string new_text = 6;
}
}
message VectorClockEntry {
uint32 replica_id = 1;
uint32 timestamp = 2;
}
message VectorClockEntry {
uint32 replica_id = 1;
uint32 timestamp = 2;
}
message Timestamp {
uint64 seconds = 1;
uint32 nanos = 2;
}
}
message Range {
uint64 start = 1;
uint64 end = 2;
}
+4 -2
View File
@@ -432,9 +432,10 @@ mod tests {
};
let request3 = proto::OpenBuffer {
worktree_id: 1,
id: 2,
path: "path/two".to_string(),
};
let response3 = proto::OpenBufferResponse {
buffer_id: 2,
buffer: Some(proto::Buffer {
content: "path/two content".to_string(),
history: vec![],
@@ -442,9 +443,10 @@ mod tests {
};
let request4 = proto::OpenBuffer {
worktree_id: 2,
id: 1,
path: "path/one".to_string(),
};
let response4 = proto::OpenBufferResponse {
buffer_id: 1,
buffer: Some(proto::Buffer {
content: "path/one content".to_string(),
history: vec![],
+2 -3
View File
@@ -71,9 +71,8 @@ macro_rules! request_message {
request_message!(Auth, AuthResponse);
request_message!(ShareWorktree, ShareWorktreeResponse);
request_message!(OpenWorktree, OpenWorktreeResponse);
request_message!(OpenFile, OpenFileResponse);
message!(CloseFile);
request_message!(OpenBuffer, OpenBufferResponse);
message!(CloseBuffer);
/// A stream of protobuf messages.
pub struct MessageStream<T> {
@@ -169,7 +168,7 @@ mod tests {
let message2 = OpenBuffer {
worktree_id: 0,
id: 1,
path: "some/path".to_string(),
}
.into_envelope(5, None, None);
+89
View File
@@ -11,6 +11,7 @@ use seahash::SeaHasher;
pub use selection::*;
use similar::{ChangeTag, TextDiff};
use tree_sitter::{InputEdit, Parser, QueryCursor};
use zed_rpc::proto;
use crate::{
language::{Language, Tree},
@@ -493,6 +494,94 @@ impl Buffer {
}
}
pub fn from_proto(
replica_id: ReplicaId,
remote_buffer: proto::Buffer,
file: Option<File>,
language: Option<Arc<Language>>,
cx: &mut ModelContext<Self>,
) -> Result<Self> {
let mut buffer = Buffer::build(
replica_id,
History::new(remote_buffer.content.into()),
file,
language,
cx,
);
let ops = remote_buffer
.history
.into_iter()
.filter_map(|op| op.variant)
.map(|op| match op {
proto::operation::Variant::Edit(edit) => {
let mut version = time::Global::new();
for entry in edit.version {
version.observe(time::Local {
replica_id: entry.replica_id as ReplicaId,
value: entry.timestamp,
});
}
let ranges = edit
.ranges
.into_iter()
.map(|range| range.start as usize..range.end as usize)
.collect();
Operation::Edit(EditOperation {
timestamp: InsertionTimestamp {
replica_id: edit.replica_id as ReplicaId,
local: edit.local_timestamp,
lamport: edit.lamport_timestamp,
},
version,
ranges,
new_text: edit.new_text,
})
}
});
buffer.apply_ops(ops, cx)?;
Ok(buffer)
}
pub fn to_proto(&self) -> proto::Buffer {
let ops = self
.history
.ops
.values()
.map(|op| {
let version = op
.version
.iter()
.map(|entry| proto::VectorClockEntry {
replica_id: entry.replica_id as u32,
timestamp: entry.value,
})
.collect();
let ranges = op
.ranges
.iter()
.map(|range| proto::Range {
start: range.start as u64,
end: range.end as u64,
})
.collect();
proto::Operation {
variant: Some(proto::operation::Variant::Edit(proto::operation::Edit {
replica_id: op.timestamp.replica_id as u32,
local_timestamp: op.timestamp.local,
lamport_timestamp: op.timestamp.lamport,
version,
ranges,
new_text: op.new_text.clone(),
})),
}
})
.collect();
proto::Buffer {
content: self.history.base_text.to_string(),
history: ops,
}
}
pub fn file(&self) -> Option<&File> {
self.file.as_ref()
}
+2 -2
View File
@@ -21,9 +21,9 @@ fn main() {
language_registry.set_theme(&settings.borrow().theme);
let app_state = AppState {
language_registry,
language_registry: language_registry.clone(),
settings,
rpc: rpc::Client::new(),
rpc: rpc::Client::new(language_registry),
};
app.run(move |cx| {
+10 -6
View File
@@ -1,6 +1,5 @@
use crate::worktree::{File, Worktree};
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};
@@ -29,18 +28,23 @@ pub struct Client {
pub state: Arc<Mutex<ClientState>>,
}
#[derive(Default)]
pub struct ClientState {
connection_id: Option<ConnectionId>,
pub shared_worktrees: HashMap<u64, ModelHandle<Worktree>>,
pub shared_files: HashMap<File, HashMap<PeerId, usize>>,
pub shared_buffers: HashMap<PeerId, HashMap<usize, ModelHandle<Buffer>>>,
pub language_registry: Arc<LanguageRegistry>,
}
impl Client {
pub fn new() -> Self {
pub fn new(language_registry: Arc<LanguageRegistry>) -> Self {
Self {
peer: Peer::new(),
state: Default::default(),
state: Arc::new(Mutex::new(ClientState {
connection_id: None,
shared_worktrees: Default::default(),
shared_buffers: Default::default(),
language_registry,
})),
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
use crate::{AppState, language::LanguageRegistry, rpc, settings, time::ReplicaId};
use crate::{language::LanguageRegistry, rpc, settings, time::ReplicaId, AppState};
use ctor::ctor;
use gpui::AppContext;
use rand::Rng;
@@ -149,7 +149,7 @@ pub fn build_app_state(cx: &AppContext) -> AppState {
let language_registry = Arc::new(LanguageRegistry::new());
AppState {
settings,
language_registry,
rpc: rpc::Client::new(),
language_registry: language_registry.clone(),
rpc: rpc::Client::new(language_registry),
}
}
+37 -54
View File
@@ -44,9 +44,8 @@ pub fn init(cx: &mut MutableAppContext, rpc: rpc::Client) {
]);
pane::init(cx);
rpc.on_message(remote::open_file, cx);
rpc.on_message(remote::close_file, cx);
rpc.on_message(remote::open_buffer, cx);
rpc.on_message(remote::close_buffer, cx);
}
pub struct OpenParams {
@@ -110,41 +109,52 @@ fn open_paths(params: &OpenParams, cx: &mut MutableAppContext) {
mod remote {
use super::*;
pub async fn open_file(
request: TypedEnvelope<proto::OpenFile>,
pub async fn open_buffer(
request: TypedEnvelope<proto::OpenBuffer>,
rpc: &rpc::Client,
cx: &mut AsyncAppContext,
) -> anyhow::Result<()> {
// let message = &request.payload;
// let peer_id = request
// .original_sender_id
// .ok_or_else(|| anyhow!("missing original sender id"))?;
let message = &request.payload;
let peer_id = request
.original_sender_id
.ok_or_else(|| anyhow!("missing original sender id"))?;
// let mut state = rpc.state.lock().await;
// let worktree = state
// .shared_worktrees
// .get(&message.worktree_id)
// .ok_or_else(|| anyhow!("worktree {} not found", message.worktree_id))?
// .clone();
let mut state = rpc.state.lock().await;
let worktree = state
.shared_worktrees
.get(&message.worktree_id)
.ok_or_else(|| anyhow!("worktree {} not found", message.worktree_id))?
.clone();
// let file = worktree.file(&message.path);
// let id = file.id() as u64;
// let mtime = file.mtime().as_secs();
let buffer = worktree
.update(cx, |worktree, cx| {
worktree.open_buffer(
Path::new(&message.path),
state.language_registry.clone(),
cx,
)
})
.await?;
state
.shared_buffers
.entry(peer_id)
.or_default()
.insert(buffer.id(), buffer.clone());
// *state
// .shared_files
// .entry(file)
// .or_insert(Default::default())
// .entry(peer_id)
// .or_insert(0) += 1;
rpc.respond(
request.receipt(),
proto::OpenBufferResponse {
buffer_id: buffer.id() as u64,
buffer: Some(buffer.read_with(cx, |buf, _| buf.to_proto())),
},
)
.await?;
// rpc.respond(request.receipt(), proto::OpenFileResponse { id, mtime })
// .await?;
Ok(())
}
pub async fn close_file(
request: TypedEnvelope<proto::CloseFile>,
pub async fn close_buffer(
request: TypedEnvelope<proto::CloseBuffer>,
rpc: &rpc::Client,
_: &mut AsyncAppContext,
) -> anyhow::Result<()> {
@@ -169,33 +179,6 @@ mod remote {
Ok(())
}
pub async fn open_buffer(
request: TypedEnvelope<proto::OpenBuffer>,
rpc: &rpc::Client,
cx: &mut AsyncAppContext,
) -> anyhow::Result<()> {
// let message = &request.payload;
// let handle = {
// let state = rpc.state.lock().await;
// let mut files = state.shared_files.keys();
// files.find(|file| file.id() == message.id).cloned()
// };
// let buffer = if let Some(handle) = handle {
// let history = cx.read(|cx| handle.load_history(cx)).await?;
// Some(proto::Buffer {
// content: history.base_text.to_string(),
// history: Vec::new(),
// })
// } else {
// None
// };
// rpc.respond(request.receipt(), proto::OpenBufferResponse { buffer })
// .await?;
Ok(())
}
}
pub trait Item: Entity + Sized {
+91 -26
View File
@@ -65,7 +65,7 @@ impl Worktree {
}
pub fn remote(
id: usize,
id: u64,
worktree: proto::Worktree,
rpc: rpc::Client,
connection_id: ConnectionId,
@@ -98,6 +98,14 @@ impl Worktree {
}
}
pub fn as_remote_mut(&mut self) -> Option<&mut RemoteWorktree> {
if let Worktree::Remote(worktree) = self {
Some(worktree)
} else {
None
}
}
pub fn snapshot(&self) -> Snapshot {
match self {
Worktree::Local(worktree) => worktree.snapshot(),
@@ -110,10 +118,12 @@ impl Worktree {
path: impl AsRef<Path>,
language_registry: Arc<LanguageRegistry>,
cx: &mut ModelContext<Self>,
) -> impl Future<Output = Result<ModelHandle<Buffer>>> + 'static {
) -> Task<Result<ModelHandle<Buffer>>> {
match self {
Worktree::Local(worktree) => worktree.open_buffer(path.as_ref(), language_registry, cx),
Worktree::Remote(_) => todo!(),
Worktree::Remote(worktree) => {
worktree.open_buffer(path.as_ref(), language_registry, cx)
}
}
}
@@ -148,7 +158,7 @@ pub struct LocalWorktree {
_event_stream_handle: fsevent::Handle,
poll_scheduled: bool,
rpc: Option<rpc::Client>,
open_buffers: HashSet<WeakModelHandle<Buffer>>,
open_buffers: HashMap<usize, WeakModelHandle<Buffer>>,
}
impl LocalWorktree {
@@ -220,12 +230,12 @@ impl LocalWorktree {
path: &Path,
language_registry: Arc<LanguageRegistry>,
cx: &mut ModelContext<Worktree>,
) -> impl Future<Output = Result<ModelHandle<Buffer>>> + 'static {
) -> Task<Result<ModelHandle<Buffer>>> {
let handle = cx.handle();
// If there is already a buffer for the given path, then return it.
let mut existing_buffer = None;
self.open_buffers.retain(|buffer| {
self.open_buffers.retain(|_buffer_id, buffer| {
if let Some(buffer) = buffer.upgrade(cx.as_ref()) {
if let Some(file) = buffer.read(cx.as_ref()).file() {
if file.worktree_id() == handle.id() && file.path.as_ref() == path {
@@ -238,12 +248,16 @@ impl LocalWorktree {
}
});
let mut new_buffer = None;
if existing_buffer.is_none() {
let path = Arc::from(path);
let contents = self.load(&path, cx.as_ref());
new_buffer = Some(cx.spawn(|this, mut cx| async move {
let contents = contents.await?;
let path = Arc::from(path);
cx.spawn(|this, mut cx| async move {
if let Some(existing_buffer) = existing_buffer {
Ok(existing_buffer)
} else {
let contents = this
.read_with(&cx, |this, cx| {
this.as_local().unwrap().load(&path, cx.as_ref())
})
.await?;
let language = language_registry.select_language(&path).cloned();
let file = File::new(handle, path.into());
let buffer = cx.add_model(|cx| {
@@ -251,19 +265,11 @@ impl LocalWorktree {
});
this.update(&mut cx, |this, _| {
let this = this.as_local_mut().unwrap();
this.open_buffers.insert(buffer.downgrade());
this.open_buffers.insert(buffer.id(), buffer.downgrade());
});
Ok(buffer)
}));
}
async move {
if let Some(existing_buffer) = existing_buffer {
Ok(existing_buffer)
} else {
new_buffer.unwrap().await
}
}
})
}
pub fn scan_complete(&self) -> impl Future<Output = ()> {
@@ -292,7 +298,7 @@ impl LocalWorktree {
fn observe_snapshot_diff(&mut self, diff: Diff, cx: &mut ModelContext<Worktree>) {
let handle = cx.handle();
self.open_buffers.retain(|buffer| {
self.open_buffers.retain(|_buffer_id, buffer| {
if let Some(buffer) = buffer.upgrade(cx.as_ref()) {
buffer.update(cx, |buffer, cx| {
let handle = handle.clone();
@@ -506,16 +512,17 @@ impl fmt::Debug for LocalWorktree {
}
pub struct RemoteWorktree {
remote_id: usize,
remote_id: u64,
snapshot: Snapshot,
rpc: rpc::Client,
connection_id: ConnectionId,
replica_id: ReplicaId,
open_buffers: HashMap<u64, WeakModelHandle<Buffer>>,
}
impl RemoteWorktree {
fn new(
remote_id: usize,
remote_id: u64,
worktree: proto::Worktree,
rpc: rpc::Client,
connection_id: ConnectionId,
@@ -568,8 +575,64 @@ impl RemoteWorktree {
rpc,
connection_id,
replica_id,
open_buffers: Default::default(),
}
}
pub fn open_buffer(
&mut self,
path: &Path,
language_registry: Arc<LanguageRegistry>,
cx: &mut ModelContext<Worktree>,
) -> Task<Result<ModelHandle<Buffer>>> {
let handle = cx.handle();
let mut existing_buffer = None;
self.open_buffers.retain(|_buffer_id, buffer| {
if let Some(buffer) = buffer.upgrade(cx.as_ref()) {
if let Some(file) = buffer.read(cx.as_ref()).file() {
if file.worktree_id() == handle.id() && file.path.as_ref() == path {
existing_buffer = Some(buffer);
}
}
true
} else {
false
}
});
let rpc = self.rpc.clone();
let replica_id = self.replica_id;
let connection_id = self.connection_id;
let remote_worktree_id = self.remote_id;
let path = path.to_string_lossy().to_string();
cx.spawn(|this, mut cx| async move {
if let Some(existing_buffer) = existing_buffer {
Ok(existing_buffer)
} else {
let file = File::new(handle, Path::new(&path).into());
let language = language_registry.select_language(&path).cloned();
let response = rpc
.request(
connection_id,
proto::OpenBuffer {
worktree_id: remote_worktree_id as u64,
path,
},
)
.await?;
let buffer_id = response.buffer_id;
let remote_buffer = response.buffer.ok_or_else(|| anyhow!("empty buffer"))?;
let buffer = cx.add_model(|cx| {
Buffer::from_proto(replica_id, remote_buffer, Some(file), language, cx).unwrap()
});
this.update(&mut cx, |this, _| {
let this = this.as_remote_mut().unwrap();
this.open_buffers.insert(buffer_id, buffer.downgrade());
});
Ok(buffer)
}
})
}
}
#[derive(Clone)]
@@ -833,7 +896,9 @@ impl File {
pub fn saved_buffer(&self, buffer: ModelHandle<Buffer>, cx: &mut MutableAppContext) {
self.worktree.update(cx, |worktree, _| {
if let Worktree::Local(worktree) = worktree {
worktree.open_buffers.insert(buffer.downgrade());
worktree
.open_buffers
.insert(buffer.id(), buffer.downgrade());
}
})
}