Show remote collaborators' active selections

Antonio Scandurra created

Change summary

crates/editor/src/editor.rs       | 37 +++++++++++++++------
crates/editor/src/multi_buffer.rs | 30 +++++++++++++++++
crates/language/src/buffer.rs     | 55 +++++++++++++++++++++++++++++---
crates/language/src/proto.rs      | 15 +-------
crates/rpc/proto/zed.proto        |  4 -
crates/rpc/src/rpc.rs             |  2 
crates/text/src/text.rs           | 16 ++++-----
script/seed-db                    |  3 -
8 files changed, 117 insertions(+), 45 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -1991,8 +1991,8 @@ impl Editor {
 
     pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
         if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
-            if let Some((selections, _)) = self.selection_history.get(&tx_id) {
-                self.selections = selections.clone();
+            if let Some((selections, _)) = self.selection_history.get(&tx_id).cloned() {
+                self.set_selections(selections, cx);
             }
             self.request_autoscroll(Autoscroll::Fit, cx);
         }
@@ -2000,8 +2000,8 @@ impl Editor {
 
     pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
         if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
-            if let Some((_, Some(selections))) = self.selection_history.get(&tx_id) {
-                self.selections = selections.clone();
+            if let Some((_, Some(selections))) = self.selection_history.get(&tx_id).cloned() {
+                self.set_selections(selections, cx);
             }
             self.request_autoscroll(Autoscroll::Fit, cx);
         }
@@ -3256,13 +3256,23 @@ impl Editor {
         }
         self.pause_cursor_blinking(cx);
 
-        self.selections = Arc::from_iter(selections.into_iter().map(|selection| Selection {
-            id: selection.id,
-            start: buffer.anchor_before(selection.start),
-            end: buffer.anchor_before(selection.end),
-            reversed: selection.reversed,
-            goal: selection.goal,
-        }));
+        self.set_selections(
+            Arc::from_iter(selections.into_iter().map(|selection| Selection {
+                id: selection.id,
+                start: buffer.anchor_before(selection.start),
+                end: buffer.anchor_before(selection.end),
+                reversed: selection.reversed,
+                goal: selection.goal,
+            })),
+            cx,
+        );
+    }
+
+    fn set_selections(&mut self, selections: Arc<[Selection<Anchor>]>, cx: &mut ViewContext<Self>) {
+        self.selections = selections;
+        self.buffer.update(cx, |buffer, cx| {
+            buffer.set_active_selections(&self.selections, cx)
+        });
     }
 
     fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
@@ -3651,11 +3661,16 @@ impl View for Editor {
     fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
         self.focused = true;
         self.blink_cursors(self.blink_epoch, cx);
+        self.buffer.update(cx, |buffer, cx| {
+            buffer.set_active_selections(&self.selections, cx)
+        });
     }
 
     fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
         self.focused = false;
         self.show_local_cursors = false;
+        self.buffer
+            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
         cx.emit(Event::Blurred);
         cx.notify();
     }

crates/editor/src/multi_buffer.rs 🔗

