@@ -1866,7 +1866,7 @@ impl ProjectPanel {
) {
let selection = self.find_entry(
self.selection.as_ref(),
- true,
+ false,
|entry, worktree_id| {
(self.selection.is_none()
|| self.selection.is_some_and(|selection| {
@@ -6726,6 +6726,286 @@ mod tests {
);
}
+ #[gpui::test]
+ async fn test_select_git_entry(cx: &mut gpui::TestAppContext) {
+ use git::status::{FileStatus, StatusCode, TrackedStatus};
+ use std::path::Path;
+
+ init_test_with_editor(cx);
+
+ let fs = FakeFs::new(cx.executor().clone());
+ fs.insert_tree(
+ "/root",
+ json!({
+ "tree1": {
+ ".git": {},
+ "dir1": {
+ "modified1.txt": "",
+ "unmodified1.txt": "",
+ "modified2.txt": "",
+ },
+ "dir2": {
+ "modified3.txt": "",
+ "unmodified2.txt": "",
+ },
+ "modified4.txt": "",
+ "unmodified3.txt": "",
+ },
+ "tree2": {
+ ".git": {},
+ "dir3": {
+ "modified5.txt": "",
+ "unmodified4.txt": "",
+ },
+ "modified6.txt": "",
+ "unmodified5.txt": "",
+ }
+ }),
+ )
+ .await;
+
+ // Mark files as git modified
+ let tree1_modified_files = [
+ "dir1/modified1.txt",
+ "dir1/modified2.txt",
+ "modified4.txt",
+ "dir2/modified3.txt",
+ ];
+
+ let tree2_modified_files = ["dir3/modified5.txt", "modified6.txt"];
+
+ let root1_dot_git = Path::new("/root/tree1/.git");
+ let root2_dot_git = Path::new("/root/tree2/.git");
+ let set_value = FileStatus::Tracked(TrackedStatus {
+ index_status: StatusCode::Modified,
+ worktree_status: StatusCode::Modified,
+ });
+
+ fs.with_git_state(&root1_dot_git, true, |git_repo_state| {
+ for file_path in tree1_modified_files {
+ git_repo_state.statuses.insert(file_path.into(), set_value);
+ }
+ });
+
+ fs.with_git_state(&root2_dot_git, true, |git_repo_state| {
+ for file_path in tree2_modified_files {
+ git_repo_state.statuses.insert(file_path.into(), set_value);
+ }
+ });
+
+ let project = Project::test(
+ fs.clone(),
+ ["/root/tree1".as_ref(), "/root/tree2".as_ref()],
+ cx,
+ )
+ .await;
+ let workspace =
+ cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
+ let cx = &mut VisualTestContext::from_window(*workspace, cx);
+ let panel = workspace.update(cx, ProjectPanel::new).unwrap();
+
+ // Check initial state
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..15, cx),
+ &[
+ "v tree1",
+ " > .git",
+ " > dir1",
+ " > dir2",
+ " modified4.txt",
+ " unmodified3.txt",
+ "v tree2",
+ " > .git",
+ " > dir3",
+ " modified6.txt",
+ " unmodified5.txt"
+ ],
+ );
+
+ // Test selecting next modified entry
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_next_git_entry(&SelectNextGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..6, cx),
+ &[
+ "v tree1",
+ " > .git",
+ " v dir1",
+ " modified1.txt <== selected",
+ " modified2.txt",
+ " unmodified1.txt",
+ ],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_next_git_entry(&SelectNextGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..6, cx),
+ &[
+ "v tree1",
+ " > .git",
+ " v dir1",
+ " modified1.txt",
+ " modified2.txt <== selected",
+ " unmodified1.txt",
+ ],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_next_git_entry(&SelectNextGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 6..9, cx),
+ &[
+ " v dir2",
+ " modified3.txt <== selected",
+ " unmodified2.txt",
+ ],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_next_git_entry(&SelectNextGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 9..11, cx),
+ &[" modified4.txt <== selected", " unmodified3.txt",],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_next_git_entry(&SelectNextGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 13..16, cx),
+ &[
+ " v dir3",
+ " modified5.txt <== selected",
+ " unmodified4.txt",
+ ],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_next_git_entry(&SelectNextGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 16..18, cx),
+ &[" modified6.txt <== selected", " unmodified5.txt",],
+ );
+
+ // Wraps around to first modified file
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_next_git_entry(&SelectNextGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..18, cx),
+ &[
+ "v tree1",
+ " > .git",
+ " v dir1",
+ " modified1.txt <== selected",
+ " modified2.txt",
+ " unmodified1.txt",
+ " v dir2",
+ " modified3.txt",
+ " unmodified2.txt",
+ " modified4.txt",
+ " unmodified3.txt",
+ "v tree2",
+ " > .git",
+ " v dir3",
+ " modified5.txt",
+ " unmodified4.txt",
+ " modified6.txt",
+ " unmodified5.txt",
+ ],
+ );
+
+ // Wraps around again to last modified file
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_prev_git_entry(&SelectPrevGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 16..18, cx),
+ &[" modified6.txt <== selected", " unmodified5.txt",],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_prev_git_entry(&SelectPrevGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 13..16, cx),
+ &[
+ " v dir3",
+ " modified5.txt <== selected",
+ " unmodified4.txt",
+ ],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_prev_git_entry(&SelectPrevGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 9..11, cx),
+ &[" modified4.txt <== selected", " unmodified3.txt",],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_prev_git_entry(&SelectPrevGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 6..9, cx),
+ &[
+ " v dir2",
+ " modified3.txt <== selected",
+ " unmodified2.txt",
+ ],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_prev_git_entry(&SelectPrevGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..6, cx),
+ &[
+ "v tree1",
+ " > .git",
+ " v dir1",
+ " modified1.txt",
+ " modified2.txt <== selected",
+ " unmodified1.txt",
+ ],
+ );
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.select_prev_git_entry(&SelectPrevGitEntry, window, cx);
+ });
+
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..6, cx),
+ &[
+ "v tree1",
+ " > .git",
+ " v dir1",
+ " modified1.txt <== selected",
+ " modified2.txt",
+ " unmodified1.txt",
+ ],
+ );
+ }
+
#[gpui::test]
async fn test_select_directory(cx: &mut gpui::TestAppContext) {
init_test_with_editor(cx);