Ignore excluded files on worktree entry refresh

This commit is contained in:
Kirill Bulatov 2023-12-04 17:30:46 +02:00
parent 1f6c69c7dc
commit f0ca7141b8
11 changed files with 267 additions and 212 deletions

View file

@ -1121,20 +1121,22 @@ impl Project {
project_path: impl Into<ProjectPath>,
is_directory: bool,
cx: &mut ModelContext<Self>,
) -> Option<Task<Result<Entry>>> {
) -> Task<Result<Option<Entry>>> {
let project_path = project_path.into();
let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
return Task::ready(Ok(None));
};
if self.is_local() {
Some(worktree.update(cx, |worktree, cx| {
worktree.update(cx, |worktree, cx| {
worktree
.as_local_mut()
.unwrap()
.create_entry(project_path.path, is_directory, cx)
}))
})
} else {
let client = self.client.clone();
let project_id = self.remote_id().unwrap();
Some(cx.spawn_weak(|_, mut cx| async move {
cx.spawn_weak(|_, mut cx| async move {
let response = client
.request(proto::CreateProjectEntry {
worktree_id: project_path.worktree_id.to_proto(),
@ -1143,19 +1145,20 @@ impl Project {
is_directory,
})
.await?;
let entry = response
.entry
.ok_or_else(|| anyhow!("missing entry in response"))?;
worktree
.update(&mut cx, |worktree, cx| {
worktree.as_remote_mut().unwrap().insert_entry(
entry,
response.worktree_scan_id as usize,
cx,
)
})
.await
}))
match response.entry {
Some(entry) => worktree
.update(&mut cx, |worktree, cx| {
worktree.as_remote_mut().unwrap().insert_entry(
entry,
response.worktree_scan_id as usize,
cx,
)
})
.await
.map(Some),
None => Ok(None),
}
})
}
}
@ -1164,8 +1167,10 @@ impl Project {
entry_id: ProjectEntryId,
new_path: impl Into<Arc<Path>>,
cx: &mut ModelContext<Self>,
) -> Option<Task<Result<Entry>>> {
let worktree = self.worktree_for_entry(entry_id, cx)?;
) -> Task<Result<Option<Entry>>> {
let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
return Task::ready(Ok(None));
};
let new_path = new_path.into();
if self.is_local() {
worktree.update(cx, |worktree, cx| {
@ -1178,7 +1183,7 @@ impl Project {
let client = self.client.clone();
let project_id = self.remote_id().unwrap();
Some(cx.spawn_weak(|_, mut cx| async move {
cx.spawn_weak(|_, mut cx| async move {
let response = client
.request(proto::CopyProjectEntry {
project_id,
@ -1186,19 +1191,20 @@ impl Project {
new_path: new_path.to_string_lossy().into(),
})
.await?;
let entry = response
.entry
.ok_or_else(|| anyhow!("missing entry in response"))?;
worktree
.update(&mut cx, |worktree, cx| {
worktree.as_remote_mut().unwrap().insert_entry(
entry,
response.worktree_scan_id as usize,
cx,
)
})
.await
}))
match response.entry {
Some(entry) => worktree
.update(&mut cx, |worktree, cx| {
worktree.as_remote_mut().unwrap().insert_entry(
entry,
response.worktree_scan_id as usize,
cx,
)
})
.await
.map(Some),
None => Ok(None),
}
})
}
}
@ -1207,8 +1213,10 @@ impl Project {
entry_id: ProjectEntryId,
new_path: impl Into<Arc<Path>>,
cx: &mut ModelContext<Self>,
) -> Option<Task<Result<Entry>>> {
let worktree = self.worktree_for_entry(entry_id, cx)?;
) -> Task<Result<Option<Entry>>> {
let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
return Task::ready(Ok(None));
};
let new_path = new_path.into();
if self.is_local() {
worktree.update(cx, |worktree, cx| {
@ -1221,7 +1229,7 @@ impl Project {
let client = self.client.clone();
let project_id = self.remote_id().unwrap();
Some(cx.spawn_weak(|_, mut cx| async move {
cx.spawn_weak(|_, mut cx| async move {
let response = client
.request(proto::RenameProjectEntry {
project_id,
@ -1229,19 +1237,20 @@ impl Project {
new_path: new_path.to_string_lossy().into(),
})
.await?;
let entry = response
.entry
.ok_or_else(|| anyhow!("missing entry in response"))?;
worktree
.update(&mut cx, |worktree, cx| {
worktree.as_remote_mut().unwrap().insert_entry(
entry,
response.worktree_scan_id as usize,
cx,
)
})
.await
}))
match response.entry {
Some(entry) => worktree
.update(&mut cx, |worktree, cx| {
worktree.as_remote_mut().unwrap().insert_entry(
entry,
response.worktree_scan_id as usize,
cx,
)
})
.await
.map(Some),
None => Ok(None),
}
})
}
}
@ -6820,7 +6829,7 @@ impl Project {
})
.await?;
Ok(proto::ProjectEntryResponse {
entry: Some((&entry).into()),
entry: entry.as_ref().map(|e| e.into()),
worktree_scan_id: worktree_scan_id as u64,
})
}
@ -6844,11 +6853,10 @@ impl Project {
.as_local_mut()
.unwrap()
.rename_entry(entry_id, new_path, cx)
.ok_or_else(|| anyhow!("invalid entry"))
})?
})
.await?;
Ok(proto::ProjectEntryResponse {
entry: Some((&entry).into()),
entry: entry.as_ref().map(|e| e.into()),
worktree_scan_id: worktree_scan_id as u64,
})
}
@ -6872,11 +6880,10 @@ impl Project {
.as_local_mut()
.unwrap()
.copy_entry(entry_id, new_path, cx)
.ok_or_else(|| anyhow!("invalid entry"))
})?
})
.await?;
Ok(proto::ProjectEntryResponse {
entry: Some((&entry).into()),
entry: entry.as_ref().map(|e| e.into()),
worktree_scan_id: worktree_scan_id as u64,
})
}