@@ -12,7 +12,7 @@ use language::{
 use std::{
     cell::{Ref, RefCell},
     cmp, io,
-    iter::Peekable,
+    iter::{FromIterator, Peekable},
     ops::{Range, Sub},
     sync::Arc,
     time::{Duration, Instant, SystemTime},
@@ -242,6 +242,34 @@ impl MultiBuffer {
             .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
     }
 
+    pub fn set_active_selections(
+        &mut self,
+        selections: &[Selection<Anchor>],
+        cx: &mut ModelContext<Self>,
+    ) {
+        // TODO
+        let this = self.read(cx);
+        self.as_singleton().unwrap().update(cx, |buffer, cx| {
+            let buffer_snapshot = buffer.snapshot();
+            let selections = selections.iter().map(|selection| Selection {
+                id: selection.id,
+                start: buffer_snapshot.anchor_before(selection.start.to_offset(&this)),
+                end: buffer_snapshot.anchor_before(selection.end.to_offset(&this)),
+                reversed: selection.reversed,
+                goal: selection.goal,
+            });
+            buffer.set_active_selections(Arc::from_iter(selections), cx);
+        });
+    }
+
+    pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
+        for buffer in self.buffers.values() {
+            buffer
+                .buffer
+                .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
+        }
+    }
+
     pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
         // TODO
         self.as_singleton()

crates/language/src/buffer.rs 🔗

@@ -289,6 +289,12 @@ impl Buffer {
             .map(|op| text::Operation::Edit(proto::deserialize_edit_operation(op)));
         buffer.apply_ops(ops)?;
         let mut this = Self::build(buffer, file);
+        for selection_set in message.selections {
+            this.remote_selections.insert(
+                selection_set.replica_id as ReplicaId,
+                proto::deserialize_selections(selection_set.selections),
+            );
+        }
         this.apply_diagnostic_update(
             Arc::from(proto::deserialize_diagnostics(message.diagnostics)),
             cx,
@@ -306,7 +312,14 @@ impl Buffer {
                 .history()
                 .map(proto::serialize_edit_operation)
                 .collect(),
-            selections: Vec::new(),
+            selections: self
+                .remote_selections
+                .iter()
+                .map(|(replica_id, selections)| proto::SelectionSet {
+                    replica_id: *replica_id as u32,
+                    selections: proto::serialize_selections(selections),
+                })
+                .collect(),
             diagnostics: proto::serialize_diagnostics(self.diagnostics.iter()),
         }
     }
@@ -835,7 +848,7 @@ impl Buffer {
         cx.emit(Event::DiagnosticsUpdated);
         Ok(Operation::UpdateDiagnostics {
             diagnostics: Arc::from(self.diagnostics.iter().cloned().collect::<Vec<_>>()),
-            lamport_timestamp: self.lamport_timestamp(),
+            lamport_timestamp: self.text.lamport_clock.tick(),
         })
     }
 
@@ -1084,6 +1097,35 @@ impl Buffer {
         }
     }
 
+    pub fn set_active_selections(
+        &mut self,
+        selections: Arc<[Selection<Anchor>]>,
+        cx: &mut ModelContext<Self>,
+    ) {
+        let lamport_timestamp = self.text.lamport_clock.tick();
+        self.remote_selections
+            .insert(self.text.replica_id(), selections.clone());
+        self.send_operation(
+            Operation::UpdateSelections {
+                replica_id: self.text.replica_id(),
+                selections,
+                lamport_timestamp,
+            },
+            cx,
+        );
+    }
+
+    pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
+        let lamport_timestamp = self.text.lamport_clock.tick();
+        self.send_operation(
+            Operation::RemoveSelections {
+                replica_id: self.text.replica_id(),
+                lamport_timestamp,
+            },
+            cx,
+        );
+    }
+
     fn update_language_server(&mut self) {
         let language_server = if let Some(language_server) = self.language_server.as_mut() {
             language_server
@@ -1321,14 +1363,14 @@ impl Buffer {
                 lamport_timestamp,
             } => {
                 self.remote_selections.insert(replica_id, selections);
-                self.text.observe_lamport_timestamp(lamport_timestamp);
+                self.text.lamport_clock.observe(lamport_timestamp);
             }
             Operation::RemoveSelections {
-                replica_id: set_id,
+                replica_id,
                 lamport_timestamp,
             } => {
-                self.remote_selections.remove(&set_id);
-                self.text.observe_lamport_timestamp(lamport_timestamp);
+                self.remote_selections.remove(&replica_id);
+                self.text.lamport_clock.observe(lamport_timestamp);
             }
         }
     }
