diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index b97966692f6377d9761b9b689df1471031e0a011..104075f474840c2d02971cb516503db62e1dfe4b 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -311,8 +311,7 @@ impl PickerDelegate for RecentProjectsDelegate { .filter(|(_, (id, _, _))| !self.is_current_workspace(*id, cx)) .map(|(id, (_, _, paths))| { let combined_string = paths - .paths() - .iter() + .ordered_paths() .map(|path| path.compact().to_string_lossy().into_owned()) .collect::>() .join(""); @@ -462,8 +461,7 @@ impl PickerDelegate for RecentProjectsDelegate { let mut path_start_offset = 0; let (match_labels, paths): (Vec<_>, Vec<_>) = paths - .paths() - .iter() + .ordered_paths() .map(|p| p.compact()) .map(|path| { let highlighted_text = diff --git a/crates/workspace/src/history_manager.rs b/crates/workspace/src/history_manager.rs index f68b58ff8289c68883393e7be5087322ca76d480..1b80e7c0125aac0ccf4a0f41c1b5be3c4a651e72 100644 --- a/crates/workspace/src/history_manager.rs +++ b/crates/workspace/src/history_manager.rs @@ -128,8 +128,7 @@ impl HistoryManager { impl HistoryManagerEntry { pub fn new(id: WorkspaceId, paths: &PathList) -> Self { let path = paths - .paths() - .iter() + .ordered_paths() .map(|path| path.compact()) .collect::>(); Self { id, path } diff --git a/crates/workspace/src/path_list.rs b/crates/workspace/src/path_list.rs index 01e2ffda949faf502de087fb0077cdbc758001ab..035f9e44fcce46527faa0c1053b7a6bb09aae0c8 100644 --- a/crates/workspace/src/path_list.rs +++ b/crates/workspace/src/path_list.rs @@ -3,15 +3,22 @@ use std::{ sync::Arc, }; +use itertools::Itertools; use util::paths::SanitizedPath; /// A list of absolute paths, in a specific 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. +/// +/// The paths can be retrieved in the original order using `ordered_paths()`. #[derive(Default, PartialEq, Eq, Debug, Clone)] pub struct PathList { + /// The paths, in lexicographic order. paths: Arc<[PathBuf]>, + /// The order in which the paths were provided. + /// + /// See `ordered_paths()` for a way to get the paths in the original order. order: Arc<[usize]>, } @@ -42,14 +49,25 @@ impl PathList { self.paths.is_empty() } + /// Get the paths in lexicographic order. pub fn paths(&self) -> &[PathBuf] { self.paths.as_ref() } + /// Get the order in which the paths were provided. pub fn order(&self) -> &[usize] { self.order.as_ref() } + /// Get the paths in the original order. + pub fn ordered_paths(&self) -> impl Iterator { + self.order + .iter() + .zip(self.paths.iter()) + .sorted_by_key(|(i, _)| **i) + .map(|(_, path)| path) + } + pub fn is_lexicographically_ordered(&self) -> bool { self.order.iter().enumerate().all(|(i, &j)| i == j) } @@ -109,15 +127,70 @@ mod tests { let list1 = PathList::new(&["a/d", "a/c"]); let list2 = PathList::new(&["a/c", "a/d"]); - assert_eq!(list1.paths(), list2.paths()); - assert_ne!(list1, list2); - assert_eq!(list1.order(), &[1, 0]); - assert_eq!(list2.order(), &[0, 1]); + assert_eq!(list1.paths(), list2.paths(), "paths differ"); + assert_eq!(list1.order(), &[1, 0], "list1 order incorrect"); + assert_eq!(list2.order(), &[0, 1], "list2 order incorrect"); let list1_deserialized = PathList::deserialize(&list1.serialize()); - assert_eq!(list1_deserialized, list1); + assert_eq!(list1_deserialized, list1, "list1 deserialization failed"); let list2_deserialized = PathList::deserialize(&list2.serialize()); - assert_eq!(list2_deserialized, list2); + assert_eq!(list2_deserialized, list2, "list2 deserialization failed"); + + assert_eq!( + list1.ordered_paths().collect_array().unwrap(), + [&PathBuf::from("a/d"), &PathBuf::from("a/c")], + "list1 ordered paths incorrect" + ); + assert_eq!( + list2.ordered_paths().collect_array().unwrap(), + [&PathBuf::from("a/c"), &PathBuf::from("a/d")], + "list2 ordered paths incorrect" + ); + } + + #[test] + fn test_path_list_ordering() { + let list = PathList::new(&["b", "a", "c"]); + assert_eq!( + list.paths(), + &[PathBuf::from("a"), PathBuf::from("b"), PathBuf::from("c")] + ); + assert_eq!(list.order(), &[1, 0, 2]); + assert!(!list.is_lexicographically_ordered()); + + let serialized = list.serialize(); + let deserialized = PathList::deserialize(&serialized); + assert_eq!(deserialized, list); + + assert_eq!( + deserialized.ordered_paths().collect_array().unwrap(), + [ + &PathBuf::from("b"), + &PathBuf::from("a"), + &PathBuf::from("c") + ] + ); + + let list = PathList::new(&["b", "c", "a"]); + assert_eq!( + list.paths(), + &[PathBuf::from("a"), PathBuf::from("b"), PathBuf::from("c")] + ); + assert_eq!(list.order(), &[2, 0, 1]); + assert!(!list.is_lexicographically_ordered()); + + let serialized = list.serialize(); + let deserialized = PathList::deserialize(&serialized); + assert_eq!(deserialized, list); + + assert_eq!( + deserialized.ordered_paths().collect_array().unwrap(), + [ + &PathBuf::from("b"), + &PathBuf::from("c"), + &PathBuf::from("a"), + ] + ); } } diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index efd5116dc39d3ca615112c503ea135b7da076264..48c094da0eb43231b81201773726d28cd58996c1 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -2493,7 +2493,7 @@ mod tests { let workspace_6 = SerializedWorkspace { id: WorkspaceId(6), - paths: PathList::new(&["/tmp6a", "/tmp6b", "/tmp6c"]), + paths: PathList::new(&["/tmp6c", "/tmp6b", "/tmp6a"]), location: SerializedWorkspaceLocation::Local, center_group: Default::default(), window_bounds: Default::default(), @@ -2534,7 +2534,7 @@ mod tests { assert_eq!(locations.len(), 1); assert_eq!( locations[0].0, - PathList::new(&["/tmp6a", "/tmp6b", "/tmp6c"]), + PathList::new(&["/tmp6c", "/tmp6b", "/tmp6a"]), ); assert_eq!(locations[0].1, Some(60)); } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 0be758efb7a4622361b5dfe4785e44eb16fe1d4f..81247b89593074e8f44565d9a8daabaf6b8307f5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1539,7 +1539,7 @@ impl Workspace { persistence::DB.workspace_for_roots(paths_to_open.as_slice()); if let Some(paths) = serialized_workspace.as_ref().map(|ws| &ws.paths) { - paths_to_open = paths.paths().to_vec(); + paths_to_open = paths.ordered_paths().cloned().collect(); if !paths.is_lexicographically_ordered() { project_handle .update(cx, |project, cx| {