View file

@ -955,13 +955,8 @@ impl LocalWorktree {
) -> Task<Result<(File, String, Option<String>)>> {
let path = Arc::from(path);
let abs_path = self.absolutize(&path);
let is_excluded = self.is_path_excluded(abs_path.clone());
let fs = self.fs.clone();
let entry = if is_excluded {
None
} else {
Some(self.refresh_entry(path.clone(), None, cx))
};
let entry = self.refresh_entry(path.clone(), None, cx);
cx.spawn(|this, cx| async move {
let text = fs.load(&abs_path).await?;
@ -984,22 +979,19 @@ impl LocalWorktree {
None
};
match entry {
Some(entry) => {
let entry = entry.await?;
Ok((
File {
entry_id: Some(entry.id),
worktree: this,
path: entry.path,
mtime: entry.mtime,
is_local: true,
is_deleted: false,
},
text,
diff_base,
))
}
match entry.await? {
Some(entry) => Ok((
File {
entry_id: Some(entry.id),
worktree: this,
path: entry.path,
mtime: entry.mtime,
is_local: true,
is_deleted: false,
},
text,
diff_base,
)),
None => {
let metadata = fs
.metadata(&abs_path)
@ -1044,17 +1036,37 @@ impl LocalWorktree {
let text = buffer.as_rope().clone();
let fingerprint = text.fingerprint();
let version = buffer.version();
let save = self.write_file(path, text, buffer.line_ending(), cx);
let save = self.write_file(path.as_ref(), text, buffer.line_ending(), cx);
let fs = Arc::clone(&self.fs);
let abs_path = self.absolutize(&path);
cx.as_mut().spawn(|mut cx| async move {
let entry = save.await?;
let (entry_id, mtime, path) = match entry {
Some(entry) => (Some(entry.id), entry.mtime, entry.path),
None => {
let metadata = fs
.metadata(&abs_path)
.await
.with_context(|| {
format!(
"Fetching metadata after saving the excluded buffer {abs_path:?}"
)
})?
.with_context(|| {
format!("Excluded buffer {path:?} got removed during saving")
})?;
(None, metadata.mtime, path)
}
};
if has_changed_file {
let new_file = Arc::new(File {
entry_id: Some(entry.id),
entry_id,
worktree: handle,
path: entry.path,
mtime: entry.mtime,
path,
mtime,
is_local: true,
is_deleted: false,
});
@ -1080,13 +1092,13 @@ impl LocalWorktree {
project_id,
buffer_id,
version: serialize_version(&version),
mtime: Some(entry.mtime.into()),
mtime: Some(mtime.into()),
fingerprint: serialize_fingerprint(fingerprint),
})?;
}
buffer_handle.update(&mut cx, |buffer, cx| {
buffer.did_save(version.clone(), fingerprint, entry.mtime, cx);
buffer.did_save(version.clone(), fingerprint, mtime, cx);
});
Ok(())
@ -1111,7 +1123,7 @@ impl LocalWorktree {
path: impl Into<Arc<Path>>,
is_dir: bool,
cx: &mut ModelContext<Worktree>,
) -> Task<Result<Entry>> {
) -> Task<Result<Option<Entry>>> {
let path = path.into();
let lowest_ancestor = self.lowest_ancestor(&path);
let abs_path = self.absolutize(&path);
@ -1128,7 +1140,7 @@ impl LocalWorktree {
cx.spawn(|this, mut cx| async move {
write.await?;
let (result, refreshes) = this.update(&mut cx, |this, cx| {
let mut refreshes = Vec::<Task<anyhow::Result<Entry>>>::new();
let mut refreshes = Vec::new();
let refresh_paths = path.strip_prefix(&lowest_ancestor).unwrap();
for refresh_path in refresh_paths.ancestors() {
if refresh_path == Path::new("") {
@ -1155,14 +1167,14 @@ impl LocalWorktree {
})
}
pub fn write_file(
pub(crate) fn write_file(
&self,
path: impl Into<Arc<Path>>,
text: Rope,
line_ending: LineEnding,
cx: &mut ModelContext<Worktree>,
) -> Task<Result<Entry>> {
let path = path.into();
) -> Task<Result<Option<Entry>>> {
let path: Arc<Path> = path.into();
let abs_path = self.absolutize(&path);
let fs = self.fs.clone();
let write = cx
@ -1221,8 +1233,11 @@ impl LocalWorktree {
entry_id: ProjectEntryId,
new_path: impl Into<Arc<Path>>,
cx: &mut ModelContext<Worktree>,
) -> Option<Task<Result<Entry>>> {
let old_path = self.entry_for_id(entry_id)?.path.clone();
) -> Task<Result<Option<Entry>>> {
let old_path = match self.entry_for_id(entry_id) {
Some(entry) => entry.path.clone(),
None => return Task::ready(Ok(None)),
};
let new_path = new_path.into();
let abs_old_path = self.absolutize(&old_path);
let abs_new_path = self.absolutize(&new_path);
@ -1232,7 +1247,7 @@ impl LocalWorktree {
.await
});
Some(cx.spawn(|this, mut cx| async move {
cx.spawn(|this, mut cx| async move {
rename.await?;
this.update(&mut cx, |this, cx| {
this.as_local_mut()
@ -1240,7 +1255,7 @@ impl LocalWorktree {
.refresh_entry(new_path.clone(), Some(old_path), cx)
})
.await
}))
})
}
pub fn copy_entry(
@ -1248,8 +1263,11 @@ impl LocalWorktree {
entry_id: ProjectEntryId,
new_path: impl Into<Arc<Path>>,
cx: &mut ModelContext<Worktree>,
) -> Option<Task<Result<Entry>>> {
let old_path = self.entry_for_id(entry_id)?.path.clone();
) -> Task<Result<Option<Entry>>> {
let old_path = match self.entry_for_id(entry_id) {
Some(entry) => entry.path.clone(),
None => return Task::ready(Ok(None)),
};
let new_path = new_path.into();
let abs_old_path = self.absolutize(&old_path);
let abs_new_path = self.absolutize(&new_path);
@ -1264,7 +1282,7 @@ impl LocalWorktree {
.await
});
Some(cx.spawn(|this, mut cx| async move {
cx.spawn(|this, mut cx| async move {
copy.await?;
this.update(&mut cx, |this, cx| {
this.as_local_mut()
@ -1272,7 +1290,7 @@ impl LocalWorktree {
.refresh_entry(new_path.clone(), None, cx)
})
.await
}))
})
}
pub fn expand_entry(
@ -1308,7 +1326,10 @@ impl LocalWorktree {
path: Arc<Path>,
old_path: Option<Arc<Path>>,
cx: &mut ModelContext<Worktree>,
) -> Task<Result<Entry>> {
) -> Task<Result<Option<Entry>>> {
if self.is_path_excluded(self.absolutize(&path)) {
return Task::ready(Ok(None));
}
let paths = if let Some(old_path) = old_path.as_ref() {
vec![old_path.clone(), path.clone()]
} else {
@ -1317,13 +1338,15 @@ impl LocalWorktree {
let mut refresh = self.refresh_entries_for_paths(paths);
cx.spawn_weak(move |this, mut cx| async move {
refresh.recv().await;
this.upgrade(&cx)
let new_entry = this
.upgrade(&cx)
.ok_or_else(|| anyhow!("worktree was dropped"))?
.update(&mut cx, |this, _| {
this.entry_for_path(path)
.cloned()
.ok_or_else(|| anyhow!("failed to read path after update"))
})
})?;
Ok(Some(new_entry))
})
}

View file

@ -1174,6 +1174,7 @@ async fn test_create_directory_during_initial_scan(cx: &mut TestAppContext) {
.create_entry("a/e".as_ref(), true, cx)
})
.await
.unwrap()
.unwrap();
assert!(entry.is_dir());
@ -1222,6 +1223,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
.create_entry("a/b/c/d.txt".as_ref(), false, cx)
})
.await
.unwrap()
.unwrap();
assert!(entry.is_file());
@ -1257,6 +1259,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
.create_entry("a/b/c/d.txt".as_ref(), false, cx)
})
.await
.unwrap()
.unwrap();
assert!(entry.is_file());
@ -1275,6 +1278,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
.create_entry("a/b/c/e.txt".as_ref(), false, cx)
})
.await
.unwrap()
.unwrap();
assert!(entry.is_file());
@ -1291,6 +1295,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
.create_entry("d/e/f/g.txt".as_ref(), false, cx)
})
.await
.unwrap()
.unwrap();
assert!(entry.is_file());
@ -1616,14 +1621,14 @@ fn randomly_mutate_worktree(
entry.id.0,
new_path
);
let task = worktree.rename_entry(entry.id, new_path, cx).unwrap();
let task = worktree.rename_entry(entry.id, new_path, cx);
cx.foreground().spawn(async move {
task.await?;
task.await?.unwrap();
Ok(())
})
}
_ => {
let task = if entry.is_dir() {
if entry.is_dir() {
let child_path = entry.path.join(random_filename(rng));
let is_dir = rng.gen_bool(0.3);
log::info!(
@ -1631,15 +1636,20 @@ fn randomly_mutate_worktree(
if is_dir { "dir" } else { "file" },
child_path,
);
worktree.create_entry(child_path, is_dir, cx)
let task = worktree.create_entry(child_path, is_dir, cx);
cx.foreground().spawn(async move {
task.await?;
Ok(())
})
} else {
log::info!("overwriting file {:?} ({})", entry.path, entry.id.0);
worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx)
};
cx.foreground().spawn(async move {
task.await?;
Ok(())
})
let task =
worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx);
cx.foreground().spawn(async move {
task.await?;
Ok(())
})
}
}
}
}