Replace `project_path` with `project_entry` in `workspace::{Item, ItemView}`

Antonio Scandurra created

Change summary

crates/diagnostics/src/diagnostics.rs |  4 
crates/editor/src/items.rs            | 18 ++-----
crates/project/src/project.rs         |  8 +++
crates/project/src/worktree.rs        |  9 +++
crates/workspace/src/pane.rs          | 30 ++++++------
crates/workspace/src/workspace.rs     | 50 ++++++++++++++-------
crates/zed/src/zed.rs                 | 67 +++++++++++++++++++++-------
7 files changed, 122 insertions(+), 64 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -528,7 +528,7 @@ impl workspace::Item for ProjectDiagnostics {
         ProjectDiagnosticsEditor::new(handle, workspace.weak_handle(), workspace.settings(), cx)
     }
 
-    fn project_path(&self) -> Option<project::ProjectPath> {
+    fn project_entry(&self) -> Option<project::ProjectEntry> {
         None
     }
 }
@@ -544,7 +544,7 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
         "Project Diagnostics".to_string()
     }
 
-    fn project_path(&self, _: &AppContext) -> Option<project::ProjectPath> {
+    fn project_entry(&self, _: &AppContext) -> Option<project::ProjectEntry> {
         None
     }
 

crates/editor/src/items.rs 🔗

@@ -6,7 +6,7 @@ use gpui::{
 };
 use language::{Bias, Buffer, Diagnostic, File as _};
 use postage::watch;
-use project::{File, ProjectPath, Worktree};
+use project::{File, ProjectEntry, ProjectPath, Worktree};
 use std::fmt::Write;
 use std::path::Path;
 use std::rc::Rc;
@@ -74,11 +74,8 @@ impl ItemHandle for BufferItemHandle {
         Box::new(WeakBufferItemHandle(self.0.downgrade()))
     }
 
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        File::from_dyn(self.0.read(cx).file()).map(|f| ProjectPath {
-            worktree_id: f.worktree_id(cx),
-            path: f.path().clone(),
-        })
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
+        File::from_dyn(self.0.read(cx).file()).and_then(|f| f.project_entry(cx))
     }
 
     fn id(&self) -> usize {
@@ -134,11 +131,8 @@ impl ItemView for Editor {
         }
     }
 
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        File::from_dyn(self.buffer().read(cx).file(cx)).map(|file| ProjectPath {
-            worktree_id: file.worktree_id(cx),
-            path: file.path().clone(),
-        })
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
+        File::from_dyn(self.buffer().read(cx).file(cx)).and_then(|file| file.project_entry(cx))
     }
 
     fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
@@ -163,7 +157,7 @@ impl ItemView for Editor {
     }
 
     fn can_save(&self, cx: &AppContext) -> bool {
-        self.project_path(cx).is_some()
+        self.project_entry(cx).is_some()
     }
 
     fn save(&mut self, cx: &mut ViewContext<Self>) -> Result<Task<Result<()>>> {

crates/project/src/project.rs 🔗

@@ -567,6 +567,14 @@ impl Project {
         }
     }
 
+    pub fn path_for_entry(&self, entry: ProjectEntry, cx: &AppContext) -> Option<ProjectPath> {
+        let worktree = self.worktree_for_id(entry.worktree_id, cx)?.read(cx);
+        Some(ProjectPath {
+            worktree_id: entry.worktree_id,
+            path: worktree.entry_for_id(entry.entry_id)?.path.clone(),
+        })
+    }
+
     pub fn is_running_disk_based_diagnostics(&self) -> bool {
         self.pending_disk_based_diagnostics > 0
     }

crates/project/src/worktree.rs 🔗

@@ -1,7 +1,7 @@
 use super::{
     fs::{self, Fs},
     ignore::IgnoreStack,
-    DiagnosticSummary,
+    DiagnosticSummary, ProjectEntry,
 };
 use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
 use anyhow::{anyhow, Context, Result};
@@ -2372,6 +2372,13 @@ impl File {
     pub fn worktree_id(&self, cx: &AppContext) -> WorktreeId {
         self.worktree.read(cx).id()
     }
+
+    pub fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
+        self.entry_id.map(|entry_id| ProjectEntry {
+            worktree_id: self.worktree_id(cx),
+            entry_id,
+        })
+    }
 }
 
 #[derive(Clone, Debug)]

crates/workspace/src/pane.rs 🔗

@@ -10,7 +10,7 @@ use gpui::{
     Entity, MutableAppContext, Quad, RenderContext, Task, View, ViewContext, ViewHandle,
 };
 use postage::watch;
-use project::ProjectPath;
+use project::ProjectEntry;
 use std::{any::Any, cell::RefCell, cmp, mem, rc::Rc};
 use util::ResultExt;
 
@@ -87,7 +87,7 @@ struct NavHistoryState {
     mode: NavigationMode,
     backward_stack: VecDeque<NavigationEntry>,
     forward_stack: VecDeque<NavigationEntry>,
-    paths_by_item: HashMap<usize, ProjectPath>,
+    project_entries_by_item: HashMap<usize, ProjectEntry>,
 }
 
 #[derive(Copy, Clone)]
@@ -148,10 +148,10 @@ impl Pane {
     ) -> Task<()> {
         let to_load = pane.update(cx, |pane, cx| {
             // Retrieve the weak item handle from the history.
-            let entry = pane.nav_history.pop(mode)?;
+            let nav_entry = pane.nav_history.pop(mode)?;
 
             // If the item is still present in this pane, then activate it.
-            if let Some(index) = entry
+            if let Some(index) = nav_entry
                 .item_view
                 .upgrade(cx)
                 .and_then(|v| pane.index_for_item_view(v.as_ref()))
@@ -164,7 +164,7 @@ impl Pane {
 
                 pane.active_item_index = index;
                 pane.focus_active_item(cx);
-                if let Some(data) = entry.data {
+                if let Some(data) = nav_entry.data {
                     pane.active_item()?.navigate(data, cx);
                 }
                 cx.notify();
@@ -176,17 +176,17 @@ impl Pane {
                 pane.nav_history
                     .0
                     .borrow_mut()
-                    .paths_by_item
-                    .get(&entry.item_view.id())
+                    .project_entries_by_item
+                    .get(&nav_entry.item_view.id())
                     .cloned()
-                    .map(|project_path| (project_path, entry))
+                    .map(|project_entry| (project_entry, nav_entry))
             }
         });
 
-        if let Some((project_path, entry)) = to_load {
+        if let Some((project_entry, nav_entry)) = to_load {
             // If the item was no longer present, then load it again from its previous path.
             let pane = pane.downgrade();
-            let task = workspace.load_path(project_path, cx);
+            let task = workspace.load_entry(project_entry, cx);
             cx.spawn(|workspace, mut cx| async move {
                 let item = task.await;
                 if let Some(pane) = cx.read(|cx| pane.upgrade(cx)) {
@@ -196,7 +196,7 @@ impl Pane {
                             let item_view = workspace.open_item_in_pane(item, &pane, cx);
                             pane.update(cx, |p, _| p.nav_history.set_mode(NavigationMode::Normal));
 
-                            if let Some(data) = entry.data {
+                            if let Some(data) = nav_entry.data {
                                 item_view.navigate(data, cx);
                             }
                         });
@@ -323,10 +323,12 @@ impl Pane {
                 }
 
                 let mut nav_history = self.nav_history.0.borrow_mut();
-                if let Some(path) = item_view.project_path(cx) {
-                    nav_history.paths_by_item.insert(item_view.id(), path);
+                if let Some(entry) = item_view.project_entry(cx) {
+                    nav_history
+                        .project_entries_by_item
+                        .insert(item_view.id(), entry);
                 } else {
-                    nav_history.paths_by_item.remove(&item_view.id());
+                    nav_history.project_entries_by_item.remove(&item_view.id());
                 }
 
                 item_ix += 1;

crates/workspace/src/workspace.rs 🔗

@@ -27,7 +27,7 @@ pub use pane::*;
 pub use pane_group::*;
 use parking_lot::Mutex;
 use postage::{prelude::Stream, watch};
-use project::{fs, Fs, Project, ProjectPath, Worktree};
+use project::{fs, Fs, Project, ProjectEntry, ProjectPath, Worktree};
 pub use settings::Settings;
 use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
 use status_bar::StatusBar;
@@ -140,8 +140,7 @@ pub trait Item: Entity + Sized {
         nav_history: Rc<NavHistory>,
         cx: &mut ViewContext<Self::View>,
     ) -> Self::View;
-
-    fn project_path(&self) -> Option<ProjectPath>;
+    fn project_entry(&self) -> Option<ProjectEntry>;
 }
 
 pub trait ItemView: View {
@@ -151,7 +150,7 @@ pub trait ItemView: View {
     fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
     fn item_handle(&self, cx: &AppContext) -> Self::ItemHandle;
     fn title(&self, cx: &AppContext) -> String;
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry>;
     fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
     where
         Self: Sized,
@@ -196,7 +195,7 @@ pub trait ItemHandle: Send + Sync {
     fn boxed_clone(&self) -> Box<dyn ItemHandle>;
     fn downgrade(&self) -> Box<dyn WeakItemHandle>;
     fn to_any(&self) -> AnyModelHandle;
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry>;
 }
 
 pub trait WeakItemHandle {
@@ -207,7 +206,7 @@ pub trait WeakItemHandle {
 pub trait ItemViewHandle {
     fn item_handle(&self, cx: &AppContext) -> Box<dyn ItemHandle>;
     fn title(&self, cx: &AppContext) -> String;
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry>;
     fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
     fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
     fn added_to_pane(&mut self, cx: &mut ViewContext<Pane>);
@@ -262,8 +261,8 @@ impl<T: Item> ItemHandle for ModelHandle<T> {
         self.clone().into()
     }
 
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        self.read(cx).project_path()
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
+        self.read(cx).project_entry()
     }
 }
 
@@ -294,8 +293,8 @@ impl ItemHandle for Box<dyn ItemHandle> {
         self.as_ref().to_any()
     }
 
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        self.as_ref().project_path(cx)
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
+        self.as_ref().project_entry(cx)
     }
 }
 
@@ -332,8 +331,8 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
         self.read(cx).title(cx)
     }
 
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        self.read(cx).project_path(cx)
+    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry> {
+        self.read(cx).project_entry(cx)
     }
 
     fn boxed_clone(&self) -> Box<dyn ItemViewHandle> {
@@ -776,6 +775,18 @@ impl Workspace {
         })
     }
 
+    pub fn load_entry(
+        &mut self,
+        path: ProjectEntry,
+        cx: &mut ViewContext<Self>,
+    ) -> Task<Result<Box<dyn ItemHandle>>> {
+        if let Some(path) = self.project.read(cx).path_for_entry(path, cx) {
+            self.load_path(path, cx)
+        } else {
+            Task::ready(Err(anyhow!("entry does not exist")))
+        }
+    }
+
     pub fn load_path(
         &mut self,
         path: ProjectPath,
@@ -805,10 +816,13 @@ impl Workspace {
     }
 
     fn item_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
-        self.items
-            .iter()
-            .filter_map(|i| i.upgrade(cx))
-            .find(|i| i.project_path(cx).as_ref() == Some(path))
+        let project = self.project.read(cx);
+        self.items.iter().filter_map(|i| i.upgrade(cx)).find(|i| {
+            let item_path = i
+                .project_entry(cx)
+                .and_then(|entry| project.path_for_entry(entry, cx));
+            item_path.as_ref() == Some(path)
+        })
     }
 
     pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ModelHandle<T>> {
@@ -822,7 +836,9 @@ impl Workspace {
     }
 
     fn active_project_path(&self, cx: &ViewContext<Self>) -> Option<ProjectPath> {
-        self.active_item(cx).and_then(|item| item.project_path(cx))
+        self.active_item(cx)
+            .and_then(|item| item.project_entry(cx))
+            .and_then(|entry| self.project.read(cx).path_for_entry(entry, cx))
     }
 
     pub fn save_active_item(&mut self, _: &Save, cx: &mut ViewContext<Self>) {

crates/zed/src/zed.rs 🔗

@@ -268,9 +268,11 @@ mod tests {
             .await
             .unwrap();
         cx.read(|cx| {
-            let pane = workspace.read(cx).active_pane().read(cx);
+            let workspace = workspace.read(cx);
+            let pane = workspace.active_pane().read(cx);
+            let entry = pane.active_item().unwrap().project_entry(cx).unwrap();
             assert_eq!(
-                pane.active_item().unwrap().project_path(cx),
+                workspace.project().read(cx).path_for_entry(entry, cx),
                 Some(file1.clone())
             );
             assert_eq!(pane.item_views().count(), 1);
@@ -282,9 +284,11 @@ mod tests {
             .await
             .unwrap();
         cx.read(|cx| {
-            let pane = workspace.read(cx).active_pane().read(cx);
+            let workspace = workspace.read(cx);
+            let pane = workspace.active_pane().read(cx);
+            let entry = pane.active_item().unwrap().project_entry(cx).unwrap();
             assert_eq!(
-                pane.active_item().unwrap().project_path(cx),
+                workspace.project().read(cx).path_for_entry(entry, cx),
                 Some(file2.clone())
             );
             assert_eq!(pane.item_views().count(), 2);
@@ -298,9 +302,11 @@ mod tests {
         assert_eq!(entry_1.id(), entry_1b.id());
 
         cx.read(|cx| {
-            let pane = workspace.read(cx).active_pane().read(cx);
+            let workspace = workspace.read(cx);
+            let pane = workspace.active_pane().read(cx);
+            let entry = pane.active_item().unwrap().project_entry(cx).unwrap();
             assert_eq!(
-                pane.active_item().unwrap().project_path(cx),
+                workspace.project().read(cx).path_for_entry(entry, cx),
                 Some(file1.clone())
             );
             assert_eq!(pane.item_views().count(), 2);
@@ -315,13 +321,11 @@ mod tests {
             .await
             .unwrap();
 
-        workspace.read_with(&cx, |w, cx| {
+        workspace.read_with(&cx, |workspace, cx| {
+            let pane = workspace.active_pane().read(cx);
+            let entry = pane.active_item().unwrap().project_entry(cx).unwrap();
             assert_eq!(
-                w.active_pane()
-                    .read(cx)
-                    .active_item()
-                    .unwrap()
-                    .project_path(cx.as_ref()),
+                workspace.project().read(cx).path_for_entry(entry, cx),
                 Some(file2.clone())
             );
         });
@@ -336,14 +340,22 @@ mod tests {
         t1.await.unwrap();
         t2.await.unwrap();
         cx.read(|cx| {
-            let pane = workspace.read(cx).active_pane().read(cx);
+            let workspace = workspace.read(cx);
+            let pane = workspace.active_pane().read(cx);
+            let entry = pane.active_item().unwrap().project_entry(cx).unwrap();
             assert_eq!(
-                pane.active_item().unwrap().project_path(cx),
+                workspace.project().read(cx).path_for_entry(entry, cx),
                 Some(file3.clone())
             );
             let pane_entries = pane
                 .item_views()
-                .map(|i| i.project_path(cx).unwrap())
+                .map(|i| {
+                    workspace
+                        .project()
+                        .read(cx)
+                        .path_for_entry(i.project_entry(cx).unwrap(), cx)
+                        .unwrap()
+                })
                 .collect::<Vec<_>>();
             assert_eq!(pane_entries, &[file1, file2, file3]);
         });
@@ -666,8 +678,15 @@ mod tests {
             .await
             .unwrap();
         cx.read(|cx| {
+            let workspace = workspace.read(cx);
+            let pane1_entry = pane_1
+                .read(cx)
+                .active_item()
+                .unwrap()
+                .project_entry(cx)
+                .unwrap();
             assert_eq!(
-                pane_1.read(cx).active_item().unwrap().project_path(cx),
+                workspace.project().read(cx).path_for_entry(pane1_entry, cx),
                 Some(file1.clone())
             );
         });
@@ -682,7 +701,15 @@ mod tests {
             assert_ne!(pane_1, pane_2);
 
             let pane2_item = pane_2.read(cx).active_item().unwrap();
-            assert_eq!(pane2_item.project_path(cx.as_ref()), Some(file1.clone()));
+            let pane2_entry = pane2_item.project_entry(cx).unwrap();
+            assert_eq!(
+                workspace
+                    .read(cx)
+                    .project()
+                    .read(cx)
+                    .path_for_entry(pane2_entry, cx),
+                Some(file1.clone())
+            );
 
             cx.dispatch_action(window_id, vec![pane_2.id()], &workspace::CloseActiveItem);
             let workspace = workspace.read(cx);
@@ -862,7 +889,11 @@ mod tests {
                 let item = workspace.active_item(cx).unwrap();
                 let editor = item.to_any().downcast::<Editor>().unwrap();
                 let selections = editor.update(cx, |editor, cx| editor.selected_display_ranges(cx));
-                (item.project_path(cx).unwrap(), selections[0].start)
+                let path = workspace
+                    .project()
+                    .read(cx)
+                    .path_for_entry(item.project_entry(cx).unwrap(), cx);
+                (path.unwrap(), selections[0].start)
             })
         }
     }