Move git status out of Entry (#22224)

- [x] Rewrite worktree git handling
- [x] Fix tests
- [x] Fix `test_propagate_statuses_for_repos_under_project`
- [x] Replace `WorkDirectoryEntry` with `WorkDirectory` in
`RepositoryEntry`
- [x] Add a worktree event for capturing git status changes
- [x] Confirm that the local repositories are correctly updating the new
WorkDirectory field
- [x] Implement the git statuses query as a join when pulling entries
out of worktree
- [x] Use this new join to implement the project panel and outline
panel.
- [x] Synchronize git statuses over the wire for collab and remote dev
(use the existing `worktree_repository_statuses` table, adjust as
needed)
- [x] Only send changed statuses to collab

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.com>
Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Mikayla Maki 2025-01-03 17:00:16 -08:00 committed by GitHub
parent 72057e5716
commit 9613084f59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 2824 additions and 1254 deletions

View file

@ -2748,7 +2748,7 @@ mod tests {
.iter()
.filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
.count(),
"Should have one folded block, prodicing a header of the second buffer"
"Should have one folded block, producing a header of the second buffer"
);
assert_eq!(
blocks_snapshot.text(),
@ -2994,7 +2994,7 @@ mod tests {
}
})
.count(),
"Should have one folded block, prodicing a header of the second buffer"
"Should have one folded block, producing a header of the second buffer"
);
assert_eq!(blocks_snapshot.text(), "\n");
assert_eq!(

View file

@ -11780,7 +11780,7 @@ impl Editor {
}
/// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
/// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
/// Returns a map of display rows that are highlighted and their corresponding highlight color.
/// Allows to ignore certain kinds of highlights.
pub fn highlighted_display_rows(
&mut self,
@ -12573,7 +12573,7 @@ impl Editor {
.file()
.is_none()
.then(|| {
// Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
// Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
// so `workspace.open_project_item` will never find them, always opening a new editor.
// Instead, we try to activate the existing editor in the pane first.
let (editor, pane_item_index) =

View file

@ -194,14 +194,24 @@ impl ProjectDiffEditor {
let open_tasks = project
.update(&mut cx, |project, cx| {
let worktree = project.worktree_for_id(id, cx)?;
let applicable_entries = worktree
.read(cx)
.entries(false, 0)
.filter(|entry| !entry.is_external)
.filter(|entry| entry.is_file())
.filter_map(|entry| Some((entry.git_status?, entry)))
.filter_map(|(git_status, entry)| {
Some((git_status, entry.id, project.path_for_entry(entry.id, cx)?))
let snapshot = worktree.read(cx).snapshot();
let applicable_entries = snapshot
.repositories()
.flat_map(|entry| {
entry.status().map(|git_entry| {
(git_entry.status, entry.join(git_entry.repo_path))
})
})
.filter_map(|(status, path)| {
let id = snapshot.entry_for_path(&path)?.id;
Some((
status,
id,
ProjectPath {
worktree_id: snapshot.id(),
path: path.into(),
},
))
})
.collect::<Vec<_>>();
Some(

View file

@ -615,9 +615,20 @@ impl Item for Editor {
.read(cx)
.as_singleton()
.and_then(|buffer| buffer.read(cx).project_path(cx))
.and_then(|path| self.project.as_ref()?.read(cx).entry_for_path(&path, cx))
.map(|entry| {
entry_git_aware_label_color(entry.git_status, entry.is_ignored, params.selected)
.and_then(|path| {
let project = self.project.as_ref()?.read(cx);
let entry = project.entry_for_path(&path, cx)?;
let git_status = project
.worktree_for_id(path.worktree_id, cx)?
.read(cx)
.snapshot()
.status_for_file(path.path);
Some(entry_git_aware_label_color(
git_status,
entry.is_ignored,
params.selected,
))
})
.unwrap_or_else(|| entry_label_color(params.selected))
} else {
@ -1559,10 +1570,10 @@ pub fn entry_git_aware_label_color(
Color::Ignored
} else {
match git_status {
Some(GitFileStatus::Added) => Color::Created,
Some(GitFileStatus::Added) | Some(GitFileStatus::Untracked) => Color::Created,
Some(GitFileStatus::Modified) => Color::Modified,
Some(GitFileStatus::Conflict) => Color::Conflict,
None => entry_label_color(selected),
Some(GitFileStatus::Deleted) | None => entry_label_color(selected),
}
}
}

View file

@ -257,7 +257,8 @@ impl EditorLspTestContext {
Self::new(language, Default::default(), cx).await
}
// Constructs lsp range using a marked string with '[', ']' range delimiters
/// Constructs lsp range using a marked string with '[', ']' range delimiters
#[track_caller]
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
let ranges = self.ranges(marked_text);
self.to_lsp_range(ranges[0].clone())

View file

@ -230,6 +230,7 @@ impl EditorTestContext {
self.cx.background_executor.run_until_parked();
}
#[track_caller]
pub fn ranges(&mut self, marked_text: &str) -> Vec<Range<usize>> {
let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
assert_eq!(self.buffer_text(), unmarked_text);