logiguard fork v2: patched GPUI on verified 8c74db0 tree
Rebuilt from the exact zed-industries/zed @ 8c74db00c4053d6c0a9037863d7bd048658da4ca tree (previous orphan was based on a stale/wrong fetch). Same 3-file patch: - serial.rs: add SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; add latest_serial_of() - window.rs: activate() uses keyboard-enter serial (Mutter focus gate) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
use crate::{Channel, ChannelStore};
|
||||
use anyhow::Result;
|
||||
use client::{ChannelId, Client, Collaborator, UserStore, ZED_ALWAYS_ACTIVE};
|
||||
use collections::HashMap;
|
||||
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task};
|
||||
use language::proto::serialize_version;
|
||||
use rpc::{
|
||||
AnyProtoClient, TypedEnvelope,
|
||||
proto::{self, PeerId},
|
||||
};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use text::{BufferId, ReplicaId};
|
||||
use util::ResultExt;
|
||||
|
||||
pub const ACKNOWLEDGE_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(250);
|
||||
|
||||
pub(crate) fn init(client: &AnyProtoClient) {
|
||||
client.add_entity_message_handler(ChannelBuffer::handle_update_channel_buffer);
|
||||
client.add_entity_message_handler(ChannelBuffer::handle_update_channel_buffer_collaborators);
|
||||
}
|
||||
|
||||
pub struct ChannelBuffer {
|
||||
pub channel_id: ChannelId,
|
||||
connected: bool,
|
||||
rejoining: bool,
|
||||
collaborators: HashMap<PeerId, Collaborator>,
|
||||
user_store: Entity<UserStore>,
|
||||
channel_store: Entity<ChannelStore>,
|
||||
buffer: Entity<language::Buffer>,
|
||||
buffer_epoch: u64,
|
||||
client: Arc<Client>,
|
||||
subscription: Option<client::Subscription>,
|
||||
acknowledge_task: Option<Task<Result<()>>>,
|
||||
}
|
||||
|
||||
pub enum ChannelBufferEvent {
|
||||
CollaboratorsChanged,
|
||||
Disconnected,
|
||||
Connected,
|
||||
BufferEdited,
|
||||
ChannelChanged,
|
||||
}
|
||||
|
||||
impl EventEmitter<ChannelBufferEvent> for ChannelBuffer {}
|
||||
|
||||
impl ChannelBuffer {
|
||||
pub(crate) async fn new(
|
||||
channel: Arc<Channel>,
|
||||
client: Arc<Client>,
|
||||
user_store: Entity<UserStore>,
|
||||
channel_store: Entity<ChannelStore>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<Entity<Self>> {
|
||||
let response = client
|
||||
.request(proto::JoinChannelBuffer {
|
||||
channel_id: channel.id.0,
|
||||
})
|
||||
.await?;
|
||||
let buffer_id = BufferId::new(response.buffer_id)?;
|
||||
let base_text = response.base_text;
|
||||
let operations = response
|
||||
.operations
|
||||
.into_iter()
|
||||
.map(language::proto::deserialize_operation)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let buffer = cx.new(|cx| {
|
||||
let capability = channel_store.read(cx).channel_capability(channel.id);
|
||||
language::Buffer::remote(
|
||||
buffer_id,
|
||||
ReplicaId::new(response.replica_id as u16),
|
||||
capability,
|
||||
base_text,
|
||||
)
|
||||
});
|
||||
buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx));
|
||||
|
||||
let subscription = client.subscribe_to_entity(channel.id.0)?;
|
||||
|
||||
anyhow::Ok(cx.new(|cx| {
|
||||
cx.subscribe(&buffer, Self::on_buffer_update).detach();
|
||||
cx.on_release(Self::release).detach();
|
||||
let mut this = Self {
|
||||
buffer,
|
||||
buffer_epoch: response.epoch,
|
||||
client,
|
||||
connected: true,
|
||||
rejoining: false,
|
||||
collaborators: Default::default(),
|
||||
acknowledge_task: None,
|
||||
channel_id: channel.id,
|
||||
subscription: Some(subscription.set_entity(&cx.entity(), &cx.to_async())),
|
||||
user_store,
|
||||
channel_store,
|
||||
};
|
||||
this.replace_collaborators(response.collaborators, cx);
|
||||
this
|
||||
}))
|
||||
}
|
||||
|
||||
fn release(&mut self, _: &mut App) {
|
||||
if self.connected {
|
||||
if let Some(task) = self.acknowledge_task.take() {
|
||||
task.detach();
|
||||
}
|
||||
self.client
|
||||
.send(proto::LeaveChannelBuffer {
|
||||
channel_id: self.channel_id.0,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connected(&mut self, cx: &mut Context<Self>) {
|
||||
self.connected = true;
|
||||
self.rejoining = false;
|
||||
if self.subscription.is_none() {
|
||||
let Ok(subscription) = self.client.subscribe_to_entity(self.channel_id.0) else {
|
||||
return;
|
||||
};
|
||||
self.subscription = Some(subscription.set_entity(&cx.entity(), &cx.to_async()));
|
||||
cx.emit(ChannelBufferEvent::Connected);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_rejoining(&mut self, rejoining: bool) {
|
||||
self.rejoining = rejoining;
|
||||
}
|
||||
|
||||
pub fn remote_id(&self, cx: &App) -> BufferId {
|
||||
self.buffer.read(cx).remote_id()
|
||||
}
|
||||
|
||||
pub fn user_store(&self) -> &Entity<UserStore> {
|
||||
&self.user_store
|
||||
}
|
||||
|
||||
pub(crate) fn replace_collaborators(
|
||||
&mut self,
|
||||
collaborators: Vec<proto::Collaborator>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut new_collaborators = HashMap::default();
|
||||
for collaborator in collaborators {
|
||||
if let Ok(collaborator) = Collaborator::from_proto(collaborator) {
|
||||
new_collaborators.insert(collaborator.peer_id, collaborator);
|
||||
}
|
||||
}
|
||||
|
||||
for old_collaborator in self.collaborators.values() {
|
||||
if !new_collaborators.contains_key(&old_collaborator.peer_id) {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.remove_peer(old_collaborator.replica_id, cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
self.collaborators = new_collaborators;
|
||||
cx.emit(ChannelBufferEvent::CollaboratorsChanged);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
async fn handle_update_channel_buffer(
|
||||
this: Entity<Self>,
|
||||
update_channel_buffer: TypedEnvelope<proto::UpdateChannelBuffer>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<()> {
|
||||
let ops = update_channel_buffer
|
||||
.payload
|
||||
.operations
|
||||
.into_iter()
|
||||
.map(language::proto::deserialize_operation)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
cx.notify();
|
||||
this.buffer
|
||||
.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_update_channel_buffer_collaborators(
|
||||
this: Entity<Self>,
|
||||
message: TypedEnvelope<proto::UpdateChannelBufferCollaborators>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<()> {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.replace_collaborators(message.payload.collaborators, cx);
|
||||
cx.emit(ChannelBufferEvent::CollaboratorsChanged);
|
||||
cx.notify();
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_buffer_update(
|
||||
&mut self,
|
||||
_: Entity<language::Buffer>,
|
||||
event: &language::BufferEvent,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
match event {
|
||||
language::BufferEvent::Operation {
|
||||
operation,
|
||||
is_local: true,
|
||||
} => {
|
||||
if *ZED_ALWAYS_ACTIVE
|
||||
&& let language::Operation::UpdateSelections { selections, .. } = operation
|
||||
&& selections.is_empty()
|
||||
{
|
||||
return;
|
||||
}
|
||||
let operation = language::proto::serialize_operation(operation);
|
||||
if self.rejoining {
|
||||
return;
|
||||
}
|
||||
self.client
|
||||
.send(proto::UpdateChannelBuffer {
|
||||
channel_id: self.channel_id.0,
|
||||
operations: vec![operation],
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
language::BufferEvent::Edited { .. } => {
|
||||
cx.emit(ChannelBufferEvent::BufferEdited);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acknowledge_buffer_version(&mut self, cx: &mut Context<ChannelBuffer>) {
|
||||
let buffer = self.buffer.read(cx);
|
||||
let version = buffer.version();
|
||||
let buffer_id = buffer.remote_id().into();
|
||||
let client = self.client.clone();
|
||||
let epoch = self.epoch();
|
||||
|
||||
self.acknowledge_task = Some(cx.spawn(async move |_, cx| {
|
||||
cx.background_executor()
|
||||
.timer(ACKNOWLEDGE_DEBOUNCE_INTERVAL)
|
||||
.await;
|
||||
client
|
||||
.send(proto::AckBufferOperation {
|
||||
buffer_id,
|
||||
epoch,
|
||||
version: serialize_version(&version),
|
||||
})
|
||||
.ok();
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn epoch(&self) -> u64 {
|
||||
self.buffer_epoch
|
||||
}
|
||||
|
||||
pub fn buffer(&self) -> Entity<language::Buffer> {
|
||||
self.buffer.clone()
|
||||
}
|
||||
|
||||
pub fn collaborators(&self) -> &HashMap<PeerId, Collaborator> {
|
||||
&self.collaborators
|
||||
}
|
||||
|
||||
pub fn channel(&self, cx: &App) -> Option<Arc<Channel>> {
|
||||
self.channel_store
|
||||
.read(cx)
|
||||
.channel_for_id(self.channel_id)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn disconnect(&mut self, cx: &mut Context<Self>) {
|
||||
log::info!("channel buffer {} disconnected", self.channel_id);
|
||||
if self.connected {
|
||||
self.connected = false;
|
||||
self.rejoining = false;
|
||||
self.subscription.take();
|
||||
cx.emit(ChannelBufferEvent::Disconnected);
|
||||
cx.notify()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn channel_changed(&mut self, cx: &mut Context<Self>) {
|
||||
cx.emit(ChannelBufferEvent::ChannelChanged);
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.connected
|
||||
}
|
||||
|
||||
pub fn replica_id(&self, cx: &App) -> ReplicaId {
|
||||
self.buffer.read(cx).replica_id()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user