project_panel: Fix the confusing display when files or directories have the same name in a case-insensitive comparison (#19211)

wannacu created

- Closes #18851

Release Notes:

- Fixed directory name comparison on Linux not considering the case
([#18851](https://github.com/zed-industries/zed/issues/18851))

Change summary

crates/util/src/paths.rs | 32 ++++++++++++++++++++++++++++++++
crates/util/src/util.rs  | 15 +++++++++++----
2 files changed, 43 insertions(+), 4 deletions(-)

Detailed changes

crates/util/src/paths.rs 🔗

@@ -433,6 +433,38 @@ mod tests {
         );
     }
 
+    #[test]
+    fn compare_paths_case_semi_sensitive() {
+        let mut paths = vec![
+            (Path::new("test_DIRS"), false),
+            (Path::new("test_DIRS/foo_1"), true),
+            (Path::new("test_DIRS/foo_2"), true),
+            (Path::new("test_DIRS/bar"), true),
+            (Path::new("test_DIRS/BAR"), true),
+            (Path::new("test_dirs"), false),
+            (Path::new("test_dirs/foo_1"), true),
+            (Path::new("test_dirs/foo_2"), true),
+            (Path::new("test_dirs/bar"), true),
+            (Path::new("test_dirs/BAR"), true),
+        ];
+        paths.sort_by(|&a, &b| compare_paths(a, b));
+        assert_eq!(
+            paths,
+            vec![
+                (Path::new("test_dirs"), false),
+                (Path::new("test_dirs/bar"), true),
+                (Path::new("test_dirs/BAR"), true),
+                (Path::new("test_dirs/foo_1"), true),
+                (Path::new("test_dirs/foo_2"), true),
+                (Path::new("test_DIRS"), false),
+                (Path::new("test_DIRS/bar"), true),
+                (Path::new("test_DIRS/BAR"), true),
+                (Path::new("test_DIRS/foo_1"), true),
+                (Path::new("test_DIRS/foo_2"), true),
+            ]
+        );
+    }
+
     #[test]
     fn path_with_position_parse_posix_path() {
         // Test POSIX filename edge cases

crates/util/src/util.rs 🔗

@@ -659,15 +659,22 @@ impl<'a> NumericPrefixWithSuffix<'a> {
         Self(prefix, remainder)
     }
 }
+
+/// When dealing with equality, we need to consider the case of the strings to achieve strict equality
+/// to handle cases like "a" < "A" instead of "a" == "A".
 impl Ord for NumericPrefixWithSuffix<'_> {
     fn cmp(&self, other: &Self) -> Ordering {
         match (self.0, other.0) {
-            (None, None) => UniCase::new(self.1).cmp(&UniCase::new(other.1)),
+            (None, None) => UniCase::new(self.1)
+                .cmp(&UniCase::new(other.1))
+                .then_with(|| self.1.cmp(other.1).reverse()),
             (None, Some(_)) => Ordering::Greater,
             (Some(_), None) => Ordering::Less,
-            (Some(a), Some(b)) => a
-                .cmp(&b)
-                .then_with(|| UniCase::new(self.1).cmp(&UniCase::new(other.1))),
+            (Some(a), Some(b)) => a.cmp(&b).then_with(|| {
+                UniCase::new(self.1)
+                    .cmp(&UniCase::new(other.1))
+                    .then_with(|| self.1.cmp(other.1).reverse())
+            }),
         }
     }
 }