Ignore excluded files on worktree entry refresh
This commit is contained in:
parent
1f6c69c7dc
commit
f0ca7141b8
11 changed files with 267 additions and 212 deletions
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue