Replace ProjectEntry struct with ProjectEntryId

Nathan Sobo , Max Brunsfeld , and Keith Simmons created

Previously, we tracked the worktree_id and entry_id separately, but now that entry ids are unique across all worktrees this is unnecessary.

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-Authored-By: Keith Simmons <keith@the-simmons.net>

Change summary

crates/diagnostics/src/diagnostics.rs     |   2 
crates/editor/src/editor.rs               |   4 
crates/editor/src/items.rs                |   6 
crates/project/src/project.rs             |  61 ++++++++++----
crates/project/src/worktree.rs            |  51 +++++------
crates/project_panel/src/project_panel.rs | 106 +++++++++---------------
crates/search/src/project_search.rs       |   2 
crates/workspace/src/pane.rs              |   8 
crates/workspace/src/workspace.rs         |  12 +-
9 files changed, 126 insertions(+), 126 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -449,7 +449,7 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
         None
     }
 
-    fn project_entry(&self, _: &AppContext) -> Option<project::ProjectEntry> {
+    fn project_entry_id(&self, _: &AppContext) -> Option<project::ProjectEntryId> {
         None
     }
 

crates/editor/src/editor.rs 🔗

@@ -842,8 +842,8 @@ impl Editor {
     ) -> ViewHandle<Editor> {
         let project = workspace.project().clone();
 
-        if let Some(project_entry) =
-            project::File::from_dyn(buffer.read(cx).file()).and_then(|file| file.project_entry(cx))
+        if let Some(project_entry) = project::File::from_dyn(buffer.read(cx).file())
+            .and_then(|file| file.project_entry_id(cx))
         {
             return workspace
                 .open_item_for_project_entry(project_entry, cx, |cx| {

crates/editor/src/items.rs 🔗

@@ -5,7 +5,7 @@ use gpui::{
     View, ViewContext, ViewHandle, WeakModelHandle,
 };
 use language::{Bias, Buffer, Diagnostic, File as _};
-use project::{File, Project, ProjectEntry, ProjectPath};
+use project::{File, Project, ProjectEntryId, ProjectPath};
 use std::fmt::Write;
 use std::path::PathBuf;
 use text::{Point, Selection};
@@ -75,8 +75,8 @@ impl ItemView for Editor {
         })
     }
 
-    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 project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
+        File::from_dyn(self.buffer().read(cx).file(cx)).and_then(|file| file.project_entry_id(cx))
     }
 
     fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>

crates/project/src/project.rs 🔗

@@ -39,7 +39,7 @@ use std::{
     path::{Component, Path, PathBuf},
     rc::Rc,
     sync::{
-        atomic::{AtomicBool, AtomicUsize},
+        atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
         Arc,
     },
     time::Instant,
@@ -51,7 +51,7 @@ pub use worktree::*;
 
 pub struct Project {
     worktrees: Vec<WorktreeHandle>,
-    active_entry: Option<ProjectEntry>,
+    active_entry: Option<ProjectEntryId>,
     languages: Arc<LanguageRegistry>,
     language_servers: HashMap<(WorktreeId, Arc<str>), Arc<LanguageServer>>,
     started_language_servers: HashMap<(WorktreeId, Arc<str>), Task<Option<Arc<LanguageServer>>>>,
@@ -114,7 +114,7 @@ pub struct Collaborator {
 
 #[derive(Clone, Debug, PartialEq)]
 pub enum Event {
-    ActiveEntryChanged(Option<ProjectEntry>),
+    ActiveEntryChanged(Option<ProjectEntryId>),
     WorktreeRemoved(WorktreeId),
     DiskBasedDiagnosticsStarted,
     DiskBasedDiagnosticsUpdated,
@@ -226,10 +226,25 @@ impl DiagnosticSummary {
     }
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct ProjectEntry {
-    pub worktree_id: WorktreeId,
-    pub entry_id: usize,
+#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ProjectEntryId(usize);
+
+impl ProjectEntryId {
+    pub fn new(counter: &AtomicUsize) -> Self {
+        Self(counter.fetch_add(1, SeqCst))
+    }
+
+    pub fn from_proto(id: u64) -> Self {
+        Self(id as usize)
+    }
+
+    pub fn to_proto(&self) -> u64 {
+        self.0 as u64
+    }
+
+    pub fn to_usize(&self) -> usize {
+        self.0
+    }
 }
 
 impl Project {
@@ -623,6 +638,24 @@ impl Project {
             .find(|worktree| worktree.read(cx).id() == id)
     }
 
+    pub fn worktree_for_entry(
+        &self,
+        entry_id: ProjectEntryId,
+        cx: &AppContext,
+    ) -> Option<ModelHandle<Worktree>> {
+        self.worktrees(cx)
+            .find(|worktree| worktree.read(cx).contains_entry(entry_id))
+    }
+
+    pub fn worktree_id_for_entry(
+        &self,
+        entry_id: ProjectEntryId,
+        cx: &AppContext,
+    ) -> Option<WorktreeId> {
+        self.worktree_for_entry(entry_id, cx)
+            .map(|worktree| worktree.read(cx).id())
+    }
+
     pub fn share(&self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
         let rpc = self.client.clone();
         cx.spawn(|this, mut cx| async move {
@@ -3163,10 +3196,7 @@ impl Project {
         let new_active_entry = entry.and_then(|project_path| {
             let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
             let entry = worktree.read(cx).entry_for_path(project_path.path)?;
-            Some(ProjectEntry {
-                worktree_id: project_path.worktree_id,
-                entry_id: entry.id,
-            })
+            Some(entry.id)
         });
         if new_active_entry != self.active_entry {
             self.active_entry = new_active_entry;
@@ -3217,18 +3247,15 @@ impl Project {
         }
     }
 
-    pub fn active_entry(&self) -> Option<ProjectEntry> {
+    pub fn active_entry(&self) -> Option<ProjectEntryId> {
         self.active_entry
     }
 
-    pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<ProjectEntry> {
+    pub fn entry_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<ProjectEntryId> {
         self.worktree_for_id(path.worktree_id, cx)?
             .read(cx)
             .entry_for_path(&path.path)
-            .map(|entry| ProjectEntry {
-                worktree_id: path.worktree_id,
-                entry_id: entry.id,
-            })
+            .map(|entry| entry.id)
     }
 
     // RPC message handlers

crates/project/src/worktree.rs 🔗

@@ -1,4 +1,4 @@
-use crate::ProjectEntry;
+use crate::ProjectEntryId;
 
 use super::{
     fs::{self, Fs},
@@ -41,10 +41,7 @@ use std::{
     future::Future,
     ops::{Deref, DerefMut},
     path::{Path, PathBuf},
-    sync::{
-        atomic::{AtomicUsize, Ordering::SeqCst},
-        Arc,
-    },
+    sync::{atomic::AtomicUsize, Arc},
     time::{Duration, SystemTime},
 };
 use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap};
@@ -103,7 +100,7 @@ pub struct LocalSnapshot {
     abs_path: Arc<Path>,
     scan_id: usize,
     ignores: HashMap<Arc<Path>, (Arc<Gitignore>, usize)>,
-    removed_entry_ids: HashMap<u64, usize>,
+    removed_entry_ids: HashMap<u64, ProjectEntryId>,
     next_entry_id: Arc<AtomicUsize>,
     snapshot: Snapshot,
 }
@@ -858,13 +855,16 @@ impl Snapshot {
         self.id
     }
 
+    pub fn contains_entry(&self, entry_id: ProjectEntryId) -> bool {
+        self.entries_by_id.get(&entry_id, &()).is_some()
+    }
+
     pub(crate) fn apply_remote_update(&mut self, update: proto::UpdateWorktree) -> Result<()> {
         let mut entries_by_path_edits = Vec::new();
         let mut entries_by_id_edits = Vec::new();
         for entry_id in update.removed_entries {
-            let entry_id = entry_id as usize;
             let entry = self
-                .entry_for_id(entry_id)
+                .entry_for_id(ProjectEntryId::from_proto(entry_id))
                 .ok_or_else(|| anyhow!("unknown entry"))?;
             entries_by_path_edits.push(Edit::Remove(PathKey(entry.path.clone())));
             entries_by_id_edits.push(Edit::Remove(entry.id));
@@ -987,7 +987,7 @@ impl Snapshot {
             })
     }
 
-    pub fn entry_for_id(&self, id: usize) -> Option<&Entry> {
+    pub fn entry_for_id(&self, id: ProjectEntryId) -> Option<&Entry> {
         let entry = self.entries_by_id.get(&id, &())?;
         self.entry_for_path(&entry.path)
     }
@@ -1064,7 +1064,7 @@ impl LocalSnapshot {
                             other_entries.next();
                         }
                         Ordering::Greater => {
-                            removed_entries.push(other_entry.id as u64);
+                            removed_entries.push(other_entry.id.to_proto());
                             other_entries.next();
                         }
                     }
@@ -1075,7 +1075,7 @@ impl LocalSnapshot {
                     self_entries.next();
                 }
                 (None, Some(other_entry)) => {
-                    removed_entries.push(other_entry.id as u64);
+                    removed_entries.push(other_entry.id.to_proto());
                     other_entries.next();
                 }
                 (None, None) => break,
@@ -1328,7 +1328,7 @@ pub struct File {
     pub worktree: ModelHandle<Worktree>,
     pub path: Arc<Path>,
     pub mtime: SystemTime,
-    pub(crate) entry_id: Option<usize>,
+    pub(crate) entry_id: Option<ProjectEntryId>,
     pub(crate) is_local: bool,
 }
 
@@ -1425,7 +1425,7 @@ impl language::File for File {
     fn to_proto(&self) -> rpc::proto::File {
         rpc::proto::File {
             worktree_id: self.worktree.id() as u64,
-            entry_id: self.entry_id.map(|entry_id| entry_id as u64),
+            entry_id: self.entry_id.map(|entry_id| entry_id.to_proto()),
             path: self.path.to_string_lossy().into(),
             mtime: Some(self.mtime.into()),
         }
@@ -1492,7 +1492,7 @@ impl File {
             worktree,
             path: Path::new(&proto.path).into(),
             mtime: proto.mtime.ok_or_else(|| anyhow!("no timestamp"))?.into(),
-            entry_id: proto.entry_id.map(|entry_id| entry_id as usize),
+            entry_id: proto.entry_id.map(ProjectEntryId::from_proto),
             is_local: false,
         })
     }
@@ -1505,17 +1505,14 @@ impl File {
         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,
-        })
+    pub fn project_entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
+        self.entry_id
     }
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct Entry {
-    pub id: usize,
+    pub id: ProjectEntryId,
     pub kind: EntryKind,
     pub path: Arc<Path>,
     pub inode: u64,
@@ -1539,7 +1536,7 @@ impl Entry {
         root_char_bag: CharBag,
     ) -> Self {
         Self {
-            id: next_entry_id.fetch_add(1, SeqCst),
+            id: ProjectEntryId::new(next_entry_id),
             kind: if metadata.is_dir {
                 EntryKind::PendingDir
             } else {
@@ -1629,7 +1626,7 @@ impl sum_tree::Summary for EntrySummary {
 
 #[derive(Clone, Debug)]
 struct PathEntry {
-    id: usize,
+    id: ProjectEntryId,
     path: Arc<Path>,
     is_ignored: bool,
     scan_id: usize,
@@ -1644,7 +1641,7 @@ impl sum_tree::Item for PathEntry {
 }
 
 impl sum_tree::KeyedItem for PathEntry {
-    type Key = usize;
+    type Key = ProjectEntryId;
 
     fn key(&self) -> Self::Key {
         self.id
@@ -1653,7 +1650,7 @@ impl sum_tree::KeyedItem for PathEntry {
 
 #[derive(Clone, Debug, Default)]
 struct PathEntrySummary {
-    max_id: usize,
+    max_id: ProjectEntryId,
 }
 
 impl sum_tree::Summary for PathEntrySummary {
@@ -1664,7 +1661,7 @@ impl sum_tree::Summary for PathEntrySummary {
     }
 }
 
-impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for usize {
+impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for ProjectEntryId {
     fn add_summary(&mut self, summary: &'a PathEntrySummary, _: &()) {
         *self = summary.max_id;
     }
@@ -2354,7 +2351,7 @@ impl<'a> Iterator for ChildEntriesIter<'a> {
 impl<'a> From<&'a Entry> for proto::Entry {
     fn from(entry: &'a Entry) -> Self {
         Self {
-            id: entry.id as u64,
+            id: entry.id.to_proto(),
             is_dir: entry.is_dir(),
             path: entry.path.to_string_lossy().to_string(),
             inode: entry.inode,
@@ -2379,7 +2376,7 @@ impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry {
             };
             let path: Arc<Path> = Arc::from(Path::new(&entry.path));
             Ok(Entry {
-                id: entry.id as usize,
+                id: ProjectEntryId::from_proto(entry.id),
                 kind,
                 path: path.clone(),
                 inode: entry.inode,

crates/project_panel/src/project_panel.rs 🔗

@@ -9,7 +9,7 @@ use gpui::{
     AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, View, ViewContext,
     ViewHandle, WeakViewHandle,
 };
-use project::{Project, ProjectEntry, ProjectPath, Worktree, WorktreeId};
+use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
 use std::{
     collections::{hash_map, HashMap},
     ffi::OsStr,
@@ -24,7 +24,7 @@ pub struct ProjectPanel {
     project: ModelHandle<Project>,
     list: UniformListState,
     visible_entries: Vec<(WorktreeId, Vec<usize>)>,
-    expanded_dir_ids: HashMap<WorktreeId, Vec<usize>>,
+    expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
     selection: Option<Selection>,
     handle: WeakViewHandle<Self>,
 }
@@ -32,7 +32,7 @@ pub struct ProjectPanel {
 #[derive(Copy, Clone)]
 struct Selection {
     worktree_id: WorktreeId,
-    entry_id: usize,
+    entry_id: ProjectEntryId,
     index: usize,
 }
 
@@ -47,8 +47,8 @@ struct EntryDetails {
 
 action!(ExpandSelectedEntry);
 action!(CollapseSelectedEntry);
-action!(ToggleExpanded, ProjectEntry);
-action!(Open, ProjectEntry);
+action!(ToggleExpanded, ProjectEntryId);
+action!(Open, ProjectEntryId);
 
 pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(ProjectPanel::expand_selected_entry);
@@ -64,10 +64,7 @@ pub fn init(cx: &mut MutableAppContext) {
 }
 
 pub enum Event {
-    OpenedEntry {
-        worktree_id: WorktreeId,
-        entry_id: usize,
-    },
+    OpenedEntry(ProjectEntryId),
 }
 
 impl ProjectPanel {
@@ -78,15 +75,15 @@ impl ProjectPanel {
                 cx.notify();
             })
             .detach();
-            cx.subscribe(&project, |this, _, event, cx| match event {
-                project::Event::ActiveEntryChanged(Some(ProjectEntry {
-                    worktree_id,
-                    entry_id,
-                })) => {
-                    this.expand_entry(*worktree_id, *entry_id, cx);
-                    this.update_visible_entries(Some((*worktree_id, *entry_id)), cx);
-                    this.autoscroll();
-                    cx.notify();
+            cx.subscribe(&project, |this, project, event, cx| match event {
+                project::Event::ActiveEntryChanged(Some(entry_id)) => {
+                    if let Some(worktree_id) = project.read(cx).worktree_id_for_entry(*entry_id, cx)
+                    {
+                        this.expand_entry(worktree_id, *entry_id, cx);
+                        this.update_visible_entries(Some((worktree_id, *entry_id)), cx);
+                        this.autoscroll();
+                        cx.notify();
+                    }
                 }
                 project::Event::WorktreeRemoved(id) => {
                     this.expanded_dir_ids.remove(id);
@@ -109,16 +106,13 @@ impl ProjectPanel {
             this
         });
         cx.subscribe(&project_panel, move |workspace, _, event, cx| match event {
-            &Event::OpenedEntry {
-                worktree_id,
-                entry_id,
-            } => {
-                if let Some(worktree) = project.read(cx).worktree_for_id(worktree_id, cx) {
+            &Event::OpenedEntry(entry_id) => {
+                if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) {
                     if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
                         workspace
                             .open_path(
                                 ProjectPath {
-                                    worktree_id,
+                                    worktree_id: worktree.read(cx).id(),
                                     path: entry.path.clone(),
                                 },
                                 cx,
@@ -152,10 +146,7 @@ impl ProjectPanel {
                     }
                 }
             } else {
-                let event = Event::OpenedEntry {
-                    worktree_id: worktree.id(),
-                    entry_id: entry.id,
-                };
+                let event = Event::OpenedEntry(entry.id);
                 cx.emit(event);
             }
         }
@@ -193,22 +184,20 @@ impl ProjectPanel {
     }
 
     fn toggle_expanded(&mut self, action: &ToggleExpanded, cx: &mut ViewContext<Self>) {
-        let ProjectEntry {
-            worktree_id,
-            entry_id,
-        } = action.0;
-
-        if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
-            match expanded_dir_ids.binary_search(&entry_id) {
-                Ok(ix) => {
-                    expanded_dir_ids.remove(ix);
-                }
-                Err(ix) => {
-                    expanded_dir_ids.insert(ix, entry_id);
+        let entry_id = action.0;
+        if let Some(worktree_id) = self.project.read(cx).worktree_id_for_entry(entry_id, cx) {
+            if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
+                match expanded_dir_ids.binary_search(&entry_id) {
+                    Ok(ix) => {
+                        expanded_dir_ids.remove(ix);
+                    }
+                    Err(ix) => {
+                        expanded_dir_ids.insert(ix, entry_id);
+                    }
                 }
+                self.update_visible_entries(Some((worktree_id, entry_id)), cx);
+                cx.focus_self();
             }
-            self.update_visible_entries(Some((worktree_id, entry_id)), cx);
-            cx.focus_self();
         }
     }
 
@@ -229,10 +218,7 @@ impl ProjectPanel {
     }
 
     fn open_entry(&mut self, action: &Open, cx: &mut ViewContext<Self>) {
-        cx.emit(Event::OpenedEntry {
-            worktree_id: action.0.worktree_id,
-            entry_id: action.0.entry_id,
-        });
+        cx.emit(Event::OpenedEntry(action.0));
     }
 
     fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
@@ -313,7 +299,7 @@ impl ProjectPanel {
 
     fn update_visible_entries(
         &mut self,
-        new_selected_entry: Option<(WorktreeId, usize)>,
+        new_selected_entry: Option<(WorktreeId, ProjectEntryId)>,
         cx: &mut ViewContext<Self>,
     ) {
         let worktrees = self
@@ -379,7 +365,7 @@ impl ProjectPanel {
     fn expand_entry(
         &mut self,
         worktree_id: WorktreeId,
-        entry_id: usize,
+        entry_id: ProjectEntryId,
         cx: &mut ViewContext<Self>,
     ) {
         let project = self.project.read(cx);
@@ -411,7 +397,7 @@ impl ProjectPanel {
         &self,
         range: Range<usize>,
         cx: &mut ViewContext<ProjectPanel>,
-        mut callback: impl FnMut(ProjectEntry, EntryDetails, &mut ViewContext<ProjectPanel>),
+        mut callback: impl FnMut(ProjectEntryId, EntryDetails, &mut ViewContext<ProjectPanel>),
     ) {
         let mut ix = 0;
         for (worktree_id, visible_worktree_entries) in &self.visible_entries {
@@ -450,11 +436,7 @@ impl ProjectPanel {
                                 e.worktree_id == snapshot.id() && e.entry_id == entry.id
                             }),
                         };
-                        let entry = ProjectEntry {
-                            worktree_id: snapshot.id(),
-                            entry_id: entry.id,
-                        };
-                        callback(entry, details, cx);
+                        callback(entry.id, details, cx);
                     }
                 }
             }
@@ -463,13 +445,13 @@ impl ProjectPanel {
     }
 
     fn render_entry(
-        entry: ProjectEntry,
+        entry_id: ProjectEntryId,
         details: EntryDetails,
         theme: &theme::ProjectPanel,
         cx: &mut ViewContext<Self>,
     ) -> ElementBox {
         let is_dir = details.is_dir;
-        MouseEventHandler::new::<Self, _, _>(entry.entry_id, cx, |state, _| {
+        MouseEventHandler::new::<Self, _, _>(entry_id.to_usize(), cx, |state, _| {
             let style = match (details.is_selected, state.hovered) {
                 (false, false) => &theme.entry,
                 (false, true) => &theme.hovered_entry,
@@ -519,9 +501,9 @@ impl ProjectPanel {
         })
         .on_click(move |cx| {
             if is_dir {
-                cx.dispatch_action(ToggleExpanded(entry))
+                cx.dispatch_action(ToggleExpanded(entry_id))
             } else {
-                cx.dispatch_action(Open(entry))
+                cx.dispatch_action(Open(entry_id))
             }
         })
         .with_cursor_style(CursorStyle::PointingHand)
@@ -830,13 +812,7 @@ mod tests {
                     let worktree = worktree.read(cx);
                     if let Ok(relative_path) = path.strip_prefix(worktree.root_name()) {
                         let entry_id = worktree.entry_for_path(relative_path).unwrap().id;
-                        panel.toggle_expanded(
-                            &ToggleExpanded(ProjectEntry {
-                                worktree_id: worktree.id(),
-                                entry_id,
-                            }),
-                            cx,
-                        );
+                        panel.toggle_expanded(&ToggleExpanded(entry_id), cx);
                         return;
                     }
                 }

crates/search/src/project_search.rs 🔗

@@ -250,7 +250,7 @@ impl ItemView for ProjectSearchView {
         None
     }
 
-    fn project_entry(&self, _: &AppContext) -> Option<project::ProjectEntry> {
+    fn project_entry_id(&self, _: &AppContext) -> Option<project::ProjectEntryId> {
         None
     }
 

crates/workspace/src/pane.rs 🔗

@@ -10,7 +10,7 @@ use gpui::{
     AnyViewHandle, Entity, MutableAppContext, Quad, RenderContext, Task, View, ViewContext,
     ViewHandle, WeakViewHandle,
 };
-use project::{ProjectEntry, ProjectPath};
+use project::{ProjectEntryId, ProjectPath};
 use std::{
     any::{Any, TypeId},
     cell::RefCell,
@@ -97,7 +97,7 @@ pub enum Event {
 }
 
 pub struct Pane {
-    item_views: Vec<(Option<usize>, Box<dyn ItemViewHandle>)>,
+    item_views: Vec<(Option<ProjectEntryId>, Box<dyn ItemViewHandle>)>,
     active_item_index: usize,
     nav_history: Rc<RefCell<NavHistory>>,
     toolbars: HashMap<TypeId, Box<dyn ToolbarHandle>>,
@@ -323,9 +323,9 @@ impl Pane {
             .map(|(_, view)| view.clone())
     }
 
-    pub fn item_for_entry(&self, entry: ProjectEntry) -> Option<Box<dyn ItemViewHandle>> {
+    pub fn item_for_entry(&self, entry_id: ProjectEntryId) -> Option<Box<dyn ItemViewHandle>> {
         self.item_views.iter().find_map(|(id, view)| {
-            if *id == Some(entry.entry_id) {
+            if *id == Some(entry_id) {
                 Some(view.boxed_clone())
             } else {
                 None

crates/workspace/src/workspace.rs 🔗

@@ -26,7 +26,7 @@ use log::error;
 pub use pane::*;
 pub use pane_group::*;
 use postage::prelude::Stream;
-use project::{fs, Fs, Project, ProjectEntry, ProjectPath, Worktree};
+use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree};
 pub use settings::Settings;
 use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
 use status_bar::StatusBar;
@@ -138,7 +138,7 @@ pub trait ItemView: View {
     fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
     fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
-    fn project_entry(&self, cx: &AppContext) -> Option<ProjectEntry>;
+    fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
     fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>);
     fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
     where
@@ -191,7 +191,7 @@ pub trait ItemView: View {
 pub trait ItemViewHandle: 'static {
     fn tab_content(&self, style: &theme::Tab, cx: &AppContext) -> ElementBox;
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
-    fn project_entry_id(&self, cx: &AppContext) -> Option<usize>;
+    fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
     fn boxed_clone(&self) -> Box<dyn ItemViewHandle>;
     fn set_nav_history(&self, nav_history: Rc<RefCell<NavHistory>>, cx: &mut MutableAppContext);
     fn clone_on_split(&self, cx: &mut MutableAppContext) -> Option<Box<dyn ItemViewHandle>>;
@@ -239,8 +239,8 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
         self.read(cx).project_path(cx)
     }
 
-    fn project_entry_id(&self, cx: &AppContext) -> Option<usize> {
-        Some(self.read(cx).project_entry(cx)?.entry_id)
+    fn project_entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId> {
+        self.read(cx).project_entry_id(cx)
     }
 
     fn boxed_clone(&self) -> Box<dyn ItemViewHandle> {
@@ -850,7 +850,7 @@ impl Workspace {
 
     pub fn open_item_for_project_entry<T, F>(
         &mut self,
-        project_entry: ProjectEntry,
+        project_entry: ProjectEntryId,
         cx: &mut ViewContext<Self>,
         build_view: F,
     ) -> Box<dyn ItemViewHandle>