diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 4dab8ae4742ba9bf14df3393f60ddec752eaf47e..8f7dac889d13b6ff1d80557811da555b1b7216a3 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -16518,7 +16518,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) { leader.update(cx, |leader, cx| { leader.buffer.update(cx, |multibuffer, cx| { multibuffer.set_excerpts_for_path( - PathKey::namespaced(1, rel_path("b.txt").into_arc()), + PathKey::with_sort_prefix(1, rel_path("b.txt").into_arc()), buffer_1.clone(), vec![ Point::row_range(0..3), @@ -16529,7 +16529,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) { cx, ); multibuffer.set_excerpts_for_path( - PathKey::namespaced(1, rel_path("a.txt").into_arc()), + PathKey::with_sort_prefix(1, rel_path("a.txt").into_arc()), buffer_2.clone(), vec![Point::row_range(0..6), Point::row_range(8..12)], 0, @@ -21032,7 +21032,7 @@ async fn test_display_diff_hunks(cx: &mut TestAppContext) { for buffer in &buffers { let snapshot = buffer.read(cx).snapshot(); multibuffer.set_excerpts_for_path( - PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()), + PathKey::with_sort_prefix(0, buffer.read(cx).file().unwrap().path().clone()), buffer.clone(), vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)], 2, diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index a87db56c968ce5f8347eda801e99900326ede5ad..201a699e2f0e8527ed62babdc941febcf9426a2d 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -43,8 +43,8 @@ struct CommitMetadataFile { worktree_id: WorktreeId, } -const COMMIT_METADATA_NAMESPACE: u64 = 0; -const FILE_NAMESPACE: u64 = 1; +const COMMIT_METADATA_SORT_PREFIX: u64 = 0; +const FILE_NAMESPACE_SORT_PREFIX: u64 = 1; impl CommitView { pub fn open( @@ -145,7 +145,7 @@ impl CommitView { }); multibuffer.update(cx, |multibuffer, cx| { multibuffer.set_excerpts_for_path( - PathKey::namespaced(COMMIT_METADATA_NAMESPACE, file.title.clone()), + PathKey::with_sort_prefix(COMMIT_METADATA_SORT_PREFIX, file.title.clone()), buffer.clone(), vec![Point::zero()..buffer.read(cx).max_point()], 0, @@ -193,7 +193,7 @@ impl CommitView { .collect::>(); let path = snapshot.file().unwrap().path().clone(); let _is_newly_added = multibuffer.set_excerpts_for_path( - PathKey::namespaced(FILE_NAMESPACE, path), + PathKey::with_sort_prefix(FILE_NAMESPACE_SORT_PREFIX, path), buffer, diff_hunk_ranges, multibuffer_context_lines(cx), diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index d8e15247c10b7cfc71b0c495689a1f969d03f046..ce6ddf43f6dbf1e4df32c45f7c96f7c08447df06 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -4973,6 +4973,7 @@ mod tests { use settings::SettingsStore; use theme::LoadThemes; use util::path; + use util::rel_path::rel_path; use super::*; @@ -5595,6 +5596,68 @@ mod tests { }); } + #[gpui::test] + async fn test_open_diff(cx: &mut TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.background_executor.clone()); + fs.insert_tree( + path!("/project"), + json!({ + ".git": {}, + "tracked": "tracked\n", + "untracked": "\n", + }), + ) + .await; + + fs.set_head_and_index_for_repo( + path!("/project/.git").as_ref(), + &[("tracked", "old tracked\n".into())], + ); + + let project = Project::test(fs.clone(), [Path::new(path!("/project"))], 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, GitPanel::new).unwrap(); + + // Enable the `sort_by_path` setting and wait for entries to be updated, + // as there should no longer be separators between Tracked and Untracked + // files. + cx.update(|_window, cx| { + SettingsStore::update_global(cx, |store, cx| { + store.update_user_settings(cx, |settings| { + settings.git_panel.get_or_insert_default().sort_by_path = Some(true); + }) + }); + }); + + cx.update_window_entity(&panel, |panel, _, _| { + std::mem::replace(&mut panel.update_visible_entries_task, Task::ready(())) + }) + .await; + + // Confirm that `Open Diff` still works for the untracked file, updating + // the Project Diff's active path. + panel.update_in(cx, |panel, window, cx| { + panel.selected_entry = Some(1); + panel.open_diff(&Confirm, window, cx); + }); + cx.run_until_parked(); + + let _ = workspace.update(cx, |workspace, _window, cx| { + let active_path = workspace + .item_of_type::(cx) + .expect("ProjectDiff should exist") + .read(cx) + .active_path(cx) + .expect("active_path should exist"); + + assert_eq!(active_path.path, rel_path("untracked").into_arc()); + }); + } + fn assert_entry_paths(entries: &[GitListEntry], expected_paths: &[Option<&str>]) { assert_eq!(entries.len(), expected_paths.len()); for (entry, expected_path) in entries.iter().zip(expected_paths) { diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 0e59d25222b51d9df5afb1f40967b42b7e53cb20..6b70f1975e8f361b04fb2ce2eb4966b5da968936 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -16,7 +16,7 @@ use editor::{ use futures::StreamExt; use git::{ Commit, StageAll, StageAndNext, ToggleStaged, UnstageAll, UnstageAndNext, - repository::{Branch, Upstream, UpstreamTracking, UpstreamTrackingStatus}, + repository::{Branch, RepoPath, Upstream, UpstreamTracking, UpstreamTrackingStatus}, status::FileStatus, }; use gpui::{ @@ -27,7 +27,7 @@ use language::{Anchor, Buffer, Capability, OffsetRangeExt}; use multi_buffer::{MultiBuffer, PathKey}; use project::{ Project, ProjectPath, - git_store::{GitStore, GitStoreEvent}, + git_store::{GitStore, GitStoreEvent, Repository}, }; use settings::{Settings, SettingsStore}; use std::any::{Any, TypeId}; @@ -73,9 +73,9 @@ struct DiffBuffer { file_status: FileStatus, } -const CONFLICT_NAMESPACE: u64 = 1; -const TRACKED_NAMESPACE: u64 = 2; -const NEW_NAMESPACE: u64 = 3; +const CONFLICT_SORT_PREFIX: u64 = 1; +const TRACKED_SORT_PREFIX: u64 = 2; +const NEW_SORT_PREFIX: u64 = 3; impl ProjectDiff { pub(crate) fn register(workspace: &mut Workspace, cx: &mut Context) { @@ -234,16 +234,8 @@ impl ProjectDiff { return; }; let repo = git_repo.read(cx); - - let namespace = if repo.had_conflict_on_last_merge_head_change(&entry.repo_path) { - CONFLICT_NAMESPACE - } else if entry.status.is_created() { - NEW_NAMESPACE - } else { - TRACKED_NAMESPACE - }; - - let path_key = PathKey::namespaced(namespace, entry.repo_path.0); + let sort_prefix = sort_prefix(repo, &entry.repo_path, entry.status, cx); + let path_key = PathKey::with_sort_prefix(sort_prefix, entry.repo_path.0); self.move_to_path(path_key, window, cx) } @@ -388,16 +380,8 @@ impl ProjectDiff { else { continue; }; - let namespace = if GitPanelSettings::get_global(cx).sort_by_path { - TRACKED_NAMESPACE - } else if repo.had_conflict_on_last_merge_head_change(&entry.repo_path) { - CONFLICT_NAMESPACE - } else if entry.status.is_created() { - NEW_NAMESPACE - } else { - TRACKED_NAMESPACE - }; - let path_key = PathKey::namespaced(namespace, entry.repo_path.0.clone()); + let sort_prefix = sort_prefix(repo, &entry.repo_path, entry.status, cx); + let path_key = PathKey::with_sort_prefix(sort_prefix, entry.repo_path.0.clone()); previous_paths.remove(&path_key); let load_buffer = self @@ -541,6 +525,18 @@ impl ProjectDiff { } } +fn sort_prefix(repo: &Repository, repo_path: &RepoPath, status: FileStatus, cx: &App) -> u64 { + if GitPanelSettings::get_global(cx).sort_by_path { + TRACKED_SORT_PREFIX + } else if repo.had_conflict_on_last_merge_head_change(repo_path) { + CONFLICT_SORT_PREFIX + } else if status.is_created() { + NEW_SORT_PREFIX + } else { + TRACKED_SORT_PREFIX + } +} + impl EventEmitter for ProjectDiff {} impl Focusable for ProjectDiff { @@ -1463,7 +1459,7 @@ mod tests { let editor = cx.update_window_entity(&diff, |diff, window, cx| { diff.move_to_path( - PathKey::namespaced(TRACKED_NAMESPACE, rel_path("foo").into_arc()), + PathKey::with_sort_prefix(TRACKED_SORT_PREFIX, rel_path("foo").into_arc()), window, cx, ); @@ -1484,7 +1480,7 @@ mod tests { let editor = cx.update_window_entity(&diff, |diff, window, cx| { diff.move_to_path( - PathKey::namespaced(TRACKED_NAMESPACE, rel_path("bar").into_arc()), + PathKey::with_sort_prefix(TRACKED_SORT_PREFIX, rel_path("bar").into_arc()), window, cx, ); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 31ffde7bb9494ab0835626cdce88e99a9ad86f3c..be01c4b6a1f9f67703f99bcd0b9b331574a6b360 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -161,24 +161,25 @@ impl MultiBufferDiffHunk { #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)] pub struct PathKey { - namespace: Option, + // Used by the derived PartialOrd & Ord + sort_prefix: Option, path: Arc, } impl PathKey { - pub fn namespaced(namespace: u64, path: Arc) -> Self { + pub fn with_sort_prefix(sort_prefix: u64, path: Arc) -> Self { Self { - namespace: Some(namespace), + sort_prefix: Some(sort_prefix), path, } } pub fn for_buffer(buffer: &Entity, cx: &App) -> Self { if let Some(file) = buffer.read(cx).file() { - Self::namespaced(file.worktree_id(cx).to_proto(), file.path().clone()) + Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone()) } else { Self { - namespace: None, + sort_prefix: None, path: RelPath::unix(&buffer.entity_id().to_string()) .unwrap() .into_arc(), diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index e82c44d644ba5204412418b473335aec76851faf..bad99d5412cd009ecaeabe9c6ad7686f07d30862 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -1525,7 +1525,7 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) { cx, ) }); - let path1: PathKey = PathKey::namespaced(0, rel_path("root").into_arc()); + let path1: PathKey = PathKey::with_sort_prefix(0, rel_path("root").into_arc()); let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); multibuffer.update(cx, |multibuffer, cx| { @@ -1620,7 +1620,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) { cx, ) }); - let path1: PathKey = PathKey::namespaced(0, rel_path("root").into_arc()); + let path1: PathKey = PathKey::with_sort_prefix(0, rel_path("root").into_arc()); let buf2 = cx.new(|cx| { Buffer::local( indoc! { @@ -1639,7 +1639,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) { cx, ) }); - let path2 = PathKey::namespaced(1, rel_path("root").into_arc()); + let path2 = PathKey::with_sort_prefix(1, rel_path("root").into_arc()); let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); multibuffer.update(cx, |multibuffer, cx| { @@ -1816,7 +1816,7 @@ fn test_set_excerpts_for_buffer_rename(cx: &mut TestAppContext) { cx, ) }); - let path: PathKey = PathKey::namespaced(0, rel_path("root").into_arc()); + let path: PathKey = PathKey::with_sort_prefix(0, rel_path("root").into_arc()); let buf2 = cx.new(|cx| { Buffer::local( indoc! {