diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 14553c911957f543269268c3906afd8e7d810006..19d2af917bb120548c98570a00e49a72c161f9e6 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -18,7 +18,7 @@ use crate::{ worktree::FileHandle, }; use anyhow::{anyhow, Result}; -use gpui::{AppContext, Entity, ModelContext}; +use gpui::{Entity, ModelContext}; use lazy_static::lazy_static; use rand::prelude::*; use std::{ @@ -26,7 +26,7 @@ use std::{ hash::BuildHasher, iter::{self, Iterator}, ops::{AddAssign, Range}, - path::PathBuf, + path::Path, str, sync::Arc, time::{Duration, Instant}, @@ -429,11 +429,11 @@ impl Buffer { } } - pub fn path(&self, app: &AppContext) -> Option { - self.file.as_ref().map(|file| file.path(app)) + pub fn path(&self) -> Option<&Arc> { + self.file.as_ref().map(|file| file.path()) } - pub fn entry_id(&self) -> Option<(usize, u64)> { + pub fn entry_id(&self) -> Option<(usize, Arc)> { 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 63a2f278fa86c444e17c5b782381c007ed51fb86..b3c2eb04967bb0cea97fb90f29259f2936c8d848 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -20,6 +20,7 @@ use std::{ fmt::Write, iter::FromIterator, ops::Range, + path::Path, sync::Arc, time::Duration, }; @@ -1375,7 +1376,7 @@ impl workspace::ItemView for BufferView { } fn title(&self, app: &AppContext) -> std::string::String { - if let Some(path) = self.buffer.read(app).path(app) { + if let Some(path) = self.buffer.read(app).path() { path.file_name() .expect("buffer's path is always to a file") .to_string_lossy() @@ -1385,7 +1386,7 @@ impl workspace::ItemView for BufferView { } } - fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)> { + fn entry_id(&self, app: &AppContext) -> Option<(usize, Arc)> { self.buffer.read(app).entry_id() } diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index d959cae31234a43d6b48a67e895f71018a757fc8..cfddd00a840f5956be77708a60786253cca274e9 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -14,7 +14,7 @@ use gpui::{ AppContext, Axis, Border, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, WeakViewHandle, }; -use std::{cmp, path::Path}; +use std::{cmp, path::Path, sync::Arc}; pub struct FileFinder { handle: WeakViewHandle, @@ -44,7 +44,7 @@ pub fn init(app: &mut MutableAppContext) { } pub enum Event { - Selected(usize, u64), + Selected(usize, Arc), Dismissed, } @@ -137,18 +137,18 @@ impl FileFinder { app: &AppContext, ) -> Option { let tree_id = path_match.tree_id; - let entry_id = path_match.entry_id; self.worktree(tree_id, app).map(|_| { - let path = &path_match.path; - let file_name = Path::new(path) + let path = path_match.path.clone(); + let path_string = &path_match.path_string; + let file_name = Path::new(&path_string) .file_name() .unwrap_or_default() .to_string_lossy() .to_string(); let path_positions = path_match.positions.clone(); - let file_name_start = path.chars().count() - file_name.chars().count(); + let file_name_start = path_string.chars().count() - file_name.chars().count(); let mut file_name_positions = Vec::new(); file_name_positions.extend(path_positions.iter().filter_map(|pos| { if pos >= &file_name_start { @@ -191,7 +191,7 @@ impl FileFinder { ) .with_child( Label::new( - path.into(), + path_string.into(), settings.ui_font_family, settings.ui_font_size, ) @@ -217,7 +217,7 @@ impl FileFinder { EventHandler::new(container.boxed()) .on_mouse_down(move |ctx| { - ctx.dispatch_action("file_finder:select", (tree_id, entry_id)); + ctx.dispatch_action("file_finder:select", (tree_id, path.clone())); true }) .named("match") @@ -245,8 +245,8 @@ impl FileFinder { ctx: &mut ViewContext, ) { match event { - Event::Selected(tree_id, entry_id) => { - workspace_view.open_entry((*tree_id, *entry_id), ctx); + Event::Selected(tree_id, path) => { + workspace_view.open_entry((*tree_id, path.clone()), ctx); workspace_view.dismiss_modal(ctx); } Event::Dismissed => { @@ -329,13 +329,12 @@ impl FileFinder { fn confirm(&mut self, _: &(), ctx: &mut ViewContext) { if let Some(m) = self.matches.get(self.selected) { - ctx.emit(Event::Selected(m.tree_id, m.entry_id)); + ctx.emit(Event::Selected(m.tree_id, m.path.clone())); } } - fn select(&mut self, entry: &(usize, u64), ctx: &mut ViewContext) { - let (tree_id, entry_id) = *entry; - ctx.emit(Event::Selected(tree_id, entry_id)); + fn select(&mut self, (tree_id, path): &(usize, Arc), ctx: &mut ViewContext) { + ctx.emit(Event::Selected(*tree_id, path.clone())); } fn spawn_search(&mut self, query: String, ctx: &mut ViewContext) { diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index ea3226715ae1ba1463f3dcd260febbc13d286383..076373cf88fd85098c9e60f0201f56c6add9c769 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -7,7 +7,7 @@ use gpui::{ keymap::Binding, AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext, }; -use std::cmp; +use std::{cmp, path::Path, sync::Arc}; pub fn init(app: &mut MutableAppContext) { app.add_action( @@ -105,7 +105,11 @@ impl Pane { self.items.get(self.active_item).cloned() } - pub fn activate_entry(&mut self, entry_id: (usize, u64), ctx: &mut ViewContext) -> bool { + pub fn activate_entry( + &mut self, + entry_id: (usize, Arc), + 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) diff --git a/zed/src/workspace/workspace.rs b/zed/src/workspace/workspace.rs index 50bc959404ac00afeb156de7b61f6dc9cbe44fe0..eceaf45578ea3ac74e5dcd8ebc08de37acb4ae51 100644 --- a/zed/src/workspace/workspace.rs +++ b/zed/src/workspace/workspace.rs @@ -138,10 +138,22 @@ impl Workspace { pub fn open_entry( &mut self, - entry: (usize, u64), + (worktree_id, path): (usize, Arc), ctx: &mut ModelContext<'_, Self>, ) -> anyhow::Result + Send>>> { - if let Some(item) = self.items.get(&entry).cloned() { + let worktree = self + .worktrees + .get(&worktree_id) + .cloned() + .ok_or_else(|| anyhow!("worktree {} does not exist", worktree_id,))?; + + let inode = worktree + .read(ctx) + .inode_for_path(&path) + .ok_or_else(|| anyhow!("path {:?} does not exist", path))?; + + let item_key = (worktree_id, inode); + if let Some(item) = self.items.get(&item_key).cloned() { return Ok(async move { match item { OpenedItem::Loaded(handle) => { @@ -159,25 +171,20 @@ impl Workspace { .boxed()); } - let worktree = self - .worktrees - .get(&entry.0) - .cloned() - .ok_or(anyhow!("worktree {} does not exist", entry.0,))?; - let replica_id = self.replica_id; - let file = worktree.file(entry.1, ctx.as_ref())?; + let file = worktree.file(path.clone(), ctx.as_ref())?; let history = file.load_history(ctx.as_ref()); let buffer = async move { Ok(Buffer::from_history(replica_id, file, history.await?)) }; let (mut tx, rx) = watch::channel(None); - self.items.insert(entry, OpenedItem::Loading(rx)); + self.items.insert(item_key, OpenedItem::Loading(rx)); ctx.spawn( buffer, move |me, buffer: anyhow::Result, ctx| match buffer { Ok(buffer) => { let handle = Box::new(ctx.add_model(|_| buffer)) as Box; - me.items.insert(entry, OpenedItem::Loaded(handle.clone())); + me.items + .insert(item_key, OpenedItem::Loaded(handle.clone())); ctx.spawn( async move { tx.update(|value| *value = Some(Ok(handle))).await; @@ -199,7 +206,7 @@ impl Workspace { ) .detach(); - self.open_entry(entry, ctx) + self.open_entry((worktree_id, path), ctx) } fn on_worktree_updated(&mut self, _: ModelHandle, ctx: &mut ModelContext) { @@ -213,18 +220,20 @@ impl Entity for Workspace { #[cfg(test)] pub trait WorkspaceHandle { - fn file_entries(&self, app: &AppContext) -> Vec<(usize, u64)>; + fn file_entries(&self, app: &AppContext) -> Vec<(usize, Arc)>; } #[cfg(test)] impl WorkspaceHandle for ModelHandle { - fn file_entries(&self, app: &AppContext) -> Vec<(usize, u64)> { + fn file_entries(&self, app: &AppContext) -> Vec<(usize, Arc)> { self.read(app) .worktrees() .iter() .flat_map(|tree| { let tree_id = tree.id(); - tree.read(app).files(0).map(move |f| (tree_id, f.inode())) + tree.read(app) + .files(0) + .map(move |f| (tree_id, f.path().clone())) }) .collect::>() } @@ -253,14 +262,14 @@ mod tests { // Get the first file entry. let tree = app.read(|ctx| workspace.read(ctx).worktrees.iter().next().unwrap().clone()); - let file_inode = app.read(|ctx| tree.read(ctx).files(0).next().unwrap().inode()); - let entry = (tree.id(), file_inode); + let path = app.read(|ctx| tree.read(ctx).files(0).next().unwrap().path().clone()); + let entry = (tree.id(), path); // Open the same entry twice before it finishes loading. let (future_1, future_2) = workspace.update(&mut app, |w, app| { ( - w.open_entry(entry, app).unwrap(), - w.open_entry(entry, app).unwrap(), + w.open_entry(entry.clone(), app).unwrap(), + w.open_entry(entry.clone(), app).unwrap(), ) }); diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 205806490ca336adcad7283944fc8358e495df10..f8ca8822b2cccb8fe5c81c300fbcf86f0f8e00ee 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -6,7 +6,11 @@ use gpui::{ ClipboardItem, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, }; use log::error; -use std::{collections::HashSet, path::PathBuf}; +use std::{ + collections::HashSet, + path::{Path, PathBuf}, + sync::Arc, +}; pub fn init(app: &mut MutableAppContext) { app.add_action("workspace:save", WorkspaceView::save_active_item); @@ -19,7 +23,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, u64)>; + fn entry_id(&self, app: &AppContext) -> Option<(usize, Arc)>; fn clone_on_split(&self, _: &mut ViewContext) -> Option where Self: Sized, @@ -42,7 +46,7 @@ pub trait ItemView: View { pub trait ItemViewHandle: Send + Sync { fn title(&self, app: &AppContext) -> String; - fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)>; + fn entry_id(&self, app: &AppContext) -> Option<(usize, Arc)>; 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 +61,7 @@ impl ItemViewHandle for ViewHandle { self.read(app).title(app) } - fn entry_id(&self, app: &AppContext) -> Option<(usize, u64)> { + fn entry_id(&self, app: &AppContext) -> Option<(usize, Arc)> { self.read(app).entry_id(app) } @@ -124,7 +128,7 @@ pub struct WorkspaceView { center: PaneGroup, panes: Vec>, active_pane: ViewHandle, - loading_entries: HashSet<(usize, u64)>, + loading_entries: HashSet<(usize, Arc)>, } impl WorkspaceView { @@ -189,24 +193,23 @@ impl WorkspaceView { } } - pub fn open_entry(&mut self, entry: (usize, u64), ctx: &mut ViewContext) { + pub fn open_entry(&mut self, entry: (usize, Arc), ctx: &mut ViewContext) { if self.loading_entries.contains(&entry) { return; } if self .active_pane() - .update(ctx, |pane, ctx| pane.activate_entry(entry, ctx)) + .update(ctx, |pane, ctx| pane.activate_entry(entry.clone(), ctx)) { return; } - self.loading_entries.insert(entry); + self.loading_entries.insert(entry.clone()); - match self - .workspace - .update(ctx, |workspace, ctx| workspace.open_entry(entry, ctx)) - { + match self.workspace.update(ctx, |workspace, ctx| { + workspace.open_entry(entry.clone(), ctx) + }) { Err(error) => error!("{}", error), Ok(item) => { let settings = self.settings.clone(); @@ -396,32 +399,35 @@ mod tests { app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) .await; let entries = app.read(|ctx| workspace.file_entries(ctx)); - let file1 = entries[0]; - let file2 = entries[1]; - let file3 = entries[2]; + let file1 = entries[0].clone(); + let file2 = entries[1].clone(); + let file3 = entries[2].clone(); let (_, workspace_view) = app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx)); let pane = app.read(|ctx| workspace_view.read(ctx).active_pane().clone()); // Open the first entry - workspace_view.update(&mut app, |w, ctx| w.open_entry(file1, ctx)); + workspace_view.update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)); pane.condition(&app, |pane, _| pane.items().len() == 1) .await; // Open the second entry - workspace_view.update(&mut app, |w, ctx| w.open_entry(file2, ctx)); + workspace_view.update(&mut app, |w, ctx| w.open_entry(file2.clone(), ctx)); pane.condition(&app, |pane, _| pane.items().len() == 2) .await; app.read(|ctx| { let pane = pane.read(ctx); - assert_eq!(pane.active_item().unwrap().entry_id(ctx), Some(file2)); + assert_eq!( + pane.active_item().unwrap().entry_id(ctx), + Some(file2.clone()) + ); }); // Open the first entry again - workspace_view.update(&mut app, |w, ctx| w.open_entry(file1, ctx)); + workspace_view.update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)); pane.condition(&app, move |pane, ctx| { - pane.active_item().unwrap().entry_id(ctx) == Some(file1) + pane.active_item().unwrap().entry_id(ctx) == Some(file1.clone()) }) .await; app.read(|ctx| { @@ -430,8 +436,8 @@ mod tests { // Open the third entry twice concurrently workspace_view.update(&mut app, |w, ctx| { - w.open_entry(file3, ctx); - w.open_entry(file3, ctx); + w.open_entry(file3.clone(), ctx); + w.open_entry(file3.clone(), ctx); }); pane.condition(&app, |pane, _| pane.items().len() == 3) .await; @@ -456,18 +462,21 @@ mod tests { app.read(|ctx| workspace.read(ctx).worktree_scans_complete(ctx)) .await; let entries = app.read(|ctx| workspace.file_entries(ctx)); - let file1 = entries[0]; + let file1 = entries[0].clone(); let (window_id, workspace_view) = app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx)); let pane_1 = app.read(|ctx| workspace_view.read(ctx).active_pane().clone()); - workspace_view.update(&mut app, |w, ctx| w.open_entry(file1, ctx)); - pane_1 - .condition(&app, move |pane, ctx| { - pane.active_item().and_then(|i| i.entry_id(ctx)) == Some(file1) - }) - .await; + workspace_view.update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)); + { + let file1 = file1.clone(); + pane_1 + .condition(&app, move |pane, ctx| { + pane.active_item().and_then(|i| i.entry_id(ctx)) == Some(file1.clone()) + }) + .await; + } app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ()); app.update(|ctx| { @@ -475,7 +484,7 @@ mod tests { assert_ne!(pane_1, pane_2); let pane2_item = pane_2.read(ctx).active_item().unwrap(); - assert_eq!(pane2_item.entry_id(ctx.as_ref()), Some(file1)); + assert_eq!(pane2_item.entry_id(ctx.as_ref()), Some(file1.clone())); ctx.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ()); let workspace_view = workspace_view.read(ctx); diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index a402d6f41c6c8d8cbf357ea37b1a2f6e7cabc612..0f5b8fd0efb8ce48f70e6368ba78d5d55cc85139 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -54,7 +54,7 @@ pub struct Worktree { #[derive(Clone)] pub struct FileHandle { worktree: ModelHandle, - inode: u64, + path: Arc, } impl Worktree { @@ -152,25 +152,14 @@ impl Worktree { path.starts_with(&self.snapshot.path) } - pub fn has_inode(&self, inode: u64) -> bool { - todo!() - // self.snapshot.entries.get(&inode).is_some() - } - - pub fn abs_path_for_inode(&self, ino: u64) -> Result { - let mut result = self.snapshot.path.to_path_buf(); - result.push(self.path_for_inode(ino, false)?); - Ok(result) - } - pub fn load_history( &self, - ino: u64, + relative_path: &Path, ctx: &AppContext, ) -> impl Future> { - let path = self.abs_path_for_inode(ino); + let path = self.snapshot.path.join(relative_path); ctx.background_executor().spawn(async move { - let mut file = std::fs::File::open(&path?)?; + let mut file = std::fs::File::open(&path)?; let mut base_text = String::new(); file.read_to_string(&mut base_text)?; Ok(History::new(Arc::from(base_text))) @@ -179,14 +168,14 @@ impl Worktree { pub fn save<'a>( &self, - ino: u64, + relative_path: &Path, content: BufferSnapshot, ctx: &AppContext, ) -> Task> { - let path = self.abs_path_for_inode(ino); + let path = self.snapshot.path.join(relative_path); ctx.background_executor().spawn(async move { let buffer_size = content.text_summary().bytes.min(10 * 1024); - let file = std::fs::File::create(&path?)?; + let file = std::fs::File::create(&path)?; let mut writer = std::io::BufWriter::with_capacity(buffer_size, file); for chunk in content.fragments() { writer.write(chunk.as_bytes())?; @@ -258,7 +247,7 @@ impl Snapshot { } } - fn inode_for_path(&self, path: impl AsRef) -> Option { + pub fn inode_for_path(&self, path: impl AsRef) -> Option { self.entry_for_path(path.as_ref()).map(|e| e.inode()) } @@ -288,10 +277,6 @@ impl Snapshot { } } - pub fn path_for_inode(&self, mut inode: u64, include_root: bool) -> Result { - todo!("this method should go away") - } - fn insert_entry(&mut self, entry: Entry) { if !entry.is_dir() && entry.path().file_name() == Some(&GITIGNORE) { self.insert_ignore_file(entry.path()); @@ -362,24 +347,21 @@ impl fmt::Debug for Snapshot { } impl FileHandle { - pub fn path(&self, ctx: &AppContext) -> PathBuf { - self.worktree - .read(ctx) - .path_for_inode(self.inode, false) - .unwrap() + pub fn path(&self) -> &Arc { + &self.path } pub fn load_history(&self, ctx: &AppContext) -> impl Future> { - self.worktree.read(ctx).load_history(self.inode, ctx) + self.worktree.read(ctx).load_history(&self.path, ctx) } pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { let worktree = self.worktree.read(ctx); - worktree.save(self.inode, content, ctx) + worktree.save(&self.path, content, ctx) } - pub fn entry_id(&self) -> (usize, u64) { - (self.worktree.id(), self.inode) + pub fn entry_id(&self) -> (usize, Arc) { + (self.worktree.id(), self.path.clone()) } } @@ -402,13 +384,20 @@ pub enum Entry { } impl Entry { - fn path(&self) -> &Arc { + pub fn path(&self) -> &Arc { match self { Entry::Dir { path, .. } => path, Entry::File { path, .. } => path, } } + pub fn inode(&self) -> u64 { + match self { + Entry::Dir { inode, .. } => *inode, + Entry::File { inode, .. } => *inode, + } + } + fn is_ignored(&self) -> Option { match self { Entry::Dir { is_ignored, .. } => *is_ignored, @@ -423,13 +412,6 @@ impl Entry { } } - pub fn inode(&self) -> u64 { - match self { - Entry::Dir { inode, .. } => *inode, - Entry::File { inode, .. } => *inode, - } - } - fn is_dir(&self) -> bool { matches!(self, Entry::Dir { .. }) } @@ -683,7 +665,7 @@ impl BackgroundScanner { }); } else { self.snapshot.lock().insert_entry(Entry::File { - path_entry: PathEntry::new(inode, &relative_path), + path_entry: PathEntry::new(inode, relative_path.clone()), path: relative_path, inode, is_symlink, @@ -729,7 +711,7 @@ impl BackgroundScanner { }); } else { new_entries.push(Entry::File { - path_entry: PathEntry::new(child_inode, &child_relative_path), + path_entry: PathEntry::new(child_inode, child_relative_path.clone()), path: child_relative_path, inode: child_inode, is_symlink: child_is_symlink, @@ -956,11 +938,12 @@ impl BackgroundScanner { .is_symlink(); let relative_path_with_root = root_path .parent() - .map_or(path, |parent| path.strip_prefix(parent).unwrap()); + .map_or(path, |parent| path.strip_prefix(parent).unwrap()) + .into(); let entry = if metadata.file_type().is_dir() { Entry::Dir { - path: Arc::from(relative_path_with_root), + path: relative_path_with_root, inode, is_symlink, pending: true, @@ -968,8 +951,8 @@ impl BackgroundScanner { } } else { Entry::File { - path_entry: PathEntry::new(inode, relative_path_with_root), - path: Arc::from(relative_path_with_root), + path_entry: PathEntry::new(inode, relative_path_with_root.clone()), + path: relative_path_with_root, inode, is_symlink, is_ignored: None, @@ -987,19 +970,18 @@ struct ScanJob { } pub trait WorktreeHandle { - fn file(&self, entry_id: u64, app: &AppContext) -> Result; + fn file(&self, path: impl AsRef, app: &AppContext) -> Result; } impl WorktreeHandle for ModelHandle { - fn file(&self, inode: u64, app: &AppContext) -> Result { - if self.read(app).has_inode(inode) { - Ok(FileHandle { + fn file(&self, path: impl AsRef, app: &AppContext) -> Result { + self.read(app) + .entry_for_path(&path) + .map(|entry| FileHandle { worktree: self.clone(), - inode, + path: entry.path().clone(), }) - } else { - Err(anyhow!("entry does not exist in tree")) - } + .ok_or_else(|| anyhow!("path does not exist in tree")) } } @@ -1125,14 +1107,13 @@ mod tests { ctx.thread_pool().clone(), ) .iter() - .map(|result| tree.path_for_inode(result.entry_id, true)) - .collect::, _>>() - .unwrap(); + .map(|result| result.path.clone()) + .collect::>>(); assert_eq!( results, vec![ - PathBuf::from("root_link/banana/carrot/date"), - PathBuf::from("root_link/banana/carrot/endive"), + PathBuf::from("root_link/banana/carrot/date").into(), + PathBuf::from("root_link/banana/carrot/endive").into(), ] ); }) @@ -1152,25 +1133,15 @@ mod tests { let buffer = Buffer::new(1, "a line of text.\n".repeat(10 * 1024)); - let file_inode = app.read(|ctx| { - let tree = tree.read(ctx); - let inode = tree.files(0).next().unwrap().inode(); - assert_eq!( - tree.path_for_inode(inode, false) - .unwrap() - .file_name() - .unwrap(), - "file1" - ); - inode - }); - - tree.update(&mut app, |tree, ctx| { - smol::block_on(tree.save(file_inode, buffer.snapshot(), ctx.as_ref())).unwrap() + let path = tree.update(&mut app, |tree, ctx| { + let path = tree.files(0).next().unwrap().path().clone(); + assert_eq!(path.file_name().unwrap(), "file1"); + smol::block_on(tree.save(&path, buffer.snapshot(), ctx.as_ref())).unwrap(); + path }); let loaded_history = app - .read(|ctx| tree.read(ctx).load_history(file_inode, ctx)) + .read(|ctx| tree.read(ctx).load_history(&path, ctx)) .await .unwrap(); assert_eq!(loaded_history.base_text.as_ref(), buffer.text()); @@ -1196,15 +1167,16 @@ mod tests { app.read(|ctx| assert_eq!(tree.read(ctx).file_count(), 2)); let file2 = app.read(|ctx| { - let inode = tree.read(ctx).inode_for_path("b/c/file2").unwrap(); - let file2 = tree.file(inode, ctx).unwrap(); - assert_eq!(file2.path(ctx), Path::new("b/c/file2")); + let file2 = tree.file("b/c/file2", ctx).unwrap(); + assert_eq!(file2.path().as_ref(), Path::new("b/c/file2")); file2 }); std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap(); - tree.condition(&app, move |_, ctx| file2.path(ctx) == Path::new("d/file2")) - .await; + tree.condition(&app, move |_, _| { + file2.path().as_ref() == Path::new("d/file2") + }) + .await; }); } @@ -1513,7 +1485,7 @@ mod tests { )); if let Entry::File { path_entry, .. } = entry { assert_eq!( - String::from_iter(path_entry.path.iter()), + String::from_iter(path_entry.path_chars.iter()), entry.path().to_str().unwrap() ); } diff --git a/zed/src/worktree/fuzzy.rs b/zed/src/worktree/fuzzy.rs index d34d963fc7fa3021efdc57965e116a39a9e46265..5b1a61e8c07a911434e0e179a011f5ec0ef90e3f 100644 --- a/zed/src/worktree/fuzzy.rs +++ b/zed/src/worktree/fuzzy.rs @@ -14,20 +14,22 @@ const MIN_DISTANCE_PENALTY: f64 = 0.2; #[derive(Clone, Debug)] pub struct PathEntry { pub ino: u64, - pub path_chars: CharBag, - pub path: Arc<[char]>, + pub char_bag: CharBag, + pub path_chars: Arc<[char]>, + pub path: Arc, pub lowercase_path: Arc<[char]>, } impl PathEntry { - pub fn new(ino: u64, path: &Path) -> Self { - let path = path.to_string_lossy(); - let lowercase_path = path.to_lowercase().chars().collect::>().into(); - let path: Arc<[char]> = path.chars().collect::>().into(); - let path_chars = CharBag::from(path.as_ref()); + pub fn new(ino: u64, path: Arc) -> Self { + let path_str = path.to_string_lossy(); + let lowercase_path = path_str.to_lowercase().chars().collect::>().into(); + let path_chars: Arc<[char]> = path_str.chars().collect::>().into(); + let char_bag = CharBag::from(path_chars.as_ref()); Self { ino, + char_bag, path_chars, path, lowercase_path, @@ -39,9 +41,9 @@ impl PathEntry { pub struct PathMatch { pub score: f64, pub positions: Vec, - pub path: String, + pub path_string: String, pub tree_id: usize, - pub entry_id: u64, + pub path: Arc, } impl PartialEq for PathMatch { @@ -199,7 +201,7 @@ fn match_single_tree_paths<'a>( best_position_matrix: &mut Vec, ) { for path_entry in path_entries { - if !path_entry.path_chars.is_superset(query_chars) { + if !path_entry.char_bag.is_superset(query_chars) { continue; } @@ -212,7 +214,7 @@ fn match_single_tree_paths<'a>( continue; } - let matrix_len = query.len() * (path_entry.path.len() - skipped_prefix_len); + let matrix_len = query.len() * (path_entry.path_chars.len() - skipped_prefix_len); score_matrix.clear(); score_matrix.resize(matrix_len, None); best_position_matrix.clear(); @@ -221,7 +223,7 @@ fn match_single_tree_paths<'a>( let score = score_match( &query[..], &lowercase_query[..], - &path_entry.path, + &path_entry.path_chars, &path_entry.lowercase_path, skipped_prefix_len, smart_case, @@ -235,8 +237,12 @@ fn match_single_tree_paths<'a>( if score > 0.0 { results.push(Reverse(PathMatch { tree_id: snapshot.id, - entry_id: path_entry.ino, - path: path_entry.path.iter().skip(skipped_prefix_len).collect(), + path_string: path_entry + .path_chars + .iter() + .skip(skipped_prefix_len) + .collect(), + path: path_entry.path.clone(), score, positions: match_positions.clone(), })); @@ -496,12 +502,13 @@ mod tests { for (i, path) in paths.iter().enumerate() { let lowercase_path: Arc<[char]> = path.to_lowercase().chars().collect::>().into(); - let path_chars = CharBag::from(lowercase_path.as_ref()); - let path = path.chars().collect(); + let char_bag = CharBag::from(lowercase_path.as_ref()); + let path_chars = path.chars().collect(); path_entries.push(PathEntry { ino: i as u64, + char_bag, path_chars, - path, + path: Arc::from(PathBuf::from(path)), lowercase_path, }); } @@ -540,7 +547,11 @@ mod tests { .rev() .map(|result| { ( - paths[result.0.entry_id as usize].clone(), + paths + .iter() + .copied() + .find(|p| result.0.path.as_ref() == Path::new(p)) + .unwrap(), result.0.positions, ) })