WIP - Reopen closed items when going back in nav history

Max Brunsfeld created

Change summary

Cargo.lock                        |  1 
crates/workspace/Cargo.toml       |  1 
crates/workspace/src/pane.rs      | 77 +++++++++++++++++++++-----------
crates/workspace/src/workspace.rs | 44 +++++++++++-------
4 files changed, 78 insertions(+), 45 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5677,6 +5677,7 @@ dependencies = [
  "project",
  "serde_json",
  "theme",
+ "util",
 ]
 
 [[package]]

crates/workspace/Cargo.toml 🔗

@@ -17,6 +17,7 @@ gpui = { path = "../gpui" }
 language = { path = "../language" }
 project = { path = "../project" }
 theme = { path = "../theme" }
+util = { path = "../util" }
 anyhow = "1.0.38"
 log = "0.4"
 parking_lot = "0.11.1"

crates/workspace/src/pane.rs 🔗

@@ -12,6 +12,7 @@ use gpui::{
 use postage::watch;
 use project::ProjectPath;
 use std::{any::Any, cell::RefCell, cmp, mem, rc::Rc};
+use util::TryFutureExt;
 
 action!(Split, SplitDirection);
 action!(ActivateItem, usize);
@@ -114,29 +115,51 @@ impl Pane {
         cx.emit(Event::Activate);
     }
 
-    pub fn go_back(&mut self, _: &GoBack, cx: &mut ViewContext<Self>) {
-        if self.navigation.0.borrow().backward_stack.is_empty() {
-            return;
-        }
-
-        if let Some(item_view) = self.active_item() {
-            self.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingBack;
-            item_view.deactivated(cx);
-            self.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal;
-        }
+    pub fn go_back(workspace: &mut Workspace, _: &GoBack, cx: &mut ViewContext<Workspace>) {
+        let project_path = workspace.active_pane().update(cx, |pane, cx| {
+            let mut navigation = pane.navigation.0.borrow_mut();
+            if let Some(entry) = navigation.backward_stack.pop() {
+                if let Some(index) = entry
+                    .item_view
+                    .upgrade(cx)
+                    .and_then(|v| pane.index_for_item_view(v.as_ref()))
+                {
+                    if let Some(item_view) = pane.active_item() {
+                        pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingBack;
+                        item_view.deactivated(cx);
+                        pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal;
+                    }
 
-        let mut navigation = self.navigation.0.borrow_mut();
-        if let Some(entry) = navigation.backward_stack.pop() {
-            if let Some(index) = entry
-                .item_view
-                .upgrade(cx)
-                .and_then(|v| self.index_for_item_view(v.as_ref()))
-            {
-                self.active_item_index = index;
-                drop(navigation);
-                self.focus_active_item(cx);
-                cx.notify();
+                    pane.active_item_index = index;
+                    drop(navigation);
+                    pane.focus_active_item(cx);
+                    cx.notify();
+                } else {
+                    return navigation.paths_by_item.get(&entry.item_view.id()).cloned();
+                }
             }
+
+            None
+        });
+
+        if let Some(project_path) = project_path {
+            let task = workspace.load_path(project_path, cx);
+            cx.spawn(|workspace, mut cx| {
+                async move {
+                    let item = task.await?;
+                    workspace.update(&mut cx, |workspace, cx| {
+                        let pane = workspace.active_pane().clone();
+                        pane.update(cx, |pane, cx| {
+                            pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::GoingBack;
+                            pane.open_item(item, workspace, cx);
+                            pane.navigation.0.borrow_mut().mode = NavigationHistoryMode::Normal;
+                        });
+                    });
+                    Ok(())
+                }
+                .log_err()
+            })
+            .detach();
         }
     }
 
@@ -268,17 +291,17 @@ impl Pane {
 
     pub fn close_item(&mut self, item_view_id: usize, cx: &mut ViewContext<Self>) {
         let mut item_ix = 0;
-        self.item_views.retain(|(item_id, item)| {
-            if item.id() == item_view_id {
+        self.item_views.retain(|(_, item_view)| {
+            if item_view.id() == item_view_id {
                 let mut navigation = self.navigation.0.borrow_mut();
-                if let Some(path) = item.project_path(cx) {
-                    navigation.paths_by_item.insert(*item_id, path);
+                if let Some(path) = item_view.project_path(cx) {
+                    navigation.paths_by_item.insert(item_view.id(), path);
                 } else {
-                    navigation.paths_by_item.remove(item_id);
+                    navigation.paths_by_item.remove(&item_view.id());
                 }
 
                 if item_ix == self.active_item_index {
-                    item.deactivated(cx);
+                    item_view.deactivated(cx);
                 }
                 item_ix += 1;
                 false

crates/workspace/src/workspace.rs 🔗

@@ -227,6 +227,7 @@ pub trait ItemViewHandle {
 }
 
 pub trait WeakItemViewHandle {
+    fn id(&self) -> usize;
     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemViewHandle>>;
 }
 
@@ -418,6 +419,10 @@ impl Clone for Box<dyn ItemHandle> {
 }
 
 impl<T: ItemView> WeakItemViewHandle for WeakViewHandle<T> {
+    fn id(&self) -> usize {
+        self.id()
+    }
+
     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemViewHandle>> {
         self.upgrade(cx)
             .map(|v| Box::new(v) as Box<dyn ItemViewHandle>)
@@ -747,46 +752,49 @@ impl Workspace {
         }
     }
 
-    #[must_use]
     pub fn open_path(
         &mut self,
         path: ProjectPath,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<Box<dyn ItemViewHandle>, Arc<anyhow::Error>>> {
+        let load_task = self.load_path(path, cx);
+        let pane = self.active_pane().clone().downgrade();
+        cx.spawn(|this, mut cx| async move {
+            let item = load_task.await?;
+            this.update(&mut cx, |this, cx| {
+                let pane = pane
+                    .upgrade(&cx)
+                    .ok_or_else(|| anyhow!("could not upgrade pane reference"))?;
+                Ok(this.open_item_in_pane(item, &pane, cx))
+            })
+        })
+    }
+
+    pub fn load_path(
+        &mut self,
+        path: ProjectPath,
+        cx: &mut ViewContext<Self>,
+    ) -> Task<Result<Box<dyn ItemHandle>>> {
         if let Some(existing_item) = self.item_for_path(&path, cx) {
-            return Task::ready(Ok(self.open_item(existing_item, cx)));
+            return Task::ready(Ok(existing_item));
         }
 
         let worktree = match self.project.read(cx).worktree_for_id(path.worktree_id, cx) {
             Some(worktree) => worktree,
             None => {
-                return Task::ready(Err(Arc::new(anyhow!(
-                    "worktree {} does not exist",
-                    path.worktree_id
-                ))));
+                return Task::ready(Err(anyhow!("worktree {} does not exist", path.worktree_id)));
             }
         };
 
         let project_path = path.clone();
         let path_openers = self.path_openers.clone();
-        let open_task = worktree.update(cx, |worktree, cx| {
+        worktree.update(cx, |worktree, cx| {
             for opener in path_openers.iter() {
                 if let Some(task) = opener.open(worktree, project_path.clone(), cx) {
                     return task;
                 }
             }
             Task::ready(Err(anyhow!("no opener found for path {:?}", project_path)))
-        });
-
-        let pane = self.active_pane().clone().downgrade();
-        cx.spawn(|this, mut cx| async move {
-            let item = open_task.await?;
-            this.update(&mut cx, |this, cx| {
-                let pane = pane
-                    .upgrade(&cx)
-                    .ok_or_else(|| anyhow!("could not upgrade pane reference"))?;
-                Ok(this.open_item_in_pane(item, &pane, cx))
-            })
         })
     }