Keep cursor at top when diff view is first opened (#25682)

João Marcos and Max Brunsfeld created

Previously, we had the cursor at the bottom while the scroll stayed at
the top.

Now, if you run `git: diff`, the cursor will also be at the top.

The cursor moving to the end was possibly a side-effect of using
`Bias::Right` for selections.

---

Release Notes:

- N/A

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

crates/git_ui/src/project_diff.rs | 39 +++++++++++++++++++-------------
1 file changed, 23 insertions(+), 16 deletions(-)

Detailed changes

crates/git_ui/src/project_diff.rs 🔗

@@ -106,7 +106,7 @@ impl ProjectDiff {
         };
         if let Some(entry) = entry {
             project_diff.update(cx, |project_diff, cx| {
-                project_diff.scroll_to(entry, window, cx);
+                project_diff.move_to_entry(entry, window, cx);
             })
         }
     }
@@ -168,7 +168,7 @@ impl ProjectDiff {
         }
     }
 
-    pub fn scroll_to(
+    pub fn move_to_entry(
         &mut self,
         entry: GitStatusEntry,
         window: &mut Window,
@@ -189,16 +189,16 @@ impl ProjectDiff {
 
         let path_key = PathKey::namespaced(namespace, entry.repo_path.0.clone());
 
-        self.scroll_to_path(path_key, window, cx)
+        self.move_to_path(path_key, window, cx)
     }
 
-    fn scroll_to_path(&mut self, path_key: PathKey, window: &mut Window, cx: &mut Context<Self>) {
+    fn move_to_path(&mut self, path_key: PathKey, window: &mut Window, cx: &mut Context<Self>) {
         if let Some(position) = self.multibuffer.read(cx).location_for_path(&path_key, cx) {
             self.editor.update(cx, |editor, cx| {
                 editor.change_selections(Some(Autoscroll::focused()), window, cx, |s| {
                     s.select_ranges([position..position]);
                 })
-            })
+            });
         } else {
             self.pending_scroll = Some(path_key);
         }
@@ -385,21 +385,28 @@ impl ProjectDiff {
                 .collect::<Vec<_>>()
         };
 
-        let is_excerpt_newly_added = self.multibuffer.update(cx, |multibuffer, cx| {
-            multibuffer.set_excerpts_for_path(
+        let (was_empty, is_excerpt_newly_added) = self.multibuffer.update(cx, |multibuffer, cx| {
+            let was_empty = multibuffer.is_empty();
+            let is_newly_added = multibuffer.set_excerpts_for_path(
                 path_key.clone(),
                 buffer,
                 diff_hunk_ranges,
                 editor::DEFAULT_MULTIBUFFER_CONTEXT,
                 cx,
-            )
+            );
+            (was_empty, is_newly_added)
         });
 
-        if is_excerpt_newly_added && diff_buffer.file_status.is_deleted() {
-            self.editor.update(cx, |editor, cx| {
+        self.editor.update(cx, |editor, cx| {
+            if was_empty {
+                editor.change_selections(None, window, cx, |selections| {
+                    selections.select_ranges([0..0])
+                });
+            }
+            if is_excerpt_newly_added && diff_buffer.file_status.is_deleted() {
                 editor.fold_buffer(snapshot.text.remote_id(), cx)
-            });
-        }
+            }
+        });
 
         if self.multibuffer.read(cx).is_empty()
             && self
@@ -415,7 +422,7 @@ impl ProjectDiff {
             });
         }
         if self.pending_scroll.as_ref() == Some(&path_key) {
-            self.scroll_to_path(path_key, window, cx);
+            self.move_to_path(path_key, window, cx);
         }
     }
 
@@ -995,13 +1002,13 @@ mod tests {
             cx,
             &"
                 - foo
-                + FOO
-                  ˇ"
+                + ˇFOO
+            "
             .unindent(),
         );
 
         editor.update_in(cx, |editor, window, cx| {
-            editor.restore_file(&Default::default(), window, cx);
+            editor.git_restore(&Default::default(), window, cx);
         });
         fs.with_git_state(path!("/project/.git").as_ref(), true, |state| {
             state.statuses = HashMap::default();