@@ -1655,6 +1697,7 @@ impl BufferSnapshot {
     {
         self.remote_selections
             .iter()
+            .filter(|(replica_id, _)| **replica_id != self.text.replica_id())
             .map(move |(replica_id, selections)| {
                 let start_ix = match selections
                     .binary_search_by(|probe| probe.end.cmp(&range.start, self).unwrap())

crates/language/src/proto.rs 🔗

@@ -1,13 +1,12 @@
-use std::sync::Arc;
-
 use crate::{diagnostic_set::DiagnosticEntry, Diagnostic, Operation};
 use anyhow::{anyhow, Result};
 use clock::ReplicaId;
 use lsp::DiagnosticSeverity;
 use rpc::proto;
+use std::sync::Arc;
 use text::*;
 
-pub use proto::Buffer;
+pub use proto::{Buffer, SelectionSet};
 
 pub fn serialize_operation(operation: &Operation) -> proto::Operation {
     proto::Operation {
@@ -48,15 +47,7 @@ pub fn serialize_operation(operation: &Operation) -> proto::Operation {
             } => proto::operation::Variant::UpdateSelections(proto::operation::UpdateSelections {
                 replica_id: *replica_id as u32,
                 lamport_timestamp: lamport_timestamp.value,
-                selections: selections
-                    .iter()
-                    .map(|selection| proto::Selection {
-                        id: selection.id as u64,
-                        start: Some(serialize_anchor(&selection.start)),
-                        end: Some(serialize_anchor(&selection.end)),
-                        reversed: selection.reversed,
-                    })
-                    .collect(),
+                selections: serialize_selections(selections),
             }),
             Operation::RemoveSelections {
                 replica_id,

crates/rpc/proto/zed.proto 🔗

@@ -234,9 +234,7 @@ message Buffer {
 
 message SelectionSet {
     uint32 replica_id = 1;
-    uint32 lamport_timestamp = 2;
-    bool is_active = 3;
-    repeated Selection selections = 4;
+    repeated Selection selections = 2;
 }
 
 message Selection {

crates/rpc/src/rpc.rs 🔗

@@ -5,4 +5,4 @@ pub mod proto;
 pub use conn::Connection;
 pub use peer::*;
 
-pub const PROTOCOL_VERSION: u32 = 3;
+pub const PROTOCOL_VERSION: u32 = 4;

crates/text/src/text.rs 🔗

@@ -49,12 +49,13 @@ pub struct Buffer {
     replica_id: ReplicaId,
     remote_id: u64,
     local_clock: clock::Local,
-    lamport_clock: clock::Lamport,
+    pub lamport_clock: clock::Lamport,
     subscriptions: Topic,
 }
 
 #[derive(Clone, Debug)]
 pub struct BufferSnapshot {
+    replica_id: ReplicaId,
     visible_text: Rope,
     deleted_text: Rope,
     undo_map: UndoMap,
@@ -464,6 +465,7 @@ impl Buffer {
 
         Buffer {
             snapshot: BufferSnapshot {
+                replica_id,
                 visible_text,
                 deleted_text: Rope::new(),
                 fragments,
@@ -495,14 +497,6 @@ impl Buffer {
         self.local_clock.replica_id
     }
 
-    pub fn lamport_timestamp(&self) -> clock::Lamport {
-        self.lamport_clock
-    }
-
-    pub fn observe_lamport_timestamp(&mut self, timestamp: clock::Lamport) {
-        self.lamport_clock.observe(timestamp);
-    }
-
     pub fn remote_id(&self) -> u64 {
         self.remote_id
     }
@@ -1219,6 +1213,10 @@ impl BufferSnapshot {
         &self.visible_text
     }
 
+    pub fn replica_id(&self) -> ReplicaId {
+        self.replica_id
+    }
+
     pub fn row_count(&self) -> u32 {
         self.max_point().row + 1
     }

script/seed-db 🔗

@@ -1,5 +1,4 @@
 #!/bin/bash
 
 set -e
-cd server
-cargo run --features seed-support --bin seed
+cargo run --package=zed-server --features seed-support --bin seed