From 27ab898e6533094307be16ec86a8f9ba738d02ac Mon Sep 17 00:00:00 2001 From: Luis Date: Mon, 16 Feb 2026 08:38:13 -0500 Subject: [PATCH] project_panel: Fix mixed sort with incorrect ordering when same file and dir name (#47863) Closes #47678 When using mixed sort mode in the project panel, a folder and file with the same name but different case (e.g., `hello` folder and `Hello.txt` file) would sort incorrectly. The file could appear between an expanded folder and its contents. The issue was in `compare_rel_paths_mixed`: the tie-breaker logic used case-sensitive comparison (`a == b`) to decide directory-before-file ordering, but `natural_sort_no_tiebreak` already considers entries equal case-insensitively. Changed to use `eq_ignore_ascii_case` to match. Release Notes: - Fixed project panel mixed sort mode ordering incorrectly when a file and folder share the same name with different casing. --- crates/util/src/paths.rs | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index f010ff57c636f40de50a06baefd99c9ee43728f9..2e014413eff563eda7d80e1e8762a8da97eb4e7a 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -1174,8 +1174,8 @@ pub fn compare_rel_paths_mixed( let ordering = match (a_key, b_key) { (Some(a), Some(b)) => natural_sort_no_tiebreak(a, b) .then_with(|| match (a_leaf_file, b_leaf_file) { - (true, false) if a == b => Ordering::Greater, - (false, true) if a == b => Ordering::Less, + (true, false) if a.eq_ignore_ascii_case(b) => Ordering::Greater, + (false, true) if a.eq_ignore_ascii_case(b) => Ordering::Less, _ => Ordering::Equal, }) .then_with(|| { @@ -1816,6 +1816,35 @@ mod tests { ); } + #[perf] + fn compare_rel_paths_mixed_same_name_different_case_file_and_dir() { + let mut paths = vec![ + (RelPath::unix("Hello.txt").unwrap(), true), + (RelPath::unix("hello").unwrap(), false), + ]; + paths.sort_by(|&a, &b| compare_rel_paths_mixed(a, b)); + assert_eq!( + paths, + vec![ + (RelPath::unix("hello").unwrap(), false), + (RelPath::unix("Hello.txt").unwrap(), true), + ] + ); + + let mut paths = vec![ + (RelPath::unix("hello").unwrap(), false), + (RelPath::unix("Hello.txt").unwrap(), true), + ]; + paths.sort_by(|&a, &b| compare_rel_paths_mixed(a, b)); + assert_eq!( + paths, + vec![ + (RelPath::unix("hello").unwrap(), false), + (RelPath::unix("Hello.txt").unwrap(), true), + ] + ); + } + #[perf] fn compare_rel_paths_mixed_with_nested_paths() { // Test that nested paths still work correctly @@ -1951,8 +1980,8 @@ mod tests { assert_eq!( paths, vec![ - (RelPath::unix("A/B.txt").unwrap(), true), (RelPath::unix("a/b/c.txt").unwrap(), true), + (RelPath::unix("A/B.txt").unwrap(), true), (RelPath::unix("a.txt").unwrap(), true), (RelPath::unix("A.txt").unwrap(), true), ]