Fetch last workspace explicitly when starting Zed

Mikayla Maki and Max created

co-authored-by: Max <max@zed.dev>

Change summary

crates/drag_and_drop/src/drag_and_drop.rs |  4 -
crates/workspace/src/persistence.rs       | 40 +++++++++++++-----------
crates/workspace/src/workspace.rs         | 28 ++++++++++------
crates/zed/src/main.rs                    | 14 +++++++-
crates/zed/src/zed.rs                     |  2 
5 files changed, 53 insertions(+), 35 deletions(-)

Detailed changes

crates/drag_and_drop/src/drag_and_drop.rs 🔗

@@ -139,9 +139,7 @@ impl<V: View> DragAndDrop<V> {
                     region_offset,
                     region,
                 }) => {
-                    if (dbg!(event.position) - (dbg!(region.origin() + region_offset))).length()
-                        > DEAD_ZONE
-                    {
+                    if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE {
                         this.currently_dragged = Some(State::Dragging {
                             window_id,
                             region_offset,

crates/workspace/src/persistence.rs 🔗

@@ -8,7 +8,7 @@ use anyhow::{anyhow, bail, Context, Result};
 use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
 use gpui::Axis;
 
-use util::{iife, unzip_option, ResultExt};
+use util::{ unzip_option, ResultExt};
 
 use crate::dock::DockPosition;
 use crate::WorkspaceId;
@@ -96,22 +96,16 @@ impl WorkspaceDb {
             WorkspaceLocation,
             bool,
             DockPosition,
-        ) = iife!({
-            if worktree_roots.len() == 0 {
-                self.select_row(sql!(
-                    SELECT workspace_id, workspace_location, left_sidebar_open, dock_visible, dock_anchor
-                    FROM workspaces
-                    ORDER BY timestamp DESC LIMIT 1))?()?
-            } else {
-                self.select_row_bound(sql!(
-                    SELECT workspace_id, workspace_location, left_sidebar_open, dock_visible, dock_anchor
-                    FROM workspaces 
-                    WHERE workspace_location = ?))?(&workspace_location)?
-            }
+        ) = 
+            self.select_row_bound(sql!{
+                SELECT workspace_id, workspace_location, left_sidebar_open, dock_visible, dock_anchor
+                FROM workspaces 
+                WHERE workspace_location = ?
+            })
+            .and_then(|mut prepared_statement| (prepared_statement)(&workspace_location))
             .context("No workspaces found")
-        })
-        .warn_on_err()
-        .flatten()?;
+            .warn_on_err()
+            .flatten()?;
 
         Some(SerializedWorkspace {
             id: workspace_id,
@@ -205,6 +199,16 @@ impl WorkspaceDb {
         }
     }
 
+    query! {
+        pub fn last_workspace() -> Result<Option<WorkspaceLocation>> {
+            SELECT workspace_location
+            FROM workspaces
+            WHERE workspace_location IS NOT NULL
+            ORDER BY timestamp DESC
+            LIMIT 1
+        }
+    }
+
     fn get_center_pane_group(&self, workspace_id: WorkspaceId) -> Result<SerializedPaneGroup> {
         self.get_pane_group(workspace_id, None)?
             .into_iter()
@@ -371,9 +375,9 @@ impl WorkspaceDb {
 
         Ok(())
     }
-    
+
     query!{
-        fn update_timestamp(workspace_id: WorkspaceId) -> Result<()> {
+        pub async fn update_timestamp(workspace_id: WorkspaceId) -> Result<()> {
             UPDATE workspaces
             SET timestamp = CURRENT_TIMESTAMP
             WHERE workspace_id = ?

crates/workspace/src/workspace.rs 🔗

@@ -172,15 +172,16 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
         let app_state = Arc::downgrade(&app_state);
         move |_: &NewFile, cx: &mut MutableAppContext| {
             if let Some(app_state) = app_state.upgrade() {
-                open_new(&app_state, false, cx).detach();
+                open_new(&app_state, cx).detach();
             }
         }
     });
+
     cx.add_global_action({
         let app_state = Arc::downgrade(&app_state);
         move |_: &NewWindow, cx: &mut MutableAppContext| {
             if let Some(app_state) = app_state.upgrade() {
-                open_new(&app_state, true, cx).detach();
+                open_new(&app_state, cx).detach();
             }
         }
     });
@@ -652,7 +653,6 @@ impl Workspace {
     fn new_local(
         abs_paths: Vec<PathBuf>,
         app_state: Arc<AppState>,
-        blank: bool,
         cx: &mut MutableAppContext,
     ) -> Task<(
         ViewHandle<Workspace>,
@@ -667,9 +667,7 @@ impl Workspace {
         );
 
         cx.spawn(|mut cx| async move {
-            let serialized_workspace = (!blank)
-                .then(|| persistence::DB.workspace_for_roots(&abs_paths.as_slice()))
-                .flatten();
+            let serialized_workspace = persistence::DB.workspace_for_roots(&abs_paths.as_slice());
 
             let paths_to_open = serialized_workspace
                 .as_ref()
@@ -807,7 +805,7 @@ impl Workspace {
         if self.project.read(cx).is_local() {
             Task::Ready(Some(callback(self, cx)))
         } else {
-            let task = Self::new_local(Vec::new(), app_state.clone(), true, cx);
+            let task = Self::new_local(Vec::new(), app_state.clone(), cx);
             cx.spawn(|_vh, mut cx| async move {
                 let (workspace, _) = task.await;
                 workspace.update(&mut cx, callback)
@@ -2184,7 +2182,11 @@ impl Workspace {
     }
 
     pub fn on_window_activation_changed(&mut self, active: bool, cx: &mut ViewContext<Self>) {
-        if !active {
+        if active {
+            cx.background()
+                .spawn(persistence::DB.update_timestamp(self.database_id()))
+                .detach();
+        } else {
             for pane in &self.panes {
                 pane.update(cx, |pane, cx| {
                     if let Some(item) = pane.active_item() {
@@ -2621,6 +2623,10 @@ pub fn activate_workspace_for_project(
     None
 }
 
+pub fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
+    DB.last_workspace().log_err().flatten()
+}
+
 #[allow(clippy::type_complexity)]
 pub fn open_paths(
     abs_paths: &[PathBuf],
@@ -2655,7 +2661,7 @@ pub fn open_paths(
                     .contains(&false);
 
             cx.update(|cx| {
-                let task = Workspace::new_local(abs_paths, app_state.clone(), false, cx);
+                let task = Workspace::new_local(abs_paths, app_state.clone(), cx);
 
                 cx.spawn(|mut cx| async move {
                     let (workspace, items) = task.await;
@@ -2674,8 +2680,8 @@ pub fn open_paths(
     })
 }
 
-pub fn open_new(app_state: &Arc<AppState>, blank: bool, cx: &mut MutableAppContext) -> Task<()> {
-    let task = Workspace::new_local(Vec::new(), app_state.clone(), blank, cx);
+pub fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) -> Task<()> {
+    let task = Workspace::new_local(Vec::new(), app_state.clone(), cx);
     cx.spawn(|mut cx| async move {
         let (workspace, opened_paths) = task.await;
 

crates/zed/src/main.rs 🔗

@@ -169,7 +169,7 @@ fn main() {
             cx.platform().activate(true);
             let paths = collect_path_args();
             if paths.is_empty() {
-                cx.dispatch_global_action(NewFile);
+                restore_or_create_workspace(cx);
             } else {
                 cx.dispatch_global_action(OpenPaths { paths });
             }
@@ -178,7 +178,7 @@ fn main() {
                 cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
                     .detach();
             } else {
-                cx.dispatch_global_action(NewFile);
+                restore_or_create_workspace(cx);
             }
             cx.spawn(|cx| async move {
                 while let Some(connection) = cli_connections_rx.next().await {
@@ -202,6 +202,16 @@ fn main() {
     });
 }
 
+fn restore_or_create_workspace(cx: &mut gpui::MutableAppContext) {
+    if let Some(location) = workspace::last_opened_workspace_paths() {
+        cx.dispatch_global_action(OpenPaths {
+            paths: location.paths().as_ref().clone(),
+        })
+    } else {
+        cx.dispatch_global_action(NewFile);
+    }
+}
+
 fn init_paths() {
     std::fs::create_dir_all(&*util::paths::CONFIG_DIR).expect("could not create config path");
     std::fs::create_dir_all(&*util::paths::LANGUAGES_DIR).expect("could not create languages path");

crates/zed/src/zed.rs 🔗

@@ -765,7 +765,7 @@ mod tests {
     #[gpui::test]
     async fn test_new_empty_workspace(cx: &mut TestAppContext) {
         let app_state = init(cx);
-        cx.update(|cx| open_new(&app_state, true, cx)).await;
+        cx.update(|cx| open_new(&app_state, cx)).await;
 
         let window_id = *cx.window_ids().first().unwrap();
         let workspace = cx.root_view::<Workspace>(window_id).unwrap();