Respect workspace order in recent projects (#12844)

Elliot Thomas created

<img width="1266" alt="Screenshot 2024-06-10 at 14 33 32"
src="https://github.com/zed-industries/zed/assets/1347854/c75de033-f2c8-4500-8b34-46b5f0260d3d">

This changes the recent projects panel to use the order of paths from
the workspace rather than always being alphanumerical.

This follows the work to introduce manual workspace ordering to ensure
the recent projects paths reflect the order of paths in the main project
panel.

Release Notes:

- Improve the recent project panel by ordering paths using the workspace
order

Change summary

crates/recent_projects/src/recent_projects.rs | 15 +++++++++++----
crates/workspace/src/persistence.rs           |  8 ++++----
crates/workspace/src/persistence/model.rs     |  4 ++--
3 files changed, 17 insertions(+), 10 deletions(-)

Detailed changes

crates/recent_projects/src/recent_projects.rs 🔗

@@ -224,9 +224,10 @@ impl PickerDelegate for RecentProjectsDelegate {
             .filter(|(_, (id, _))| !self.is_current_workspace(*id, cx))
             .map(|(id, (_, location))| {
                 let combined_string = match location {
-                    SerializedWorkspaceLocation::Local(paths, _) => paths
-                        .paths()
+                    SerializedWorkspaceLocation::Local(paths, order) => order
+                        .order()
                         .iter()
+                        .filter_map(|i| paths.paths().get(*i))
                         .map(|path| path.compact().to_string_lossy().into_owned())
                         .collect::<Vec<_>>()
                         .join(""),
@@ -285,7 +286,7 @@ impl PickerDelegate for RecentProjectsDelegate {
                     } else {
                         match candidate_workspace_location {
                             SerializedWorkspaceLocation::Local(paths, _) => {
-                                let paths = paths.paths().as_ref().clone();
+                                let paths = paths.paths().to_vec();
                                 if replace_current_window {
                                     cx.spawn(move |workspace, mut cx| async move {
                                         let continue_replacing = workspace
@@ -389,7 +390,13 @@ impl PickerDelegate for RecentProjectsDelegate {
 
         let mut path_start_offset = 0;
         let paths = match location {
-            SerializedWorkspaceLocation::Local(paths, _) => paths.paths(),
+            SerializedWorkspaceLocation::Local(paths, order) => Arc::new(
+                order
+                    .order()
+                    .iter()
+                    .filter_map(|i| paths.paths().get(*i).cloned())
+                    .collect(),
+            ),
             SerializedWorkspaceLocation::DevServer(dev_server_project) => {
                 Arc::new(vec![PathBuf::from(format!(
                     "{}:{}",

crates/workspace/src/persistence.rs 🔗

@@ -667,8 +667,8 @@ impl WorkspaceDb {
     }
 
     query! {
-        fn recent_workspaces() -> Result<Vec<(WorkspaceId, LocalPaths, Option<u64>)>> {
-            SELECT workspace_id, local_paths, dev_server_project_id
+        fn recent_workspaces() -> Result<Vec<(WorkspaceId, LocalPaths, LocalPathsOrder, Option<u64>)>> {
+            SELECT workspace_id, local_paths, local_paths_order, dev_server_project_id
             FROM workspaces
             WHERE local_paths IS NOT NULL OR dev_server_project_id IS NOT NULL
             ORDER BY timestamp DESC
@@ -732,7 +732,7 @@ impl WorkspaceDb {
         let mut delete_tasks = Vec::new();
         let dev_server_projects = self.dev_server_projects()?;
 
-        for (id, location, dev_server_project_id) in self.recent_workspaces()? {
+        for (id, location, order, dev_server_project_id) in self.recent_workspaces()? {
             if let Some(dev_server_project_id) = dev_server_project_id.map(DevServerProjectId) {
                 if let Some(dev_server_project) = dev_server_projects
                     .iter()
@@ -748,7 +748,7 @@ impl WorkspaceDb {
             if location.paths().iter().all(|path| path.exists())
                 && location.paths().iter().any(|path| path.is_dir())
             {
-                result.push((id, location.into()));
+                result.push((id, SerializedWorkspaceLocation::Local(location, order)));
             } else {
                 delete_tasks.push(self.delete_workspace_by_id(id));
             }

crates/workspace/src/persistence/model.rs 🔗

@@ -39,8 +39,8 @@ impl LocalPaths {
         Self(Arc::new(paths))
     }
 
-    pub fn paths(&self) -> Arc<Vec<PathBuf>> {
-        self.0.clone()
+    pub fn paths(&self) -> &Arc<Vec<PathBuf>> {
+        &self.0
     }
 }