Restructure git diff state management to allow viewing buffers with different diff bases (#21258)

This is a pure refactor of our Git diff state management. Buffers are no
longer are associated with one single diff (the unstaged changes).
Instead, there is an explicit project API for retrieving a buffer's
unstaged changes, and the `Editor` view layer is responsible for
choosing what diff to associate with a buffer.

The reason for this change is that we'll soon want to add multiple "git
diff views" to Zed, one of which will show the *uncommitted* changes for
a buffer. But that view will need to co-exist with other views of the
same buffer, which may want to show the unstaged changes.

### Todo

* [x] Get git gutter and git hunks working with new structure
* [x] Update editor tests to use new APIs
* [x] Update buffer tests
* [x] Restructure remoting/collab protocol
* [x] Update assertions about staged text in
`random_project_collaboration_tests`
* [x] Move buffer tests for git diff management to a new spot, using the
new APIs

Release Notes:

- N/A

---------

Co-authored-by: Richard <richard@zed.dev>
Co-authored-by: Cole <cole@zed.dev>
Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-12-04 15:02:33 -08:00 committed by GitHub
parent 31796171de
commit a2115e7242
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1832 additions and 1651 deletions

View file

@ -14,7 +14,6 @@ path = "src/git.rs"
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
clock.workspace = true
collections.workspace = true
derive_more.workspace = true
git2.workspace = true

View file

@ -64,18 +64,33 @@ impl sum_tree::Summary for DiffHunkSummary {
#[derive(Debug, Clone)]
pub struct BufferDiff {
last_buffer_version: Option<clock::Global>,
tree: SumTree<InternalDiffHunk>,
}
impl BufferDiff {
pub fn new(buffer: &BufferSnapshot) -> BufferDiff {
BufferDiff {
last_buffer_version: None,
tree: SumTree::new(buffer),
}
}
pub async fn build(diff_base: &str, buffer: &text::BufferSnapshot) -> Self {
let mut tree = SumTree::new(buffer);
let buffer_text = buffer.as_rope().to_string();
let patch = Self::diff(diff_base, &buffer_text);
if let Some(patch) = patch {
let mut divergence = 0;
for hunk_index in 0..patch.num_hunks() {
let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer, &mut divergence);
tree.push(hunk, buffer);
}
}
Self { tree }
}
pub fn is_empty(&self) -> bool {
self.tree.is_empty()
}
@ -168,27 +183,11 @@ impl BufferDiff {
#[cfg(test)]
fn clear(&mut self, buffer: &text::BufferSnapshot) {
self.last_buffer_version = Some(buffer.version().clone());
self.tree = SumTree::new(buffer);
}
pub async fn update(&mut self, diff_base: &Rope, buffer: &text::BufferSnapshot) {
let mut tree = SumTree::new(buffer);
let diff_base_text = diff_base.to_string();
let buffer_text = buffer.as_rope().to_string();
let patch = Self::diff(&diff_base_text, &buffer_text);
if let Some(patch) = patch {
let mut divergence = 0;
for hunk_index in 0..patch.num_hunks() {
let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer, &mut divergence);
tree.push(hunk, buffer);
}
}
self.tree = tree;
self.last_buffer_version = Some(buffer.version().clone());
*self = Self::build(&diff_base.to_string(), buffer).await;
}
#[cfg(test)]