added another handler for sorting

Piotr Osiewicz created

Change summary

crates/project/src/project.rs             |  7 --
crates/project_panel/src/project_panel.rs | 19 ++++---
crates/util/src/paths.rs                  | 61 +++++++++++++++++++++++++
3 files changed, 74 insertions(+), 13 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -121,7 +121,7 @@ use text::{Anchor, BufferId, OffsetRangeExt, Point, Rope};
 use toolchain_store::EmptyToolchainStore;
 use util::{
     ResultExt as _, maybe,
-    paths::{PathStyle, SanitizedPath, compare_paths, is_absolute},
+    paths::{PathStyle, SanitizedPath, compare_paths, compare_rel_paths, is_absolute},
     rel_path::RelPath,
 };
 use worktree::{CreatedEntry, Snapshot, Traversal};
@@ -4041,10 +4041,7 @@ impl Project {
             (None, None) => a.read(cx).remote_id().cmp(&b.read(cx).remote_id()),
             (None, Some(_)) => std::cmp::Ordering::Less,
             (Some(_), None) => std::cmp::Ordering::Greater,
-            (Some(a), Some(b)) => compare_paths(
-                (a.path().as_std_path(), true),
-                (b.path().as_std_path(), true),
-            ),
+            (Some(a), Some(b)) => compare_rel_paths((&a.path(), true), (&b.path(), true)),
         });
         for buffer in buffers {
             tx.send_blocking(buffer.clone()).unwrap()

crates/project_panel/src/project_panel.rs 🔗

@@ -59,7 +59,11 @@ use ui::{
     ScrollAxes, ScrollableHandle, Scrollbars, StickyCandidate, Tooltip, WithScrollbar, prelude::*,
     v_flex,
 };
-use util::{ResultExt, TakeUntilExt, TryFutureExt, maybe, paths::compare_paths, rel_path::RelPath};
+use util::{
+    ResultExt, TakeUntilExt, TryFutureExt, maybe,
+    paths::{compare_paths, compare_rel_paths},
+    rel_path::RelPath,
+};
 use workspace::{
     DraggedSelection, OpenInTerminal, OpenOptions, OpenVisible, PreviewTabsSettings, SelectedEntry,
     SplitDirection, Workspace,
@@ -1998,10 +2002,9 @@ impl ProjectPanel {
                     worktree.entry_for_id(a.entry_id),
                     worktree.entry_for_id(b.entry_id),
                 ) {
-                    (Some(a), Some(b)) => compare_paths(
-                        (a.path.as_std_path(), a.is_file()),
-                        (b.path.as_std_path(), b.is_file()),
-                    ),
+                    (Some(a), Some(b)) => {
+                        compare_rel_paths((&a.path, a.is_file()), (&b.path, b.is_file()))
+                    }
                     _ => cmp::Ordering::Equal,
                 }
             })
@@ -5889,9 +5892,9 @@ impl ClipboardEntry {
 fn cmp<T: AsRef<Entry>>(lhs: T, rhs: T) -> cmp::Ordering {
     let entry_a = lhs.as_ref();
     let entry_b = rhs.as_ref();
-    compare_paths(
-        (entry_a.path.as_std_path(), entry_a.is_file()),
-        (entry_b.path.as_std_path(), entry_b.is_file()),
+    compare_rel_paths(
+        (&entry_a.path, entry_a.is_file()),
+        (&entry_b.path, entry_b.is_file()),
     )
 }
 

crates/util/src/paths.rs 🔗

@@ -14,6 +14,8 @@ use std::{
     sync::LazyLock,
 };
 
+use crate::rel_path::RelPath;
+
 static HOME_DIR: OnceLock<PathBuf> = OnceLock::new();
 
 /// Returns the path to the user's home directory.
@@ -850,6 +852,65 @@ pub fn compare_paths(
     }
 }
 
+pub fn compare_rel_paths(
+    (path_a, a_is_file): (&RelPath, bool),
+    (path_b, b_is_file): (&RelPath, bool),
+) -> Ordering {
+    let mut components_a = path_a.components().peekable();
+    let mut components_b = path_b.components().peekable();
+
+    loop {
+        match (components_a.next(), components_b.next()) {
+            (Some(component_a), Some(component_b)) => {
+                let a_is_file = components_a.peek().is_none() && a_is_file;
+                let b_is_file = components_b.peek().is_none() && b_is_file;
+
+                let ordering = a_is_file.cmp(&b_is_file).then_with(|| {
+                    let path_a = Path::new(component_a);
+                    let path_string_a = if a_is_file {
+                        path_a.file_stem()
+                    } else {
+                        path_a.file_name()
+                    }
+                    .map(|s| s.to_string_lossy());
+
+                    let path_b = Path::new(component_b);
+                    let path_string_b = if b_is_file {
+                        path_b.file_stem()
+                    } else {
+                        path_b.file_name()
+                    }
+                    .map(|s| s.to_string_lossy());
+
+                    let compare_components = match (path_string_a, path_string_b) {
+                        (Some(a), Some(b)) => natural_sort(&a, &b),
+                        (Some(_), None) => Ordering::Greater,
+                        (None, Some(_)) => Ordering::Less,
+                        (None, None) => Ordering::Equal,
+                    };
+
+                    compare_components.then_with(|| {
+                        if a_is_file && b_is_file {
+                            let ext_a = path_a.extension().unwrap_or_default();
+                            let ext_b = path_b.extension().unwrap_or_default();
+                            ext_a.cmp(ext_b)
+                        } else {
+                            Ordering::Equal
+                        }
+                    })
+                });
+
+                if !ordering.is_eq() {
+                    return ordering;
+                }
+            }
+            (Some(_), None) => break Ordering::Greater,
+            (None, Some(_)) => break Ordering::Less,
+            (None, None) => break Ordering::Equal,
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;