Replicate fractional component of leader's scroll position

Max Brunsfeld and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/editor/src/editor.rs | 11 ++++++++---
crates/editor/src/items.rs  | 20 ++++++++++++--------
crates/rpc/proto/zed.proto  |  8 ++++++--
3 files changed, 26 insertions(+), 13 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -1039,10 +1039,15 @@ impl Editor {
         cx.notify();
     }
 
-    fn set_scroll_top_anchor(&mut self, anchor: Anchor, local: bool, cx: &mut ViewContext<Self>) {
-        self.scroll_position = Vector2F::zero();
+    fn set_scroll_top_anchor(
+        &mut self,
+        anchor: Anchor,
+        position: Vector2F,
+        cx: &mut ViewContext<Self>,
+    ) {
         self.scroll_top_anchor = anchor;
-        cx.emit(Event::ScrollPositionChanged { local });
+        self.scroll_position = position;
+        cx.emit(Event::ScrollPositionChanged { local: false });
         cx.notify();
     }
 

crates/editor/src/items.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{Anchor, Autoscroll, Editor, Event, ExcerptId, NavigationData, ToOffset, ToPoint as _};
 use anyhow::{anyhow, Result};
 use gpui::{
-    elements::*, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription,
-    Task, View, ViewContext, ViewHandle,
+    elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext,
+    RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
 };
 use language::{Bias, Buffer, Diagnostic, File as _, SelectionGoal};
 use project::{File, Project, ProjectEntryId, ProjectPath};
@@ -68,7 +68,7 @@ impl FollowableItem for Editor {
                     editor.set_selections(selections.into(), None, false, cx);
                 }
 
-                if let Some(anchor) = state.scroll_top {
+                if let Some(anchor) = state.scroll_top_anchor {
                     editor.set_scroll_top_anchor(
                         Anchor {
                             buffer_id: Some(state.buffer_id as usize),
@@ -76,7 +76,7 @@ impl FollowableItem for Editor {
                             text_anchor: language::proto::deserialize_anchor(anchor)
                                 .ok_or_else(|| anyhow!("invalid scroll top"))?,
                         },
-                        false,
+                        vec2f(state.scroll_x, state.scroll_y),
                         cx,
                     );
                 }
@@ -111,9 +111,11 @@ 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: Some(language::proto::serialize_anchor(
+            scroll_top_anchor: Some(language::proto::serialize_anchor(
                 &self.scroll_top_anchor.text_anchor,
             )),
+            scroll_x: self.scroll_position.x(),
+            scroll_y: self.scroll_position.y(),
             selections: self.selections.iter().map(serialize_selection).collect(),
         }))
     }
@@ -130,9 +132,11 @@ impl FollowableItem for Editor {
         match update {
             proto::update_view::Variant::Editor(update) => match event {
                 Event::ScrollPositionChanged { .. } => {
-                    update.scroll_top = Some(language::proto::serialize_anchor(
+                    update.scroll_top_anchor = Some(language::proto::serialize_anchor(
                         &self.scroll_top_anchor.text_anchor,
                     ));
+                    update.scroll_x = self.scroll_position.x();
+                    update.scroll_y = self.scroll_position.y();
                     true
                 }
                 Event::SelectionsChanged { .. } => {
@@ -162,7 +166,7 @@ impl FollowableItem for Editor {
                 let excerpt_id = excerpt_id.clone();
                 drop(buffer);
 
-                if let Some(anchor) = message.scroll_top {
+                if let Some(anchor) = message.scroll_top_anchor {
                     self.set_scroll_top_anchor(
                         Anchor {
                             buffer_id: Some(buffer_id),
@@ -170,7 +174,7 @@ impl FollowableItem for Editor {
                             text_anchor: language::proto::deserialize_anchor(anchor)
                                 .ok_or_else(|| anyhow!("invalid scroll top"))?,
                         },
-                        false,
+                        vec2f(message.scroll_x, message.scroll_y),
                         cx,
                     );
                 }

crates/rpc/proto/zed.proto 🔗

@@ -580,7 +580,9 @@ message UpdateView {
 
     message Editor {
         repeated Selection selections = 1;
-        Anchor scroll_top = 2;
+        Anchor scroll_top_anchor = 2;
+        float scroll_x = 3;
+        float scroll_y = 4;
     }
 }
 
@@ -595,7 +597,9 @@ message View {
     message Editor {
         uint64 buffer_id = 1;
         repeated Selection selections = 2;
-        Anchor scroll_top = 3;
+        Anchor scroll_top_anchor = 3;
+        float scroll_x = 4;
+        float scroll_y = 5;
     }
 }