Make PathList equality ignore display order (#52052)

Eric Holk created

`PathList` stores both sorted paths and the original insertion order
(for display in the project panel). Previously, `PartialEq`, `Eq`, and
`Hash` were derived, which meant two `PathList` values with the same
paths but different display orderings were considered unequal.

This change replaces the derived impls with manual ones that only
compare
the sorted `paths` field, matching the semantic intent: a `PathList`
identifies a set of directories, and the display order is not part of
that identity.

Release Notes:

- N/A

Change summary

crates/util/src/path_list.rs | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)

Detailed changes

crates/util/src/path_list.rs 🔗

@@ -1,4 +1,5 @@
 use std::{
+    hash::{Hash, Hasher},
     path::{Path, PathBuf},
     sync::Arc,
 };
@@ -7,13 +8,13 @@ use crate::paths::SanitizedPath;
 use itertools::Itertools;
 use serde::{Deserialize, Serialize};
 
-/// A list of absolute paths, in a specific order.
+/// A list of absolute paths, with an associated display order.
 ///
-/// The paths are stored in lexicographic order, so that they can be compared to
-/// other path lists without regard to the order of the paths.
+/// Two `PathList` values are considered equal if they contain the same paths,
+/// regardless of the order in which those paths were originally provided.
 ///
 /// The paths can be retrieved in the original order using `ordered_paths()`.
-#[derive(Default, PartialEq, Eq, Hash, Debug, Clone)]
+#[derive(Default, Debug, Clone)]
 pub struct PathList {
     /// The paths, in lexicographic order.
     paths: Arc<[PathBuf]>,
@@ -23,6 +24,20 @@ pub struct PathList {
     order: Arc<[usize]>,
 }
 
+impl PartialEq for PathList {
+    fn eq(&self, other: &Self) -> bool {
+        self.paths == other.paths
+    }
+}
+
+impl Eq for PathList {}
+
+impl Hash for PathList {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.paths.hash(state);
+    }
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 pub struct SerializedPathList {
     pub paths: String,
@@ -132,6 +147,12 @@ mod tests {
         assert_eq!(list1.order(), &[1, 0], "list1 order incorrect");
         assert_eq!(list2.order(), &[0, 1], "list2 order incorrect");
 
+        // Same paths in different order are equal (order is display-only).
+        assert_eq!(
+            list1, list2,
+            "same paths with different order should be equal"
+        );
+
         let list1_deserialized = PathList::deserialize(&list1.serialize());
         assert_eq!(list1_deserialized, list1, "list1 deserialization failed");