Fix panic when doing various cursor movements with a pending mouse selection (#13016)

Max Brunsfeld created

This fixes a panic in the `SelectionsCollection::first_anchor` when
there was a pending mouse selection and no other selections. Until
recently, this method was only used in vim mode, but as of
https://github.com/zed-industries/zed/commit/53b0720d546b6541fbf236d2f9d2dadbce6f126e,
it's also used in the normal `move_up` and `move_down` actions.

So until recently, the panic that this fixes could only happen in vim
mode.

Release Notes:

- Fixed a crash that could happen when using certain cursor-motion
bindings with a pending mouse selection.

Change summary

crates/editor/src/editor_tests.rs          | 36 ++++++++++++++++++++++++
crates/editor/src/selections_collection.rs |  5 ++
2 files changed, 40 insertions(+), 1 deletion(-)

Detailed changes

crates/editor/src/editor_tests.rs 🔗

@@ -481,6 +481,42 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
     });
 }
 
+#[gpui::test]
+fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
+    init_test(cx, |_| {});
+
+    let view = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+        build_editor(buffer, cx)
+    });
+
+    _ = view.update(cx, |view, cx| {
+        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
+        assert_eq!(
+            view.selections.display_ranges(cx),
+            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
+        );
+
+        view.move_down(&Default::default(), cx);
+        assert_eq!(
+            view.selections.display_ranges(cx),
+            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
+        );
+
+        view.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, cx);
+        assert_eq!(
+            view.selections.display_ranges(cx),
+            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
+        );
+
+        view.move_up(&Default::default(), cx);
+        assert_eq!(
+            view.selections.display_ranges(cx),
+            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
+        );
+    });
+}
+
 #[gpui::test]
 fn test_clone(cx: &mut TestAppContext) {
     init_test(cx, |_| {});

crates/editor/src/selections_collection.rs 🔗

@@ -256,7 +256,10 @@ impl SelectionsCollection {
     }
 
     pub fn first_anchor(&self) -> Selection<Anchor> {
-        self.disjoint[0].clone()
+        self.pending
+            .as_ref()
+            .map(|pending| pending.selection.clone())
+            .unwrap_or_else(|| self.disjoint.first().cloned().unwrap())
     }
 
     pub fn first<D: TextDimension + Ord + Sub<D, Output = D>>(