Send full multibuffer anchors to following peers

Max Brunsfeld created

Change summary

crates/editor/src/items.rs        | 94 +++++++++++++-------------------
crates/editor/src/multi_buffer.rs | 12 ++++
crates/language/src/proto.rs      | 20 ++++--
crates/rpc/proto/zed.proto        | 21 +++----
4 files changed, 73 insertions(+), 74 deletions(-)

Detailed changes

crates/editor/src/items.rs 🔗

@@ -65,34 +65,26 @@ impl FollowableItem for Editor {
                     })
                 });
             editor.update(&mut cx, |editor, cx| {
-                let excerpt_id;
-                let buffer_id;
-                {
-                    let buffer = editor.buffer.read(cx).read(cx);
-                    let singleton = buffer.as_singleton().unwrap();
-                    excerpt_id = singleton.0.clone();
-                    buffer_id = singleton.1;
-                }
+                let buffer = editor.buffer.read(cx).read(cx);
                 let selections = state
                     .selections
                     .into_iter()
                     .map(|selection| {
-                        deserialize_selection(&excerpt_id, buffer_id, selection)
+                        deserialize_selection(&buffer, selection)
                             .ok_or_else(|| anyhow!("invalid selection"))
                     })
                     .collect::<Result<Vec<_>>>()?;
+                let scroll_top_anchor = state
+                    .scroll_top_anchor
+                    .and_then(|anchor| deserialize_anchor(&buffer, anchor));
+                drop(buffer);
+
                 if !selections.is_empty() {
                     editor.set_selections_from_remote(selections, cx);
                 }
-
-                if let Some(anchor) = state.scroll_top_anchor {
+                if let Some(scroll_top_anchor) = scroll_top_anchor {
                     editor.set_scroll_top_anchor(
-                        Anchor {
-                            buffer_id: Some(state.buffer_id as usize),
-                            excerpt_id,
-                            text_anchor: language::proto::deserialize_anchor(anchor)
-                                .ok_or_else(|| anyhow!("invalid scroll top"))?,
-                        },
+                        scroll_top_anchor,
                         vec2f(state.scroll_x, state.scroll_y),
                         cx,
                     );
@@ -133,9 +125,7 @@ impl FollowableItem for Editor {
         let buffer_id = self.buffer.read(cx).as_singleton()?.read(cx).remote_id();
         Some(proto::view::Variant::Editor(proto::view::Editor {
             buffer_id,
-            scroll_top_anchor: Some(language::proto::serialize_anchor(
-                &self.scroll_top_anchor.text_anchor,
-            )),
+            scroll_top_anchor: Some(serialize_anchor(&self.scroll_top_anchor)),
             scroll_x: self.scroll_position.x(),
             scroll_y: self.scroll_position.y(),
             selections: self
@@ -159,9 +149,7 @@ impl FollowableItem for Editor {
         match update {
             proto::update_view::Variant::Editor(update) => match event {
                 Event::ScrollPositionChanged { .. } => {
-                    update.scroll_top_anchor = Some(language::proto::serialize_anchor(
-                        &self.scroll_top_anchor.text_anchor,
-                    ));
+                    update.scroll_top_anchor = Some(serialize_anchor(&self.scroll_top_anchor));
                     update.scroll_x = self.scroll_position.x();
                     update.scroll_y = self.scroll_position.y();
                     true
@@ -190,29 +178,22 @@ impl FollowableItem for Editor {
             update_view::Variant::Editor(message) => {
                 let buffer = self.buffer.read(cx);
                 let buffer = buffer.read(cx);
-                let (excerpt_id, buffer_id, _) = buffer.as_singleton().unwrap();
-                let excerpt_id = excerpt_id.clone();
-                drop(buffer);
-
                 let selections = message
                     .selections
                     .into_iter()
-                    .filter_map(|selection| {
-                        deserialize_selection(&excerpt_id, buffer_id, selection)
-                    })
+                    .filter_map(|selection| deserialize_selection(&buffer, selection))
                     .collect::<Vec<_>>();
+                let scroll_top_anchor = message
+                    .scroll_top_anchor
+                    .and_then(|anchor| deserialize_anchor(&buffer, anchor));
+                drop(buffer);
 
                 if !selections.is_empty() {
                     self.set_selections_from_remote(selections, cx);
                     self.request_autoscroll_remotely(Autoscroll::newest(), cx);
-                } else if let Some(anchor) = message.scroll_top_anchor {
+                } else if let Some(anchor) = scroll_top_anchor {
                     self.set_scroll_top_anchor(
-                        Anchor {
-                            buffer_id: Some(buffer_id),
-                            excerpt_id,
-                            text_anchor: language::proto::deserialize_anchor(anchor)
-                                .ok_or_else(|| anyhow!("invalid scroll top"))?,
-                        },
+                        anchor,
                         vec2f(message.scroll_x, message.scroll_y),
                         cx,
                     );
@@ -235,38 +216,41 @@ impl FollowableItem for Editor {
 fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
     proto::Selection {
         id: selection.id as u64,
-        start: Some(language::proto::serialize_anchor(
-            &selection.start.text_anchor,
-        )),
-        end: Some(language::proto::serialize_anchor(
-            &selection.end.text_anchor,
-        )),
+        start: Some(serialize_anchor(&selection.start)),
+        end: Some(serialize_anchor(&selection.end)),
         reversed: selection.reversed,
     }
 }
 
+fn serialize_anchor(anchor: &Anchor) -> proto::EditorAnchor {
+    proto::EditorAnchor {
+        excerpt_id: anchor.excerpt_id.to_proto(),
+        anchor: Some(language::proto::serialize_anchor(&anchor.text_anchor)),
+    }
+}
+
 fn deserialize_selection(
-    excerpt_id: &ExcerptId,
-    buffer_id: usize,
+    buffer: &MultiBufferSnapshot,
     selection: proto::Selection,
 ) -> Option<Selection<Anchor>> {
     Some(Selection {
         id: selection.id as usize,
-        start: Anchor {
-            buffer_id: Some(buffer_id),
-            excerpt_id: excerpt_id.clone(),
-            text_anchor: language::proto::deserialize_anchor(selection.start?)?,
-        },
-        end: Anchor {
-            buffer_id: Some(buffer_id),
-            excerpt_id: excerpt_id.clone(),
-            text_anchor: language::proto::deserialize_anchor(selection.end?)?,
-        },
+        start: deserialize_anchor(buffer, selection.start?)?,
+        end: deserialize_anchor(buffer, selection.end?)?,
         reversed: selection.reversed,
         goal: SelectionGoal::None,
     })
 }
 
+fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor) -> Option<Anchor> {
+    let excerpt_id = ExcerptId::from_proto(anchor.excerpt_id);
+    Some(Anchor {
+        excerpt_id,
+        text_anchor: language::proto::deserialize_anchor(anchor.anchor?)?,
+        buffer_id: Some(buffer.buffer_id_for_excerpt(excerpt_id)?),
+    })
+}
+
 impl Item for Editor {
     fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
         if let Ok(data) = data.downcast::<NavigationData>() {

crates/editor/src/multi_buffer.rs 🔗

@@ -2813,6 +2813,10 @@ impl MultiBufferSnapshot {
         }
     }
 
+    pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<usize> {
+        Some(self.excerpt(excerpt_id)?.buffer_id)
+    }
+
     fn excerpt<'a>(&'a self, excerpt_id: ExcerptId) -> Option<&'a Excerpt> {
         let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
         let locator = self.excerpt_locator_for_id(excerpt_id);
@@ -3147,6 +3151,14 @@ impl ExcerptId {
         Self(usize::MAX)
     }
 
+    pub fn to_proto(&self) -> u64 {
+        self.0 as _
+    }
+
+    pub fn from_proto(proto: u64) -> Self {
+        Self(proto as _)
+    }
+
     pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering {
         let a = snapshot.excerpt_locator_for_id(*self);
         let b = snapshot.excerpt_locator_for_id(*other);

crates/language/src/proto.rs 🔗

@@ -9,7 +9,7 @@ use rpc::proto;
 use std::{ops::Range, sync::Arc};
 use text::*;
 
-pub use proto::{BufferState, Operation, SelectionSet};
+pub use proto::{BufferState, Operation};
 
 pub fn deserialize_line_ending(message: proto::LineEnding) -> fs::LineEnding {
     match message {
@@ -122,8 +122,14 @@ pub fn serialize_selections(selections: &Arc<[Selection<Anchor>]>) -> Vec<proto:
 pub fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
     proto::Selection {
         id: selection.id as u64,
-        start: Some(serialize_anchor(&selection.start)),
-        end: Some(serialize_anchor(&selection.end)),
+        start: Some(proto::EditorAnchor {
+            anchor: Some(serialize_anchor(&selection.start)),
+            excerpt_id: 0,
+        }),
+        end: Some(proto::EditorAnchor {
+            anchor: Some(serialize_anchor(&selection.end)),
+            excerpt_id: 0,
+        }),
         reversed: selection.reversed,
     }
 }
@@ -229,8 +235,8 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
                     .filter_map(|selection| {
                         Some(Selection {
                             id: selection.id as usize,
-                            start: deserialize_anchor(selection.start?)?,
-                            end: deserialize_anchor(selection.end?)?,
+                            start: deserialize_anchor(selection.start?.anchor?)?,
+                            end: deserialize_anchor(selection.end?.anchor?)?,
                             reversed: selection.reversed,
                             goal: SelectionGoal::None,
                         })
@@ -321,8 +327,8 @@ pub fn deserialize_selections(selections: Vec<proto::Selection>) -> Arc<[Selecti
 pub fn deserialize_selection(selection: proto::Selection) -> Option<Selection<Anchor>> {
     Some(Selection {
         id: selection.id as usize,
-        start: deserialize_anchor(selection.start?)?,
-        end: deserialize_anchor(selection.end?)?,
+        start: deserialize_anchor(selection.start?.anchor?)?,
+        end: deserialize_anchor(selection.end?.anchor?)?,
         reversed: selection.reversed,
         goal: SelectionGoal::None,
     })

crates/rpc/proto/zed.proto 🔗

@@ -848,7 +848,7 @@ message UpdateView {
 
     message Editor {
         repeated Selection selections = 1;
-        Anchor scroll_top_anchor = 2;
+        EditorAnchor scroll_top_anchor = 2;
         float scroll_x = 3;
         float scroll_y = 4;
     }
@@ -865,7 +865,7 @@ message View {
     message Editor {
         uint64 buffer_id = 1;
         repeated Selection selections = 2;
-        Anchor scroll_top_anchor = 3;
+        EditorAnchor scroll_top_anchor = 3;
         float scroll_x = 4;
         float scroll_y = 5;
     }
@@ -920,21 +920,18 @@ enum LineEnding {
     Windows = 1;
 }
 
-message SelectionSet {
-    uint32 replica_id = 1;
-    repeated Selection selections = 2;
-    uint32 lamport_timestamp = 3;
-    bool line_mode = 4;
-    CursorShape cursor_shape = 5;
-}
-
 message Selection {
     uint64 id = 1;
-    Anchor start = 2;
-    Anchor end = 3;
+    EditorAnchor start = 2;
+    EditorAnchor end = 3;
     bool reversed = 4;
 }
 
+message EditorAnchor {
+    uint64 excerpt_id = 1;
+    Anchor anchor = 2;
+}
+
 enum CursorShape {
     CursorBar = 0;
     CursorBlock = 1;