WIP
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
c9d7249305
commit
dced9469f5
4 changed files with 113 additions and 80 deletions
|
@ -199,6 +199,9 @@ where
|
|||
}
|
||||
|
||||
pub fn next(&mut self) {
|
||||
if !self.did_seek {
|
||||
self.descend_to_first_item(self.tree, |_| true)
|
||||
}
|
||||
self.next_internal(|_| true)
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ impl Workspace {
|
|||
pub fn contains_path(&self, path: &Path, app: &AppContext) -> bool {
|
||||
self.worktrees
|
||||
.iter()
|
||||
.any(|worktree| worktree.read(app).contains_path(path))
|
||||
.any(|worktree| worktree.read(app).contains_abs_path(path))
|
||||
}
|
||||
|
||||
pub fn open_paths(&mut self, paths: &[PathBuf], ctx: &mut ModelContext<Self>) {
|
||||
|
@ -125,7 +125,7 @@ impl Workspace {
|
|||
|
||||
pub fn open_path<'a>(&'a mut self, path: PathBuf, ctx: &mut ModelContext<Self>) {
|
||||
for tree in self.worktrees.iter() {
|
||||
if tree.read(ctx).contains_path(&path) {
|
||||
if tree.read(ctx).contains_abs_path(&path) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,12 +64,12 @@ impl Worktree {
|
|||
let snapshot = Snapshot {
|
||||
id,
|
||||
scan_id: 0,
|
||||
path: path.into(),
|
||||
abs_path: path.into(),
|
||||
ignores: Default::default(),
|
||||
entries: Default::default(),
|
||||
};
|
||||
let (event_stream, event_stream_handle) =
|
||||
fsevent::EventStream::new(&[snapshot.path.as_ref()], Duration::from_millis(100));
|
||||
fsevent::EventStream::new(&[snapshot.abs_path.as_ref()], Duration::from_millis(100));
|
||||
|
||||
let background_snapshot = Arc::new(Mutex::new(snapshot.clone()));
|
||||
|
||||
|
@ -148,18 +148,18 @@ impl Worktree {
|
|||
self.snapshot.clone()
|
||||
}
|
||||
|
||||
pub fn contains_path(&self, path: &Path) -> bool {
|
||||
path.starts_with(&self.snapshot.path)
|
||||
pub fn contains_abs_path(&self, path: &Path) -> bool {
|
||||
path.starts_with(&self.snapshot.abs_path)
|
||||
}
|
||||
|
||||
pub fn load_history(
|
||||
&self,
|
||||
relative_path: &Path,
|
||||
path: &Path,
|
||||
ctx: &AppContext,
|
||||
) -> impl Future<Output = Result<History>> {
|
||||
let path = self.snapshot.path.join(relative_path);
|
||||
let abs_path = self.snapshot.abs_path.join(path);
|
||||
ctx.background_executor().spawn(async move {
|
||||
let mut file = std::fs::File::open(&path)?;
|
||||
let mut file = std::fs::File::open(&abs_path)?;
|
||||
let mut base_text = String::new();
|
||||
file.read_to_string(&mut base_text)?;
|
||||
Ok(History::new(Arc::from(base_text)))
|
||||
|
@ -168,14 +168,14 @@ impl Worktree {
|
|||
|
||||
pub fn save<'a>(
|
||||
&self,
|
||||
relative_path: &Path,
|
||||
path: &Path,
|
||||
content: BufferSnapshot,
|
||||
ctx: &AppContext,
|
||||
) -> Task<Result<()>> {
|
||||
let path = self.snapshot.path.join(relative_path);
|
||||
let abs_path = self.snapshot.abs_path.join(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(&abs_path)?;
|
||||
let mut writer = std::io::BufWriter::with_capacity(buffer_size, file);
|
||||
for chunk in content.fragments() {
|
||||
writer.write(chunk.as_bytes())?;
|
||||
|
@ -208,7 +208,7 @@ impl fmt::Debug for Worktree {
|
|||
pub struct Snapshot {
|
||||
id: usize,
|
||||
scan_id: usize,
|
||||
path: Arc<Path>,
|
||||
abs_path: Arc<Path>,
|
||||
ignores: BTreeMap<Arc<Path>, (Arc<Gitignore>, usize)>,
|
||||
entries: SumTree<Entry>,
|
||||
}
|
||||
|
@ -226,16 +226,23 @@ impl Snapshot {
|
|||
FileIter::all(self, start)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn paths(&self) -> impl Iterator<Item = &Arc<Path>> {
|
||||
let mut cursor = self.entries.cursor::<(), ()>();
|
||||
cursor.next();
|
||||
cursor.map(|entry| entry.path())
|
||||
}
|
||||
|
||||
pub fn visible_files(&self, start: usize) -> FileIter {
|
||||
FileIter::visible(self, start)
|
||||
}
|
||||
|
||||
pub fn root_entry(&self) -> Entry {
|
||||
self.entry_for_path(&self.path).unwrap()
|
||||
self.entry_for_path(&self.abs_path).unwrap()
|
||||
}
|
||||
|
||||
pub fn root_name(&self) -> Option<&OsStr> {
|
||||
self.path.file_name()
|
||||
self.abs_path.file_name()
|
||||
}
|
||||
|
||||
fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<Entry> {
|
||||
|
@ -252,6 +259,8 @@ impl Snapshot {
|
|||
}
|
||||
|
||||
fn is_path_ignored(&self, path: &Path) -> Result<bool> {
|
||||
dbg!(path);
|
||||
|
||||
let mut entry = self
|
||||
.entry_for_path(path)
|
||||
.ok_or_else(|| anyhow!("entry does not exist in worktree"))?;
|
||||
|
@ -263,6 +272,7 @@ impl Snapshot {
|
|||
entry.path().parent().and_then(|p| self.entry_for_path(p))
|
||||
{
|
||||
let parent_path = parent_entry.path();
|
||||
dbg!(parent_path);
|
||||
if let Some((ignore, _)) = self.ignores.get(parent_path) {
|
||||
let relative_path = path.strip_prefix(parent_path).unwrap();
|
||||
match ignore.matched_path_or_any_parents(relative_path, entry.is_dir()) {
|
||||
|
@ -322,8 +332,7 @@ impl Snapshot {
|
|||
}
|
||||
|
||||
fn insert_ignore_file(&mut self, path: &Path) {
|
||||
let root_path = self.path.parent().unwrap_or(Path::new(""));
|
||||
let (ignore, err) = Gitignore::new(root_path.join(path));
|
||||
let (ignore, err) = Gitignore::new(self.abs_path.join(path));
|
||||
if let Some(err) = err {
|
||||
log::error!("error in ignore file {:?} - {:?}", path, err);
|
||||
}
|
||||
|
@ -573,7 +582,7 @@ impl BackgroundScanner {
|
|||
}
|
||||
|
||||
fn update_other_mount_paths(&mut self) {
|
||||
let path = self.snapshot.lock().path.clone();
|
||||
let path = self.snapshot.lock().abs_path.clone();
|
||||
self.other_mount_paths.clear();
|
||||
self.other_mount_paths.extend(
|
||||
mounted_volume_paths()
|
||||
|
@ -582,8 +591,8 @@ impl BackgroundScanner {
|
|||
);
|
||||
}
|
||||
|
||||
fn path(&self) -> Arc<Path> {
|
||||
self.snapshot.lock().path.clone()
|
||||
fn abs_path(&self) -> Arc<Path> {
|
||||
self.snapshot.lock().abs_path.clone()
|
||||
}
|
||||
|
||||
fn snapshot(&self) -> Snapshot {
|
||||
|
@ -625,16 +634,15 @@ impl BackgroundScanner {
|
|||
fn scan_dirs(&self) -> io::Result<()> {
|
||||
self.snapshot.lock().scan_id += 1;
|
||||
|
||||
let path = self.path();
|
||||
let metadata = fs::metadata(&path)?;
|
||||
let path: Arc<Path> = Arc::from(Path::new(""));
|
||||
let abs_path = self.abs_path();
|
||||
let metadata = fs::metadata(&abs_path)?;
|
||||
let inode = metadata.ino();
|
||||
let is_symlink = fs::symlink_metadata(&path)?.file_type().is_symlink();
|
||||
let name: Arc<OsStr> = path.file_name().unwrap_or(OsStr::new("/")).into();
|
||||
let relative_path: Arc<Path> = Arc::from((*name).as_ref());
|
||||
let is_symlink = fs::symlink_metadata(&abs_path)?.file_type().is_symlink();
|
||||
|
||||
if metadata.file_type().is_dir() {
|
||||
let dir_entry = Entry::Dir {
|
||||
path: relative_path.clone(),
|
||||
path: path.clone(),
|
||||
inode,
|
||||
is_symlink,
|
||||
pending: true,
|
||||
|
@ -645,8 +653,8 @@ impl BackgroundScanner {
|
|||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
|
||||
tx.send(ScanJob {
|
||||
path: path.to_path_buf(),
|
||||
relative_path,
|
||||
abs_path: abs_path.to_path_buf(),
|
||||
path,
|
||||
scan_queue: tx.clone(),
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -657,7 +665,7 @@ impl BackgroundScanner {
|
|||
pool.execute(|| {
|
||||
while let Ok(job) = rx.recv() {
|
||||
if let Err(err) = self.scan_dir(&job) {
|
||||
log::error!("error scanning {:?}: {}", job.path, err);
|
||||
log::error!("error scanning {:?}: {}", job.abs_path, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -665,8 +673,8 @@ impl BackgroundScanner {
|
|||
});
|
||||
} else {
|
||||
self.snapshot.lock().insert_entry(Entry::File {
|
||||
path_entry: PathEntry::new(inode, relative_path.clone()),
|
||||
path: relative_path,
|
||||
path_entry: PathEntry::new(inode, path.clone()),
|
||||
path,
|
||||
inode,
|
||||
is_symlink,
|
||||
is_ignored: None,
|
||||
|
@ -682,37 +690,37 @@ impl BackgroundScanner {
|
|||
let mut new_entries = Vec::new();
|
||||
let mut new_jobs = Vec::new();
|
||||
|
||||
for child_entry in fs::read_dir(&job.path)? {
|
||||
for child_entry in fs::read_dir(&job.abs_path)? {
|
||||
let child_entry = child_entry?;
|
||||
let child_name: Arc<OsStr> = child_entry.file_name().into();
|
||||
let child_relative_path: Arc<Path> = job.relative_path.join(child_name.as_ref()).into();
|
||||
let child_name = child_entry.file_name();
|
||||
let child_abs_path = job.abs_path.join(&child_name);
|
||||
let child_path: Arc<Path> = job.path.join(&child_name).into();
|
||||
let child_metadata = child_entry.metadata()?;
|
||||
let child_inode = child_metadata.ino();
|
||||
let child_is_symlink = child_metadata.file_type().is_symlink();
|
||||
let child_path = job.path.join(child_name.as_ref());
|
||||
|
||||
// Disallow mount points outside the file system containing the root of this worktree
|
||||
if self.other_mount_paths.contains(&child_path) {
|
||||
if self.other_mount_paths.contains(&child_abs_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if child_metadata.is_dir() {
|
||||
new_entries.push(Entry::Dir {
|
||||
path: child_relative_path.clone(),
|
||||
path: child_path.clone(),
|
||||
inode: child_inode,
|
||||
is_symlink: child_is_symlink,
|
||||
pending: true,
|
||||
is_ignored: None,
|
||||
});
|
||||
new_jobs.push(ScanJob {
|
||||
abs_path: child_abs_path,
|
||||
path: child_path,
|
||||
relative_path: child_relative_path,
|
||||
scan_queue: job.scan_queue.clone(),
|
||||
});
|
||||
} else {
|
||||
new_entries.push(Entry::File {
|
||||
path_entry: PathEntry::new(child_inode, child_relative_path.clone()),
|
||||
path: child_relative_path,
|
||||
path_entry: PathEntry::new(child_inode, child_path.clone()),
|
||||
path: child_path,
|
||||
inode: child_inode,
|
||||
is_symlink: child_is_symlink,
|
||||
is_ignored: None,
|
||||
|
@ -722,7 +730,7 @@ impl BackgroundScanner {
|
|||
|
||||
self.snapshot
|
||||
.lock()
|
||||
.populate_dir(job.relative_path.clone(), new_entries);
|
||||
.populate_dir(job.path.clone(), new_entries);
|
||||
for new_job in new_jobs {
|
||||
job.scan_queue.send(new_job).unwrap();
|
||||
}
|
||||
|
@ -736,40 +744,44 @@ impl BackgroundScanner {
|
|||
let mut snapshot = self.snapshot();
|
||||
snapshot.scan_id += 1;
|
||||
|
||||
let root_path = if let Ok(path) = snapshot.path.canonicalize() {
|
||||
path
|
||||
let root_abs_path = if let Ok(abs_path) = snapshot.abs_path.canonicalize() {
|
||||
abs_path
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
events.sort_unstable_by(|a, b| a.path.cmp(&b.path));
|
||||
let mut paths = events.into_iter().map(|e| e.path).peekable();
|
||||
let mut abs_paths = events.into_iter().map(|e| e.path).peekable();
|
||||
let (scan_queue_tx, scan_queue_rx) = crossbeam_channel::unbounded();
|
||||
while let Some(path) = paths.next() {
|
||||
let relative_path =
|
||||
match path.strip_prefix(&root_path.parent().unwrap_or(Path::new(""))) {
|
||||
Ok(relative_path) => relative_path.to_path_buf(),
|
||||
|
||||
while let Some(abs_path) = abs_paths.next() {
|
||||
let path = match abs_path.strip_prefix(&root_abs_path) {
|
||||
Ok(path) => Arc::from(path.to_path_buf()),
|
||||
Err(_) => {
|
||||
log::error!("unexpected event {:?} for root path {:?}", path, root_path);
|
||||
log::error!(
|
||||
"unexpected event {:?} for root path {:?}",
|
||||
abs_path,
|
||||
root_abs_path
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
while paths.peek().map_or(false, |p| p.starts_with(&path)) {
|
||||
paths.next();
|
||||
while abs_paths.peek().map_or(false, |p| p.starts_with(&abs_path)) {
|
||||
abs_paths.next();
|
||||
}
|
||||
|
||||
snapshot.remove_path(&relative_path);
|
||||
snapshot.remove_path(&path);
|
||||
|
||||
match self.fs_entry_for_path(&root_path, &path) {
|
||||
match self.fs_entry_for_path(path.clone(), &abs_path) {
|
||||
Ok(Some(fs_entry)) => {
|
||||
let is_dir = fs_entry.is_dir();
|
||||
snapshot.insert_entry(fs_entry);
|
||||
if is_dir {
|
||||
scan_queue_tx
|
||||
.send(ScanJob {
|
||||
abs_path,
|
||||
path,
|
||||
relative_path: relative_path.into(),
|
||||
scan_queue: scan_queue_tx.clone(),
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -792,7 +804,7 @@ impl BackgroundScanner {
|
|||
pool.execute(|| {
|
||||
while let Ok(job) = scan_queue_rx.recv() {
|
||||
if let Err(err) = self.scan_dir(&job) {
|
||||
log::error!("error scanning {:?}: {}", job.path, err);
|
||||
log::error!("error scanning {:?}: {}", job.abs_path, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -919,8 +931,8 @@ impl BackgroundScanner {
|
|||
});
|
||||
}
|
||||
|
||||
fn fs_entry_for_path(&self, root_path: &Path, path: &Path) -> Result<Option<Entry>> {
|
||||
let metadata = match fs::metadata(&path) {
|
||||
fn fs_entry_for_path(&self, path: Arc<Path>, abs_path: &Path) -> Result<Option<Entry>> {
|
||||
let metadata = match fs::metadata(&abs_path) {
|
||||
Err(err) => {
|
||||
return match (err.kind(), err.raw_os_error()) {
|
||||
(io::ErrorKind::NotFound, _) => Ok(None),
|
||||
|
@ -930,20 +942,15 @@ impl BackgroundScanner {
|
|||
}
|
||||
Ok(metadata) => metadata,
|
||||
};
|
||||
|
||||
let inode = metadata.ino();
|
||||
let is_symlink = fs::symlink_metadata(&path)
|
||||
let is_symlink = fs::symlink_metadata(&abs_path)
|
||||
.context("failed to read symlink metadata")?
|
||||
.file_type()
|
||||
.is_symlink();
|
||||
let relative_path_with_root = root_path
|
||||
.parent()
|
||||
.map_or(path, |parent| path.strip_prefix(parent).unwrap())
|
||||
.into();
|
||||
|
||||
let entry = if metadata.file_type().is_dir() {
|
||||
Entry::Dir {
|
||||
path: relative_path_with_root,
|
||||
path,
|
||||
inode,
|
||||
is_symlink,
|
||||
pending: true,
|
||||
|
@ -951,8 +958,8 @@ impl BackgroundScanner {
|
|||
}
|
||||
} else {
|
||||
Entry::File {
|
||||
path_entry: PathEntry::new(inode, relative_path_with_root.clone()),
|
||||
path: relative_path_with_root,
|
||||
path_entry: PathEntry::new(inode, path.clone()),
|
||||
path,
|
||||
inode,
|
||||
is_symlink,
|
||||
is_ignored: None,
|
||||
|
@ -964,8 +971,8 @@ impl BackgroundScanner {
|
|||
}
|
||||
|
||||
struct ScanJob {
|
||||
path: PathBuf,
|
||||
relative_path: Arc<Path>,
|
||||
abs_path: PathBuf,
|
||||
path: Arc<Path>,
|
||||
scan_queue: crossbeam_channel::Sender<ScanJob>,
|
||||
}
|
||||
|
||||
|
@ -1149,7 +1156,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_rescan() {
|
||||
fn test_rescan_simple() {
|
||||
App::test_async((), |mut app| async move {
|
||||
let dir = temp_tree(json!({
|
||||
"a": {
|
||||
|
@ -1173,10 +1180,23 @@ mod tests {
|
|||
});
|
||||
|
||||
std::fs::rename(dir.path().join("b/c"), dir.path().join("d")).unwrap();
|
||||
tree.condition(&app, move |_, _| {
|
||||
file2.path().as_ref() == Path::new("d/file2")
|
||||
})
|
||||
.await;
|
||||
|
||||
app.read(|ctx| tree.read(ctx).next_scan_complete()).await;
|
||||
|
||||
app.read(|ctx| {
|
||||
assert_eq!(
|
||||
tree.read(ctx)
|
||||
.paths()
|
||||
.map(|p| p.to_str().unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["a", "a/file1", "b", "d", "d/file2"]
|
||||
)
|
||||
});
|
||||
|
||||
// tree.condition(&app, move |_, _| {
|
||||
// file2.path().as_ref() == Path::new("d/file2")
|
||||
// })
|
||||
// .await;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1196,6 +1216,16 @@ mod tests {
|
|||
|
||||
let tree = app.add_model(|ctx| Worktree::new(dir.path(), ctx));
|
||||
app.read(|ctx| tree.read(ctx).scan_complete()).await;
|
||||
|
||||
app.read(|ctx| {
|
||||
let paths = tree
|
||||
.read(ctx)
|
||||
.paths()
|
||||
.map(|p| p.to_str().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
println!("paths {:?}", paths);
|
||||
});
|
||||
|
||||
app.read(|ctx| {
|
||||
let tree = tree.read(ctx);
|
||||
let tracked = tree.entry_for_path("tracked-dir/tracked-file1").unwrap();
|
||||
|
@ -1255,7 +1285,7 @@ mod tests {
|
|||
Arc::new(Mutex::new(Snapshot {
|
||||
id: 0,
|
||||
scan_id: 0,
|
||||
path: root_dir.path().into(),
|
||||
abs_path: root_dir.path().into(),
|
||||
entries: Default::default(),
|
||||
ignores: Default::default(),
|
||||
})),
|
||||
|
@ -1288,7 +1318,7 @@ mod tests {
|
|||
Arc::new(Mutex::new(Snapshot {
|
||||
id: 0,
|
||||
scan_id: 0,
|
||||
path: root_dir.path().into(),
|
||||
abs_path: root_dir.path().into(),
|
||||
entries: Default::default(),
|
||||
ignores: Default::default(),
|
||||
})),
|
||||
|
|
|
@ -523,7 +523,7 @@ mod tests {
|
|||
&Snapshot {
|
||||
id: 0,
|
||||
scan_id: 0,
|
||||
path: PathBuf::new().into(),
|
||||
abs_path: PathBuf::new().into(),
|
||||
ignores: Default::default(),
|
||||
entries: Default::default(),
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue