From 24cdfd24711a68828db3490d1a60a2aa4803d191 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 8 Apr 2021 16:42:58 -0600 Subject: [PATCH] Identify Worktree entries by their inode This will allow us to re-parent elements when re-scanning when the file system changes. --- zed/src/editor/buffer/mod.rs | 2 +- zed/src/editor/buffer_view.rs | 2 +- zed/src/file_finder.rs | 6 +- zed/src/workspace/pane.rs | 15 ++-- zed/src/workspace/workspace.rs | 8 +- zed/src/workspace/workspace_view.rs | 10 +-- zed/src/worktree/fuzzy.rs | 15 ++-- zed/src/worktree/worktree.rs | 126 ++++++++++++++-------------- 8 files changed, 95 insertions(+), 89 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index d69e1d96e2..642dc1fc7f 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -433,7 +433,7 @@ impl Buffer { self.file.as_ref().map(|file| file.path(app)) } - pub fn entry_id(&self) -> Option<(usize, usize)> { + pub fn entry_id(&self) -> Option<(usize, u64)> { self.file.as_ref().map(|file| file.entry_id()) } diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index f1855c9128..e366c72bf3 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -1225,7 +1225,7 @@ impl workspace::ItemView for BufferView { } } - fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)> { + fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)> { self.buffer.read(app).entry_id() } diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 654017556d..beb49d503e 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -44,7 +44,7 @@ pub fn init(app: &mut MutableAppContext) { } pub enum Event { - Selected(usize, usize), + Selected(usize, u64), Dismissed, } @@ -339,7 +339,7 @@ impl FileFinder { } } - fn select(&mut self, entry: &(usize, usize), ctx: &mut ViewContext) { + fn select(&mut self, entry: &(usize, u64), ctx: &mut ViewContext) { let (tree_id, entry_id) = *entry; ctx.emit(Event::Selected(tree_id, entry_id)); } @@ -347,7 +347,7 @@ impl FileFinder { fn spawn_search(&mut self, query: String, ctx: &mut ViewContext) { let worktrees = self.worktrees(ctx.as_ref()); let search_id = util::post_inc(&mut self.search_count); - let pool = ctx.app().scoped_pool().clone(); + let pool = ctx.as_ref().scoped_pool().clone(); let task = ctx.background_executor().spawn(async move { let matches = match_paths(worktrees.as_slice(), &query, false, false, 100, pool); (search_id, matches) diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index e0b0109ea4..bb52a6da5a 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -105,15 +105,12 @@ impl Pane { self.items.get(self.active_item).cloned() } - pub fn activate_entry( - &mut self, - entry_id: (usize, usize), - ctx: &mut ViewContext, - ) -> bool { - if let Some(index) = self.items.iter().position(|item| { - item.entry_id(ctx.as_ref()) - .map_or(false, |id| id == entry_id) - }) { + pub fn activate_entry(&mut self, entry_id: (usize, u64), ctx: &mut ViewContext) -> bool { + if let Some(index) = self + .items + .iter() + .position(|item| item.entry_id(ctx.as_ref()).map_or(false, |id| id == entry_id)) + { self.activate_item(index, ctx); true } else { diff --git a/zed/src/workspace/workspace.rs b/zed/src/workspace/workspace.rs index a3a2d79a0e..125f4b8bc4 100644 --- a/zed/src/workspace/workspace.rs +++ b/zed/src/workspace/workspace.rs @@ -76,7 +76,7 @@ enum OpenedItem { pub struct Workspace { replica_id: ReplicaId, worktrees: HashSet>, - items: HashMap<(usize, usize), OpenedItem>, + items: HashMap<(usize, u64), OpenedItem>, } impl Workspace { @@ -125,7 +125,7 @@ impl Workspace { pub fn open_entry( &mut self, - entry: (usize, usize), + entry: (usize, u64), ctx: &mut ModelContext<'_, Self>, ) -> anyhow::Result + Send>>> { if let Some(item) = self.items.get(&entry).cloned() { @@ -200,12 +200,12 @@ impl Entity for Workspace { #[cfg(test)] pub trait WorkspaceHandle { - fn file_entries(&self, app: &AppContext) -> Vec<(usize, usize)>; + fn file_entries(&self, app: &AppContext) -> Vec<(usize, u64)>; } #[cfg(test)] impl WorkspaceHandle for ModelHandle { - fn file_entries(&self, app: &AppContext) -> Vec<(usize, usize)> { + fn file_entries(&self, app: &AppContext) -> Vec<(usize, u64)> { self.read(app) .worktrees() .iter() diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 23612116c3..ac3a7b4307 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -19,7 +19,7 @@ pub fn init(app: &mut MutableAppContext) { pub trait ItemView: View { fn title(&self, app: &AppContext) -> String; - fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>; + fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)>; fn clone_on_split(&self, _: &mut ViewContext) -> Option where Self: Sized, @@ -42,7 +42,7 @@ pub trait ItemView: View { pub trait ItemViewHandle: Send + Sync { fn title(&self, app: &AppContext) -> String; - fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>; + fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)>; fn boxed_clone(&self) -> Box; fn clone_on_split(&self, app: &mut MutableAppContext) -> Option>; fn set_parent_pane(&self, pane: &ViewHandle, app: &mut MutableAppContext); @@ -57,7 +57,7 @@ impl ItemViewHandle for ViewHandle { self.read(app).title(app) } - fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)> { + fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)> { self.read(app).entry_id(app) } @@ -124,7 +124,7 @@ pub struct WorkspaceView { center: PaneGroup, panes: Vec>, active_pane: ViewHandle, - loading_entries: HashSet<(usize, usize)>, + loading_entries: HashSet<(usize, u64)>, } impl WorkspaceView { @@ -189,7 +189,7 @@ impl WorkspaceView { } } - pub fn open_entry(&mut self, entry: (usize, usize), ctx: &mut ViewContext) { + pub fn open_entry(&mut self, entry: (usize, u64), ctx: &mut ViewContext) { if self.loading_entries.contains(&entry) { return; } diff --git a/zed/src/worktree/fuzzy.rs b/zed/src/worktree/fuzzy.rs index c4a3d451ed..8bdb7d7eea 100644 --- a/zed/src/worktree/fuzzy.rs +++ b/zed/src/worktree/fuzzy.rs @@ -12,7 +12,7 @@ const ADDITIONAL_DISTANCE_PENALTY: f64 = 0.05; const MIN_DISTANCE_PENALTY: f64 = 0.2; pub struct PathEntry { - pub entry_id: usize, + pub ino: u64, pub path_chars: CharBag, pub path: Vec, pub lowercase_path: Vec, @@ -24,7 +24,7 @@ pub struct PathMatch { pub score: f64, pub positions: Vec, pub tree_id: usize, - pub entry_id: usize, + pub entry_id: u64, pub skipped_prefix_len: usize, } @@ -191,7 +191,7 @@ fn match_single_tree_paths( if score > 0.0 { results.push(Reverse(PathMatch { tree_id, - entry_id: path_entry.entry_id, + entry_id: path_entry.ino, score, positions: match_positions.clone(), skipped_prefix_len, @@ -453,7 +453,7 @@ mod tests { let path_chars = CharBag::from(&lowercase_path[..]); let path = path.chars().collect(); path_entries.push(PathEntry { - entry_id: i, + ino: i as u64, path_chars, path, lowercase_path, @@ -490,7 +490,12 @@ mod tests { results .into_iter() .rev() - .map(|result| (paths[result.0.entry_id].clone(), result.0.positions)) + .map(|result| { + ( + paths[result.0.entry_id as usize].clone(), + result.0.positions, + ) + }) .collect() } } diff --git a/zed/src/worktree/worktree.rs b/zed/src/worktree/worktree.rs index a00b0d55c5..b7c0765693 100644 --- a/zed/src/worktree/worktree.rs +++ b/zed/src/worktree/worktree.rs @@ -33,15 +33,15 @@ pub struct Worktree(Arc>); struct WorktreeState { id: usize, path: PathBuf, - root_ino: Option, - entries: HashMap, + root_ino: Option, + entries: HashMap, file_paths: Vec, - histories: HashMap, + histories: HashMap, scanning: bool, } struct DirToScan { - id: usize, + ino: u64, path: PathBuf, relative_path: PathBuf, ignore: Option, @@ -102,14 +102,13 @@ impl Worktree { } let is_ignored = ignore.matched(&path, metadata.is_dir()).is_ignore(); - self.0.write().root_ino = Some(0); if metadata.file_type().is_dir() { let is_ignored = is_ignored || name == ".git"; - let id = self.insert_dir(None, name, ino, is_symlink, is_ignored); + self.insert_dir(None, name, ino, is_symlink, is_ignored); let (tx, rx) = channel::unbounded(); tx.send(Ok(DirToScan { - id, + ino, path, relative_path, ignore: Some(ignore), @@ -131,6 +130,7 @@ impl Worktree { } else { self.insert_file(None, name, ino, is_symlink, is_ignored, relative_path); } + self.0.write().root_ino = Some(ino); Ok(()) } @@ -159,12 +159,12 @@ impl Worktree { } } - let id = self.insert_dir(Some(to_scan.id), name, ino, is_symlink, is_ignored); - new_children.push(id); + self.insert_dir(Some(to_scan.ino), name, ino, is_symlink, is_ignored); + new_children.push(ino); let dirs_to_scan = to_scan.dirs_to_scan.clone(); let _ = to_scan.dirs_to_scan.send(Ok(DirToScan { - id, + ino, path, relative_path, ignore, @@ -175,18 +175,19 @@ impl Worktree { i.matched(to_scan.path.join(&name), false).is_ignore() }); - new_children.push(self.insert_file( - Some(to_scan.id), + self.insert_file( + Some(to_scan.ino), name, ino, is_symlink, is_ignored, relative_path, - )); + ); + new_children.push(ino); }; } - if let Some(Entry::Dir { children, .. }) = &mut self.0.write().entries.get_mut(&to_scan.id) + if let Some(Entry::Dir { children, .. }) = &mut self.0.write().entries.get_mut(&to_scan.ino) { *children = new_children.clone(); } @@ -196,16 +197,15 @@ impl Worktree { fn insert_dir( &self, - parent: Option, + parent: Option, name: OsString, ino: u64, is_symlink: bool, is_ignored: bool, - ) -> usize { + ) { let entries = &mut self.0.write().entries; - let dir_id = entries.len(); entries.insert( - dir_id, + ino, Entry::Dir { parent, name, @@ -215,27 +215,25 @@ impl Worktree { children: Vec::new(), }, ); - dir_id } fn insert_file( &self, - parent: Option, + parent: Option, name: OsString, ino: u64, is_symlink: bool, is_ignored: bool, path: PathBuf, - ) -> usize { + ) { let path = path.to_string_lossy(); let lowercase_path = path.to_lowercase().chars().collect::>(); let path = path.chars().collect::>(); let path_chars = CharBag::from(&path[..]); let mut state = self.0.write(); - let entry_id = state.entries.len(); state.entries.insert( - entry_id, + ino, Entry::File { parent, name, @@ -245,25 +243,23 @@ impl Worktree { }, ); state.file_paths.push(PathEntry { - entry_id, + ino, path_chars, path, lowercase_path, is_ignored, }); - entry_id } - pub fn entry_path(&self, mut entry_id: usize) -> Result { + pub fn entry_path(&self, mut entry_id: u64) -> Result { let state = self.0.read(); - if entry_id >= state.entries.len() { - return Err(anyhow!("Entry does not exist in tree")); - } - let mut entries = Vec::new(); loop { - let entry = &state.entries[&entry_id]; + let entry = state + .entries + .get(&entry_id) + .ok_or_else(|| anyhow!("entry does not exist in worktree"))?; entries.push(entry); if let Some(parent_id) = entry.parent() { entry_id = parent_id; @@ -279,13 +275,13 @@ impl Worktree { Ok(path) } - pub fn abs_entry_path(&self, entry_id: usize) -> Result { + pub fn abs_entry_path(&self, entry_id: u64) -> Result { let mut path = self.0.read().path.clone(); path.pop(); Ok(path.join(self.entry_path(entry_id)?)) } - fn fmt_entry(&self, f: &mut fmt::Formatter<'_>, entry_id: usize, indent: usize) -> fmt::Result { + fn fmt_entry(&self, f: &mut fmt::Formatter<'_>, entry_id: u64, indent: usize) -> fmt::Result { match &self.0.read().entries[&entry_id] { Entry::Dir { name, children, .. } => { write!( @@ -333,6 +329,10 @@ impl Worktree { } } + pub fn has_entry(&self, entry_id: u64) -> bool { + self.0.read().entries.contains_key(&entry_id) + } + pub fn entry_count(&self) -> usize { self.0.read().entries.len() } @@ -341,7 +341,7 @@ impl Worktree { self.0.read().file_paths.len() } - pub fn load_history(&self, entry_id: usize) -> impl Future> { + pub fn load_history(&self, entry_id: u64) -> impl Future> { let tree = self.clone(); async move { @@ -360,12 +360,7 @@ impl Worktree { } } - pub fn save<'a>( - &self, - entry_id: usize, - content: Snapshot, - ctx: &AppContext, - ) -> Task> { + pub fn save<'a>(&self, entry_id: u64, content: Snapshot, ctx: &AppContext) -> Task> { let path = self.abs_entry_path(entry_id); ctx.background_executor().spawn(async move { let buffer_size = content.text_summary().bytes.min(10 * 1024); @@ -420,34 +415,34 @@ impl WorktreeState { } pub trait WorktreeHandle { - fn file(&self, entry_id: usize, app: &AppContext) -> Result; + fn file(&self, entry_id: u64, app: &AppContext) -> Result; } impl WorktreeHandle for ModelHandle { - fn file(&self, entry_id: usize, app: &AppContext) -> Result { - if entry_id >= self.read(app).entry_count() { - return Err(anyhow!("Entry does not exist in tree")); + fn file(&self, entry_id: u64, app: &AppContext) -> Result { + if self.read(app).has_entry(entry_id) { + Err(anyhow!("entry does not exist in tree")) + } else { + Ok(FileHandle { + worktree: self.clone(), + entry_id, + }) } - - Ok(FileHandle { - worktree: self.clone(), - entry_id, - }) } } #[derive(Clone, Debug)] pub enum Entry { Dir { - parent: Option, + parent: Option, name: OsString, ino: u64, is_symlink: bool, is_ignored: bool, - children: Vec, + children: Vec, }, File { - parent: Option, + parent: Option, name: OsString, ino: u64, is_symlink: bool, @@ -456,12 +451,18 @@ pub enum Entry { } impl Entry { - fn parent(&self) -> Option { + fn parent(&self) -> Option { match self { Entry::Dir { parent, .. } | Entry::File { parent, .. } => *parent, } } + fn ino(&self) -> u64 { + match self { + Entry::Dir { ino, .. } | Entry::File { ino, .. } => *ino, + } + } + fn name(&self) -> &OsStr { match self { Entry::Dir { name, .. } | Entry::File { name, .. } => name, @@ -472,7 +473,7 @@ impl Entry { #[derive(Clone)] pub struct FileHandle { worktree: ModelHandle, - entry_id: usize, + entry_id: u64, } impl FileHandle { @@ -489,13 +490,13 @@ impl FileHandle { worktree.save(self.entry_id, content, ctx) } - pub fn entry_id(&self) -> (usize, usize) { + pub fn entry_id(&self) -> (usize, u64) { (self.worktree.id(), self.entry_id) } } struct IterStackEntry { - entry_id: usize, + entry_id: u64, child_idx: usize, } @@ -516,18 +517,21 @@ impl Iterator for Iter { return if let Some(entry) = state.root_entry().cloned() { self.stack.push(IterStackEntry { - entry_id: 0, + entry_id: entry.ino(), child_idx: 0, }); - Some(Traversal::Push { entry_id: 0, entry }) + Some(Traversal::Push { + entry_id: entry.ino(), + entry, + }) } else { None }; } while let Some(parent) = self.stack.last_mut() { - if let Entry::Dir { children, .. } = &state.entries[&parent.entry_id] { + if let Some(Entry::Dir { children, .. }) = &state.entries.get(&parent.entry_id) { if parent.child_idx < children.len() { let child_id = children[post_inc(&mut parent.child_idx)]; @@ -558,7 +562,7 @@ impl Iterator for Iter { #[derive(Debug)] pub enum Traversal { - Push { entry_id: usize, entry: Entry }, + Push { entry_id: u64, entry: Entry }, Pop, } @@ -568,7 +572,7 @@ pub struct FilesIter { } pub struct FilesIterItem { - pub entry_id: usize, + pub entry_id: u64, pub path: PathBuf, }