Fix ordering of multibuffer excerpts (#39476)
The ordering of path-based excerpts in multibuffers regressed with #38744, because we changed the `path` field of `PathKey` to be a string (from `std::path::Path`) and used the derived `Ord` implementation, which doesn't agree with the path-based order of worktree traversals. This PR fixes that by using `RelPath` for `PathKey`. Instead of using `File::full_path`, which can be absolute, we always use `File::path` and distinguish different worktrees using their ID. Release Notes: - N/A --------- Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
This commit is contained in:
co-authored by
Lukas Wirth
parent
2859cbdba9
commit
aced13bc9f
@@ -84,7 +84,11 @@ async fn test_project_diff(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext)
|
||||
diff.update(cx_b, |diff, cx| {
|
||||
assert_eq!(
|
||||
diff.excerpt_paths(cx),
|
||||
vec!["changed.txt", "deleted.txt", "created.txt"]
|
||||
vec![
|
||||
rel_path("changed.txt").into_arc(),
|
||||
rel_path("deleted.txt").into_arc(),
|
||||
rel_path("created.txt").into_arc()
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -121,7 +125,11 @@ async fn test_project_diff(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext)
|
||||
diff.update(cx_b, |diff, cx| {
|
||||
assert_eq!(
|
||||
diff.excerpt_paths(cx),
|
||||
vec!["deleted.txt", "unchanged.txt", "created.txt"]
|
||||
vec![
|
||||
rel_path("deleted.txt").into_arc(),
|
||||
rel_path("unchanged.txt").into_arc(),
|
||||
rel_path("created.txt").into_arc()
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16515,7 +16515,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, "b.txt".into()),
|
||||
PathKey::namespaced(1, rel_path("b.txt").into_arc()),
|
||||
buffer_1.clone(),
|
||||
vec![
|
||||
Point::row_range(0..3),
|
||||
@@ -16526,7 +16526,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
|
||||
cx,
|
||||
);
|
||||
multibuffer.set_excerpts_for_path(
|
||||
PathKey::namespaced(1, "a.txt".into()),
|
||||
PathKey::namespaced(1, rel_path("a.txt").into_arc()),
|
||||
buffer_2.clone(),
|
||||
vec![Point::row_range(0..6), Point::row_range(8..12)],
|
||||
0,
|
||||
@@ -21025,10 +21025,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().as_unix_str().into(),
|
||||
),
|
||||
PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
|
||||
buffer.clone(),
|
||||
vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
|
||||
2,
|
||||
|
||||
@@ -43,8 +43,8 @@ struct CommitMetadataFile {
|
||||
worktree_id: WorktreeId,
|
||||
}
|
||||
|
||||
const COMMIT_METADATA_NAMESPACE: u32 = 0;
|
||||
const FILE_NAMESPACE: u32 = 1;
|
||||
const COMMIT_METADATA_NAMESPACE: u64 = 0;
|
||||
const FILE_NAMESPACE: 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.as_unix_str().into()),
|
||||
PathKey::namespaced(COMMIT_METADATA_NAMESPACE, file.title.clone()),
|
||||
buffer.clone(),
|
||||
vec![Point::zero()..buffer.read(cx).max_point()],
|
||||
0,
|
||||
@@ -193,7 +193,7 @@ impl CommitView {
|
||||
.collect::<Vec<_>>();
|
||||
let path = snapshot.file().unwrap().path().clone();
|
||||
let _is_newly_added = multibuffer.set_excerpts_for_path(
|
||||
PathKey::namespaced(FILE_NAMESPACE, path.as_unix_str().into()),
|
||||
PathKey::namespaced(FILE_NAMESPACE, path),
|
||||
buffer,
|
||||
diff_hunk_ranges,
|
||||
multibuffer_context_lines(cx),
|
||||
|
||||
@@ -73,9 +73,9 @@ struct DiffBuffer {
|
||||
file_status: FileStatus,
|
||||
}
|
||||
|
||||
const CONFLICT_NAMESPACE: u32 = 1;
|
||||
const TRACKED_NAMESPACE: u32 = 2;
|
||||
const NEW_NAMESPACE: u32 = 3;
|
||||
const CONFLICT_NAMESPACE: u64 = 1;
|
||||
const TRACKED_NAMESPACE: u64 = 2;
|
||||
const NEW_NAMESPACE: u64 = 3;
|
||||
|
||||
impl ProjectDiff {
|
||||
pub(crate) fn register(workspace: &mut Workspace, cx: &mut Context<Workspace>) {
|
||||
@@ -243,7 +243,7 @@ impl ProjectDiff {
|
||||
TRACKED_NAMESPACE
|
||||
};
|
||||
|
||||
let path_key = PathKey::namespaced(namespace, entry.repo_path.as_unix_str().into());
|
||||
let path_key = PathKey::namespaced(namespace, entry.repo_path.0);
|
||||
|
||||
self.move_to_path(path_key, window, cx)
|
||||
}
|
||||
@@ -397,7 +397,7 @@ impl ProjectDiff {
|
||||
} else {
|
||||
TRACKED_NAMESPACE
|
||||
};
|
||||
let path_key = PathKey::namespaced(namespace, entry.repo_path.as_unix_str().into());
|
||||
let path_key = PathKey::namespaced(namespace, entry.repo_path.0.clone());
|
||||
|
||||
previous_paths.remove(&path_key);
|
||||
let load_buffer = self
|
||||
@@ -531,11 +531,12 @@ impl ProjectDiff {
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn excerpt_paths(&self, cx: &App) -> Vec<String> {
|
||||
pub fn excerpt_paths(&self, cx: &App) -> Vec<std::sync::Arc<util::rel_path::RelPath>> {
|
||||
self.multibuffer
|
||||
.read(cx)
|
||||
.excerpt_paths()
|
||||
.map(|key| key.path().to_string())
|
||||
.map(|key| key.path())
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -1361,7 +1362,7 @@ mod tests {
|
||||
use settings::SettingsStore;
|
||||
use std::path::Path;
|
||||
use unindent::Unindent as _;
|
||||
use util::path;
|
||||
use util::{path, rel_path::rel_path};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -1467,7 +1468,7 @@ mod tests {
|
||||
|
||||
let editor = cx.update_window_entity(&diff, |diff, window, cx| {
|
||||
diff.move_to_path(
|
||||
PathKey::namespaced(TRACKED_NAMESPACE, "foo".into()),
|
||||
PathKey::namespaced(TRACKED_NAMESPACE, rel_path("foo").into_arc()),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -1488,7 +1489,7 @@ mod tests {
|
||||
|
||||
let editor = cx.update_window_entity(&diff, |diff, window, cx| {
|
||||
diff.move_to_path(
|
||||
PathKey::namespaced(TRACKED_NAMESPACE, "bar".into()),
|
||||
PathKey::namespaced(TRACKED_NAMESPACE, rel_path("bar").into_arc()),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ use text::{
|
||||
subscription::{Subscription, Topic},
|
||||
};
|
||||
use theme::SyntaxTheme;
|
||||
use util::post_inc;
|
||||
use util::{post_inc, rel_path::RelPath};
|
||||
|
||||
const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
|
||||
|
||||
@@ -161,24 +161,33 @@ impl MultiBufferDiffHunk {
|
||||
|
||||
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
|
||||
pub struct PathKey {
|
||||
namespace: u32,
|
||||
path: Arc<str>,
|
||||
namespace: Option<u64>,
|
||||
path: Arc<RelPath>,
|
||||
}
|
||||
|
||||
impl PathKey {
|
||||
pub fn namespaced(namespace: u32, path: Arc<str>) -> Self {
|
||||
Self { namespace, path }
|
||||
pub fn namespaced(namespace: u64, path: Arc<RelPath>) -> Self {
|
||||
Self {
|
||||
namespace: Some(namespace),
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
|
||||
if let Some(file) = buffer.read(cx).file() {
|
||||
Self::namespaced(1, file.full_path(cx).to_string_lossy().into_owned().into())
|
||||
Self::namespaced(file.worktree_id(cx).to_proto(), file.path().clone())
|
||||
} else {
|
||||
Self::namespaced(0, buffer.entity_id().to_string().into())
|
||||
Self {
|
||||
namespace: None,
|
||||
path: RelPath::unix(&buffer.entity_id().to_string())
|
||||
.unwrap()
|
||||
.into_arc(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Arc<str> {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn path(&self) -> &Arc<RelPath> {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use rand::prelude::*;
|
||||
use settings::SettingsStore;
|
||||
use std::env;
|
||||
use util::RandomCharIter;
|
||||
use util::rel_path::rel_path;
|
||||
use util::test::sample_text;
|
||||
|
||||
#[ctor::ctor]
|
||||
@@ -1524,7 +1525,7 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let path1: PathKey = PathKey::namespaced(0, "/".into());
|
||||
let path1: PathKey = PathKey::namespaced(0, rel_path("root").into_arc());
|
||||
|
||||
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
@@ -1619,7 +1620,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) {
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let path1: PathKey = PathKey::namespaced(0, "/".into());
|
||||
let path1: PathKey = PathKey::namespaced(0, rel_path("root").into_arc());
|
||||
let buf2 = cx.new(|cx| {
|
||||
Buffer::local(
|
||||
indoc! {
|
||||
@@ -1638,7 +1639,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) {
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let path2 = PathKey::namespaced(1, "/".into());
|
||||
let path2 = PathKey::namespaced(1, rel_path("root").into_arc());
|
||||
|
||||
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
@@ -1815,7 +1816,7 @@ fn test_set_excerpts_for_buffer_rename(cx: &mut TestAppContext) {
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let path: PathKey = PathKey::namespaced(0, "/".into());
|
||||
let path: PathKey = PathKey::namespaced(0, rel_path("root").into_arc());
|
||||
let buf2 = cx.new(|cx| {
|
||||
Buffer::local(
|
||||
indoc! {
|
||||
|
||||
Reference in New Issue
Block a user