Merge pull request #663 from zed-industries/following-pending-selections

Max Brunsfeld created

Fix error in follower when leader creates pending selections

Change summary

crates/editor/src/editor.rs | 72 +++++++++++++++++++++++++++++++++++++++
crates/editor/src/items.rs  |  5 +-
2 files changed, 75 insertions(+), 2 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -4903,6 +4903,55 @@ impl Editor {
         );
     }
 
+    pub fn set_selections_from_remote(
+        &mut self,
+        mut selections: Vec<Selection<Anchor>>,
+        cx: &mut ViewContext<Self>,
+    ) {
+        let buffer = self.buffer.read(cx);
+        let buffer = buffer.read(cx);
+        selections.sort_by(|a, b| {
+            a.start
+                .cmp(&b.start, &*buffer)
+                .unwrap()
+                .then_with(|| b.end.cmp(&a.end, &*buffer).unwrap())
+        });
+
+        // Merge overlapping selections
+        let mut i = 1;
+        while i < selections.len() {
+            if selections[i - 1]
+                .end
+                .cmp(&selections[i].start, &*buffer)
+                .unwrap()
+                .is_ge()
+            {
+                let removed = selections.remove(i);
+                if removed
+                    .start
+                    .cmp(&selections[i - 1].start, &*buffer)
+                    .unwrap()
+                    .is_lt()
+                {
+                    selections[i - 1].start = removed.start;
+                }
+                if removed
+                    .end
+                    .cmp(&selections[i - 1].end, &*buffer)
+                    .unwrap()
+                    .is_gt()
+                {
+                    selections[i - 1].end = removed.end;
+                }
+            } else {
+                i += 1;
+            }
+        }
+
+        drop(buffer);
+        self.set_selections(selections.into(), None, false, cx);
+    }
+
     /// Compute new ranges for any selections that were located in excerpts that have
     /// since been removed.
     ///
@@ -9109,6 +9158,29 @@ mod tests {
             assert!(follower.autoscroll_request.is_some());
         });
         assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0]);
+
+        // Creating a pending selection that precedes another selection
+        leader.update(cx, |leader, cx| {
+            leader.select_ranges([1..1], None, cx);
+            leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
+        });
+        follower.update(cx, |follower, cx| {
+            follower
+                .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
+                .unwrap();
+        });
+        assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0, 1..1]);
+
+        // Extend the pending selection so that it surrounds another selection
+        leader.update(cx, |leader, cx| {
+            leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
+        });
+        follower.update(cx, |follower, cx| {
+            follower
+                .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
+                .unwrap();
+        });
+        assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..2]);
     }
 
     #[test]

crates/editor/src/items.rs 🔗

@@ -65,7 +65,7 @@ impl FollowableItem for Editor {
                     })
                     .collect::<Result<Vec<_>>>()?;
                 if !selections.is_empty() {
-                    editor.set_selections(selections.into(), None, false, cx);
+                    editor.set_selections_from_remote(selections.into(), cx);
                 }
 
                 if let Some(anchor) = state.scroll_top_anchor {
@@ -173,8 +173,9 @@ impl FollowableItem for Editor {
                         deserialize_selection(&excerpt_id, buffer_id, selection)
                     })
                     .collect::<Vec<_>>();
+
                 if !selections.is_empty() {
-                    self.set_selections(selections.into(), None, false, cx);
+                    self.set_selections_from_remote(selections, cx);
                     self.request_autoscroll_remotely(Autoscroll::Newest, cx);
                 } else {
                     if let Some(anchor) = message.scroll_top_anchor {