Merge pull request #1530 from zed-industries/robust-buffer-relaying

Create buffers for remote collaborators out of band
This commit is contained in:
Antonio Scandurra
2022-08-17 12:04:18 +02:00
committed by GitHub
9 changed files with 241 additions and 180 deletions
+46
View File
@@ -1390,6 +1390,52 @@ async fn test_leaving_worktree_while_opening_buffer(
.await;
}
#[gpui::test(iterations = 10)]
async fn test_canceling_buffer_opening(
deterministic: Arc<Deterministic>,
cx_a: &mut TestAppContext,
cx_b: &mut TestAppContext,
) {
deterministic.forbid_parking();
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
let client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
server
.make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)])
.await;
client_a
.fs
.insert_tree(
"/dir",
json!({
"a.txt": "abc",
}),
)
.await;
let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
let buffer_a = project_a
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
.await
.unwrap();
// Open a buffer as client B but cancel after a random amount of time.
let buffer_b = project_b.update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx));
deterministic.simulate_random_delay().await;
drop(buffer_b);
// Try opening the same buffer again as client B, and ensure we can
// still do it despite the cancellation above.
let buffer_b = project_b
.update(cx_b, |p, cx| p.open_buffer_by_id(buffer_a.id() as u64, cx))
.await
.unwrap();
buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "abc"));
}
#[gpui::test(iterations = 10)]
async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
cx_a.foreground().forbid_parking();
+13
View File
@@ -187,6 +187,7 @@ impl Server {
.add_request_handler(Server::forward_project_request::<proto::RenameProjectEntry>)
.add_request_handler(Server::forward_project_request::<proto::CopyProjectEntry>)
.add_request_handler(Server::forward_project_request::<proto::DeleteProjectEntry>)
.add_message_handler(Server::create_buffer_for_peer)
.add_request_handler(Server::update_buffer)
.add_message_handler(Server::update_buffer_file)
.add_message_handler(Server::buffer_reloaded)
@@ -1186,6 +1187,18 @@ impl Server {
Ok(())
}
async fn create_buffer_for_peer(
self: Arc<Server>,
request: TypedEnvelope<proto::CreateBufferForPeer>,
) -> Result<()> {
self.peer.forward_send(
request.sender_id,
ConnectionId(request.payload.peer_id),
request.payload,
)?;
Ok(())
}
async fn update_buffer(
self: Arc<Server>,
request: TypedEnvelope<proto::UpdateBuffer>,
+12 -9
View File
@@ -381,6 +381,17 @@ impl Deterministic {
state.forbid_parking = true;
state.rng = StdRng::seed_from_u64(state.seed);
}
pub async fn simulate_random_delay(&self) {
use rand::prelude::*;
use smol::future::yield_now;
if self.state.lock().rng.gen_bool(0.2) {
let yields = self.state.lock().rng.gen_range(1..=10);
for _ in 0..yields {
yield_now().await;
}
}
}
}
impl Drop for Timer {
@@ -662,17 +673,9 @@ impl Background {
#[cfg(any(test, feature = "test-support"))]
pub async fn simulate_random_delay(&self) {
use rand::prelude::*;
use smol::future::yield_now;
match self {
Self::Deterministic { executor, .. } => {
if executor.state.lock().rng.gen_bool(0.2) {
let yields = executor.state.lock().rng.gen_range(1..=10);
for _ in 0..yields {
yield_now().await;
}
}
executor.simulate_random_delay().await;
}
_ => {
panic!("this method can only be called on a deterministic executor")
+3 -3
View File
@@ -358,7 +358,7 @@ impl Buffer {
pub fn from_proto(
replica_id: ReplicaId,
message: proto::BufferState,
message: proto::Buffer,
file: Option<Arc<dyn File>>,
cx: &mut ModelContext<Self>,
) -> Result<Self> {
@@ -406,7 +406,7 @@ impl Buffer {
Ok(this)
}
pub fn to_proto(&self) -> proto::BufferState {
pub fn to_proto(&self) -> proto::Buffer {
let mut operations = self
.text
.history()
@@ -414,7 +414,7 @@ impl Buffer {
.chain(self.deferred_ops.iter().map(proto::serialize_operation))
.collect::<Vec<_>>();
operations.sort_unstable_by_key(proto::lamport_timestamp_for_operation);
proto::BufferState {
proto::Buffer {
id: self.remote_id(),
file: self.file.as_ref().map(|f| f.to_proto()),
base_text: self.base_text().to_string(),
+1 -1
View File
@@ -9,7 +9,7 @@ use rpc::proto;
use std::{ops::Range, sync::Arc};
use text::*;
pub use proto::{Buffer, BufferState, LineEnding, SelectionSet};
pub use proto::{Buffer, LineEnding, SelectionSet};
pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding {
match message {
+15 -14
View File
@@ -522,11 +522,10 @@ async fn location_links_from_proto(
for link in proto_links {
let origin = match link.origin {
Some(origin) => {
let buffer = origin
.buffer
.ok_or_else(|| anyhow!("missing origin buffer"))?;
let buffer = project
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.update(&mut cx, |this, cx| {
this.wait_for_buffer(origin.buffer_id, cx)
})
.await?;
let start = origin
.start
@@ -548,9 +547,10 @@ async fn location_links_from_proto(
};
let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
let buffer = target.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
let buffer = project
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.update(&mut cx, |this, cx| {
this.wait_for_buffer(target.buffer_id, cx)
})
.await?;
let start = target
.start
@@ -664,19 +664,19 @@ fn location_links_to_proto(
.into_iter()
.map(|definition| {
let origin = definition.origin.map(|origin| {
let buffer = project.serialize_buffer_for_peer(&origin.buffer, peer_id, cx);
let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
proto::Location {
start: Some(serialize_anchor(&origin.range.start)),
end: Some(serialize_anchor(&origin.range.end)),
buffer: Some(buffer),
buffer_id,
}
});
let buffer = project.serialize_buffer_for_peer(&definition.target.buffer, peer_id, cx);
let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
let target = proto::Location {
start: Some(serialize_anchor(&definition.target.range.start)),
end: Some(serialize_anchor(&definition.target.range.end)),
buffer: Some(buffer),
buffer_id,
};
proto::LocationLink {
@@ -792,11 +792,11 @@ impl LspCommand for GetReferences {
let locations = response
.into_iter()
.map(|definition| {
let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
proto::Location {
start: Some(serialize_anchor(&definition.range.start)),
end: Some(serialize_anchor(&definition.range.end)),
buffer: Some(buffer),
buffer_id,
}
})
.collect();
@@ -812,9 +812,10 @@ impl LspCommand for GetReferences {
) -> Result<Vec<Location>> {
let mut locations = Vec::new();
for location in message.locations {
let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
let target_buffer = project
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.update(&mut cx, |this, cx| {
this.wait_for_buffer(location.buffer_id, cx)
})
.await?;
let start = location
.start
+93 -92
View File
@@ -112,7 +112,7 @@ pub struct Project {
collaborators: HashMap<PeerId, Collaborator>,
client_subscriptions: Vec<client::Subscription>,
_subscriptions: Vec<gpui::Subscription>,
opened_buffer: (Rc<RefCell<watch::Sender<()>>>, watch::Receiver<()>),
opened_buffer: (watch::Sender<()>, watch::Receiver<()>),
shared_buffers: HashMap<PeerId, HashSet<u64>>,
#[allow(clippy::type_complexity)]
loading_buffers: HashMap<
@@ -375,6 +375,7 @@ impl Project {
client.add_model_message_handler(Self::handle_update_project);
client.add_model_message_handler(Self::handle_unregister_project);
client.add_model_message_handler(Self::handle_project_unshared);
client.add_model_message_handler(Self::handle_create_buffer_for_peer);
client.add_model_message_handler(Self::handle_update_buffer_file);
client.add_model_message_handler(Self::handle_update_buffer);
client.add_model_message_handler(Self::handle_update_diagnostic_summary);
@@ -454,7 +455,6 @@ impl Project {
let handle = cx.weak_handle();
project_store.update(cx, |store, cx| store.add_project(handle, cx));
let (opened_buffer_tx, opened_buffer_rx) = watch::channel();
Self {
worktrees: Default::default(),
collaborators: Default::default(),
@@ -472,7 +472,7 @@ impl Project {
_maintain_remote_id,
_maintain_online_status,
},
opened_buffer: (Rc::new(RefCell::new(opened_buffer_tx)), opened_buffer_rx),
opened_buffer: watch::channel(),
client_subscriptions: Vec::new(),
_subscriptions: vec![cx.observe_global::<Settings, _>(Self::on_settings_changed)],
_maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx),
@@ -540,7 +540,6 @@ impl Project {
worktrees.push(worktree);
}
let (opened_buffer_tx, opened_buffer_rx) = watch::channel();
let this = cx.add_model(|cx: &mut ModelContext<Self>| {
let handle = cx.weak_handle();
project_store.update(cx, |store, cx| store.add_project(handle, cx));
@@ -548,7 +547,7 @@ impl Project {
let mut this = Self {
worktrees: Vec::new(),
loading_buffers: Default::default(),
opened_buffer: (Rc::new(RefCell::new(opened_buffer_tx)), opened_buffer_rx),
opened_buffer: watch::channel(),
shared_buffers: Default::default(),
loading_local_worktrees: Default::default(),
active_entry: None,
@@ -1624,9 +1623,10 @@ impl Project {
path: path_string,
})
.await?;
let buffer = response.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await
this.update(&mut cx, |this, cx| {
this.wait_for_buffer(response.buffer_id, cx)
})
.await
})
}
@@ -1684,11 +1684,8 @@ impl Project {
.client
.request(proto::OpenBufferById { project_id, id });
cx.spawn(|this, mut cx| async move {
let buffer = request
.await?
.buffer
.ok_or_else(|| anyhow!("invalid buffer"))?;
this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
let buffer_id = request.await?.buffer_id;
this.update(&mut cx, |this, cx| this.wait_for_buffer(buffer_id, cx))
.await
})
} else {
@@ -1800,6 +1797,7 @@ impl Project {
})
.detach();
*self.opened_buffer.0.borrow_mut() = ();
Ok(())
}
@@ -3476,9 +3474,10 @@ impl Project {
});
cx.spawn(|this, mut cx| async move {
let response = request.await?;
let buffer = response.buffer.ok_or_else(|| anyhow!("invalid buffer"))?;
this.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.await
this.update(&mut cx, |this, cx| {
this.wait_for_buffer(response.buffer_id, cx)
})
.await
})
} else {
Task::ready(Err(anyhow!("project does not have a remote id")))
@@ -4294,9 +4293,10 @@ impl Project {
let response = request.await?;
let mut result = HashMap::default();
for location in response.locations {
let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
let target_buffer = this
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.update(&mut cx, |this, cx| {
this.wait_for_buffer(location.buffer_id, cx)
})
.await?;
let start = location
.start
@@ -5107,6 +5107,36 @@ impl Project {
})
}
async fn handle_create_buffer_for_peer(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::CreateBufferForPeer>,
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
this.update(&mut cx, |this, cx| {
let mut buffer = envelope
.payload
.buffer
.ok_or_else(|| anyhow!("invalid buffer"))?;
let mut buffer_file = None;
if let Some(file) = buffer.file.take() {
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(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
as Arc<dyn language::File>);
}
let buffer = cx.add_model(|cx| {
Buffer::from_proto(this.replica_id(), buffer, buffer_file, cx).unwrap()
});
this.register_buffer(&buffer, cx)?;
Ok(())
})
}
async fn handle_update_buffer_file(
this: ModelHandle<Self>,
envelope: TypedEnvelope<proto::UpdateBufferFile>,
@@ -5448,9 +5478,9 @@ impl Project {
for range in ranges {
let start = serialize_anchor(&range.start);
let end = serialize_anchor(&range.end);
let buffer = this.serialize_buffer_for_peer(&buffer, peer_id, cx);
let buffer_id = this.create_buffer_for_peer(&buffer, peer_id, cx);
locations.push(proto::Location {
buffer: Some(buffer),
buffer_id,
start: Some(start),
end: Some(end),
});
@@ -5487,9 +5517,9 @@ impl Project {
.await?;
Ok(proto::OpenBufferForSymbolResponse {
buffer: Some(this.update(&mut cx, |this, cx| {
this.serialize_buffer_for_peer(&buffer, peer_id, cx)
})),
buffer_id: this.update(&mut cx, |this, cx| {
this.create_buffer_for_peer(&buffer, peer_id, cx)
}),
})
}
@@ -5515,7 +5545,7 @@ impl Project {
.await?;
this.update(&mut cx, |this, cx| {
Ok(proto::OpenBufferResponse {
buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx),
})
})
}
@@ -5541,7 +5571,7 @@ impl Project {
let buffer = open_buffer.await?;
this.update(&mut cx, |this, cx| {
Ok(proto::OpenBufferResponse {
buffer: Some(this.serialize_buffer_for_peer(&buffer, peer_id, cx)),
buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx),
})
})
}
@@ -5553,13 +5583,13 @@ impl Project {
cx: &AppContext,
) -> proto::ProjectTransaction {
let mut serialized_transaction = proto::ProjectTransaction {
buffers: Default::default(),
buffer_ids: Default::default(),
transactions: Default::default(),
};
for (buffer, transaction) in project_transaction.0 {
serialized_transaction
.buffers
.push(self.serialize_buffer_for_peer(&buffer, peer_id, cx));
.buffer_ids
.push(self.create_buffer_for_peer(&buffer, peer_id, cx));
serialized_transaction
.transactions
.push(language::proto::serialize_transaction(&transaction));
@@ -5575,9 +5605,10 @@ impl Project {
) -> Task<Result<ProjectTransaction>> {
cx.spawn(|this, mut cx| async move {
let mut project_transaction = ProjectTransaction::default();
for (buffer, transaction) in message.buffers.into_iter().zip(message.transactions) {
for (buffer_id, transaction) in message.buffer_ids.into_iter().zip(message.transactions)
{
let buffer = this
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
.update(&mut cx, |this, cx| this.wait_for_buffer(buffer_id, cx))
.await?;
let transaction = language::proto::deserialize_transaction(transaction)?;
project_transaction.0.insert(buffer, transaction);
@@ -5601,81 +5632,51 @@ impl Project {
})
}
fn serialize_buffer_for_peer(
fn create_buffer_for_peer(
&mut self,
buffer: &ModelHandle<Buffer>,
peer_id: PeerId,
cx: &AppContext,
) -> proto::Buffer {
) -> u64 {
let buffer_id = buffer.read(cx).remote_id();
let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
if shared_buffers.insert(buffer_id) {
proto::Buffer {
variant: Some(proto::buffer::Variant::State(buffer.read(cx).to_proto())),
}
} else {
proto::Buffer {
variant: Some(proto::buffer::Variant::Id(buffer_id)),
if let Some(project_id) = self.remote_id() {
let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
if shared_buffers.insert(buffer_id) {
self.client
.send(proto::CreateBufferForPeer {
project_id,
peer_id: peer_id.0,
buffer: Some(buffer.read(cx).to_proto()),
})
.log_err();
}
}
buffer_id
}
fn deserialize_buffer(
&mut self,
buffer: proto::Buffer,
fn wait_for_buffer(
&self,
id: u64,
cx: &mut ModelContext<Self>,
) -> Task<Result<ModelHandle<Buffer>>> {
let replica_id = self.replica_id();
let opened_buffer_tx = self.opened_buffer.0.clone();
let mut opened_buffer_rx = self.opened_buffer.1.clone();
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.opened_buffers
.get(&id)
.and_then(|buffer| buffer.upgrade(cx))
});
if let Some(buffer) = buffer {
break buffer;
}
opened_buffer_rx
.next()
.await
.ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
};
Ok(buffer)
cx.spawn(|this, cx| async move {
let buffer = loop {
let buffer = this.read_with(&cx, |this, cx| {
this.opened_buffers
.get(&id)
.and_then(|buffer| buffer.upgrade(cx))
});
if let Some(buffer) = buffer {
break 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(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
as Arc<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, cx))?;
*opened_buffer_tx.borrow_mut().borrow_mut() = ();
Ok(buffer)
}
}
opened_buffer_rx
.next()
.await
.ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?;
};
Ok(buffer)
})
}
+56 -61
View File
@@ -55,58 +55,59 @@ message Envelope {
OpenBufferById open_buffer_by_id = 44;
OpenBufferByPath open_buffer_by_path = 45;
OpenBufferResponse open_buffer_response = 46;
UpdateBuffer update_buffer = 47;
UpdateBufferFile update_buffer_file = 48;
SaveBuffer save_buffer = 49;
BufferSaved buffer_saved = 50;
BufferReloaded buffer_reloaded = 51;
ReloadBuffers reload_buffers = 52;
ReloadBuffersResponse reload_buffers_response = 53;
FormatBuffers format_buffers = 54;
FormatBuffersResponse format_buffers_response = 55;
GetCompletions get_completions = 56;
GetCompletionsResponse get_completions_response = 57;
ApplyCompletionAdditionalEdits apply_completion_additional_edits = 58;
ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 59;
GetCodeActions get_code_actions = 60;
GetCodeActionsResponse get_code_actions_response = 61;
GetHover get_hover = 62;
GetHoverResponse get_hover_response = 63;
ApplyCodeAction apply_code_action = 64;
ApplyCodeActionResponse apply_code_action_response = 65;
PrepareRename prepare_rename = 66;
PrepareRenameResponse prepare_rename_response = 67;
PerformRename perform_rename = 68;
PerformRenameResponse perform_rename_response = 69;
SearchProject search_project = 70;
SearchProjectResponse search_project_response = 71;
CreateBufferForPeer create_buffer_for_peer = 47;
UpdateBuffer update_buffer = 48;
UpdateBufferFile update_buffer_file = 49;
SaveBuffer save_buffer = 50;
BufferSaved buffer_saved = 51;
BufferReloaded buffer_reloaded = 52;
ReloadBuffers reload_buffers = 53;
ReloadBuffersResponse reload_buffers_response = 54;
FormatBuffers format_buffers = 55;
FormatBuffersResponse format_buffers_response = 56;
GetCompletions get_completions = 57;
GetCompletionsResponse get_completions_response = 58;
ApplyCompletionAdditionalEdits apply_completion_additional_edits = 59;
ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 60;
GetCodeActions get_code_actions = 61;
GetCodeActionsResponse get_code_actions_response = 62;
GetHover get_hover = 63;
GetHoverResponse get_hover_response = 64;
ApplyCodeAction apply_code_action = 65;
ApplyCodeActionResponse apply_code_action_response = 66;
PrepareRename prepare_rename = 67;
PrepareRenameResponse prepare_rename_response = 68;
PerformRename perform_rename = 69;
PerformRenameResponse perform_rename_response = 70;
SearchProject search_project = 71;
SearchProjectResponse search_project_response = 72;
GetChannels get_channels = 72;
GetChannelsResponse get_channels_response = 73;
JoinChannel join_channel = 74;
JoinChannelResponse join_channel_response = 75;
LeaveChannel leave_channel = 76;
SendChannelMessage send_channel_message = 77;
SendChannelMessageResponse send_channel_message_response = 78;
ChannelMessageSent channel_message_sent = 79;
GetChannelMessages get_channel_messages = 80;
GetChannelMessagesResponse get_channel_messages_response = 81;
GetChannels get_channels = 73;
GetChannelsResponse get_channels_response = 74;
JoinChannel join_channel = 75;
JoinChannelResponse join_channel_response = 76;
LeaveChannel leave_channel = 77;
SendChannelMessage send_channel_message = 78;
SendChannelMessageResponse send_channel_message_response = 79;
ChannelMessageSent channel_message_sent = 80;
GetChannelMessages get_channel_messages = 81;
GetChannelMessagesResponse get_channel_messages_response = 82;
UpdateContacts update_contacts = 82;
UpdateInviteInfo update_invite_info = 83;
ShowContacts show_contacts = 84;
UpdateContacts update_contacts = 83;
UpdateInviteInfo update_invite_info = 84;
ShowContacts show_contacts = 85;
GetUsers get_users = 85;
FuzzySearchUsers fuzzy_search_users = 86;
UsersResponse users_response = 87;
RequestContact request_contact = 88;
RespondToContactRequest respond_to_contact_request = 89;
RemoveContact remove_contact = 90;
GetUsers get_users = 86;
FuzzySearchUsers fuzzy_search_users = 87;
UsersResponse users_response = 88;
RequestContact request_contact = 89;
RespondToContactRequest respond_to_contact_request = 90;
RemoveContact remove_contact = 91;
Follow follow = 91;
FollowResponse follow_response = 92;
UpdateFollowers update_followers = 93;
Unfollow unfollow = 94;
Follow follow = 92;
FollowResponse follow_response = 93;
UpdateFollowers update_followers = 94;
Unfollow unfollow = 95;
}
}
@@ -299,7 +300,7 @@ message GetDocumentHighlightsResponse {
}
message Location {
Buffer buffer = 1;
uint64 buffer_id = 1;
Anchor start = 2;
Anchor end = 3;
}
@@ -348,7 +349,7 @@ message OpenBufferForSymbol {
}
message OpenBufferForSymbolResponse {
Buffer buffer = 1;
uint64 buffer_id = 1;
}
message OpenBufferByPath {
@@ -363,12 +364,13 @@ message OpenBufferById {
}
message OpenBufferResponse {
Buffer buffer = 1;
uint64 buffer_id = 1;
}
message CloseBuffer {
message CreateBufferForPeer {
uint64 project_id = 1;
uint64 buffer_id = 2;
uint32 peer_id = 2;
Buffer buffer = 3;
}
message UpdateBuffer {
@@ -539,7 +541,7 @@ message CodeAction {
}
message ProjectTransaction {
repeated Buffer buffers = 1;
repeated uint64 buffer_ids = 1;
repeated Transaction transactions = 2;
}
@@ -807,13 +809,6 @@ message Entry {
}
message Buffer {
oneof variant {
uint64 id = 1;
BufferState state = 2;
}
}
message BufferState {
uint64 id = 1;
optional File file = 2;
string base_text = 3;
+2
View File
@@ -86,6 +86,7 @@ messages!(
(RemoveContact, Foreground),
(ChannelMessageSent, Foreground),
(CopyProjectEntry, Foreground),
(CreateBufferForPeer, Foreground),
(CreateProjectEntry, Foreground),
(DeleteProjectEntry, Foreground),
(Error, Foreground),
@@ -222,6 +223,7 @@ entity_messages!(
BufferReloaded,
BufferSaved,
CopyProjectEntry,
CreateBufferForPeer,
CreateProjectEntry,
DeleteProjectEntry,
Follow,