Clear stale projects if they no longer exist

Kay Simmons created

Change summary

crates/db/src/query.rs                        |  4 +-
crates/recent_projects/src/recent_projects.rs | 42 +++++++++++++--------
crates/workspace/src/persistence.rs           | 27 ++++++++++++-
3 files changed, 53 insertions(+), 20 deletions(-)

Detailed changes

crates/db/src/query.rs 🔗

@@ -80,7 +80,7 @@ macro_rules! query {
 
             let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
 
-            self.select::<$return_type>(sql_stmt)?(())
+            self.select::<$return_type>(sql_stmt)?()
                 .context(::std::format!(
                     "Error in {}, select_row failed to execute or parse for: {}",
                     ::std::stringify!($id),
@@ -95,7 +95,7 @@ macro_rules! query {
             self.write(|connection| {
                 let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
 
-                connection.select::<$return_type>(sql_stmt)?(())
+                connection.select::<$return_type>(sql_stmt)?()
                     .context(::std::format!(
                         "Error in {}, select_row failed to execute or parse for: {}",
                         ::std::stringify!($id),

crates/recent_projects/src/recent_projects.rs 🔗

@@ -11,9 +11,7 @@ use highlighted_workspace_location::HighlightedWorkspaceLocation;
 use ordered_float::OrderedFloat;
 use picker::{Picker, PickerDelegate};
 use settings::Settings;
-use workspace::{OpenPaths, Workspace, WorkspaceLocation};
-
-const RECENT_LIMIT: usize = 100;
+use workspace::{OpenPaths, Workspace, WorkspaceLocation, WORKSPACE_DB};
 
 actions!(recent_projects, [Toggle]);
 
@@ -30,14 +28,8 @@ struct RecentProjectsView {
 }
 
 impl RecentProjectsView {
-    fn new(cx: &mut ViewContext<Self>) -> Self {
+    fn new(workspace_locations: Vec<WorkspaceLocation>, cx: &mut ViewContext<Self>) -> Self {
         let handle = cx.weak_handle();
-        let workspace_locations: Vec<WorkspaceLocation> = workspace::WORKSPACE_DB
-            .recent_workspaces(RECENT_LIMIT)
-            .unwrap_or_default()
-            .into_iter()
-            .map(|(_, location)| location)
-            .collect();
         Self {
             picker: cx.add_view(|cx| {
                 Picker::new("Recent Projects...", handle, cx).with_max_size(800., 1200.)
@@ -48,12 +40,30 @@ impl RecentProjectsView {
         }
     }
 
-    fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
-        workspace.toggle_modal(cx, |_, cx| {
-            let view = cx.add_view(|cx| Self::new(cx));
-            cx.subscribe(&view, Self::on_event).detach();
-            view
-        });
+    fn toggle(_: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
+        cx.spawn(|workspace, mut cx| async move {
+            let workspace_locations = cx
+                .background()
+                .spawn(async {
+                    WORKSPACE_DB
+                        .recent_workspaces_on_disk()
+                        .await
+                        .unwrap_or_default()
+                        .into_iter()
+                        .map(|(_, location)| location)
+                        .collect()
+                })
+                .await;
+
+            workspace.update(&mut cx, |workspace, cx| {
+                workspace.toggle_modal(cx, |_, cx| {
+                    let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
+                    cx.subscribe(&view, Self::on_event).detach();
+                    view
+                });
+            })
+        })
+        .detach();
     }
 
     fn on_event(

crates/workspace/src/persistence.rs 🔗

@@ -196,14 +196,37 @@ impl WorkspaceDb {
     }
 
     query! {
-        pub fn recent_workspaces(limit: usize) -> Result<Vec<(WorkspaceId, WorkspaceLocation)>> {
+        fn recent_workspaces() -> Result<Vec<(WorkspaceId, WorkspaceLocation)>> {
             SELECT workspace_id, workspace_location 
             FROM workspaces
             WHERE workspace_location IS NOT NULL
             ORDER BY timestamp DESC 
-            LIMIT ?
         }
     }
+    
+    query! {
+        async fn delete_stale_workspace(id: WorkspaceId) -> Result<()> {
+            DELETE FROM workspaces
+            WHERE workspace_id IS ?
+        }
+    }
+    
+    // Returns the recent locations which are still valid on disk and deletes ones which no longer
+    // exist.
+    pub async fn recent_workspaces_on_disk(&self) -> Result<Vec<(WorkspaceId, WorkspaceLocation)>> {
+        let mut result = Vec::new();
+        let mut delete_tasks = Vec::new();
+        for (id, location) in self.recent_workspaces()? {
+            if location.paths().iter().all(|path| dbg!(path).exists()) {
+                result.push((id, location));
+            } else {
+                delete_tasks.push(self.delete_stale_workspace(id));
+            }
+        }
+        
+        futures::future::join_all(delete_tasks).await;
+        Ok(result)
+    }
 
     fn get_center_pane_group(&self, workspace_id: WorkspaceId) -> Result<SerializedPaneGroup> {
         self.get_pane_group(workspace_id, None)?