Add editor::RevertSelectedHunks to revert git diff hunks in the editor (#9068)

https://github.com/zed-industries/zed/assets/2690773/653b5658-e3f3-4aee-9a9d-0f2153b4141b

Release Notes:

- Added `editor::RevertSelectedHunks` (`cmd-alt-z` by default) for
reverting git hunks from the editor
This commit is contained in:
Kirill Bulatov 2024-03-09 01:37:24 +02:00 committed by GitHub
parent 6a7a3b257a
commit 347178039c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1003 additions and 72 deletions

View file

@ -1,6 +1,6 @@
use std::{iter, ops::Range};
use sum_tree::SumTree;
use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point};
use text::{Anchor, BufferId, BufferSnapshot, OffsetRangeExt, Point};
pub use git2 as libgit;
use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
@ -12,17 +12,53 @@ pub enum DiffHunkStatus {
Removed,
}
/// A diff hunk, representing a range of consequent lines in a singleton buffer, associated with a generic range.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiffHunk<T> {
pub buffer_range: Range<T>,
/// E.g. a range in multibuffer, that has an excerpt added, singleton buffer for which has this diff hunk.
/// Consider a singleton buffer with 10 lines, all of them are modified — so a corresponding diff hunk would have a range 0..10.
/// And a multibuffer with the excerpt of lines 2-6 from the singleton buffer.
/// If the multibuffer is searched for diff hunks, the associated range would be multibuffer rows, corresponding to rows 2..6 from the singleton buffer.
/// But the hunk range would be 0..10, same for any other excerpts from the same singleton buffer.
pub associated_range: Range<T>,
/// Singleton buffer ID this hunk belongs to.
pub buffer_id: BufferId,
/// A consequent range of lines in the singleton buffer, that were changed and produced this diff hunk.
pub buffer_range: Range<Anchor>,
/// Original singleton buffer text before the change, that was instead of the `buffer_range`.
pub diff_base_byte_range: Range<usize>,
}
impl<T> DiffHunk<T> {
fn buffer_range_empty(&self) -> bool {
if self.buffer_range.start == self.buffer_range.end {
return true;
}
// buffer diff hunks are per line, so if we arrive to the same line with different bias, it's the same hunk
let Anchor {
timestamp: timestamp_start,
offset: offset_start,
buffer_id: buffer_id_start,
bias: _,
} = self.buffer_range.start;
let Anchor {
timestamp: timestamp_end,
offset: offset_end,
buffer_id: buffer_id_end,
bias: _,
} = self.buffer_range.end;
timestamp_start == timestamp_end
&& offset_start == offset_end
&& buffer_id_start == buffer_id_end
}
}
impl DiffHunk<u32> {
pub fn status(&self) -> DiffHunkStatus {
if self.diff_base_byte_range.is_empty() {
DiffHunkStatus::Added
} else if self.buffer_range.is_empty() {
} else if self.buffer_range_empty() {
DiffHunkStatus::Removed
} else {
DiffHunkStatus::Modified
@ -35,7 +71,7 @@ impl sum_tree::Item for DiffHunk<Anchor> {
fn summary(&self) -> Self::Summary {
DiffHunkSummary {
buffer_range: self.buffer_range.clone(),
buffer_range: self.associated_range.clone(),
}
}
}
@ -57,7 +93,7 @@ impl sum_tree::Summary for DiffHunkSummary {
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct BufferDiff {
last_buffer_version: Option<clock::Global>,
tree: SumTree<DiffHunk<Anchor>>,
@ -103,8 +139,11 @@ impl BufferDiff {
})
.flat_map(move |hunk| {
[
(&hunk.buffer_range.start, hunk.diff_base_byte_range.start),
(&hunk.buffer_range.end, hunk.diff_base_byte_range.end),
(
&hunk.associated_range.start,
hunk.diff_base_byte_range.start,
),
(&hunk.associated_range.end, hunk.diff_base_byte_range.end),
]
.into_iter()
});
@ -112,17 +151,17 @@ impl BufferDiff {
let mut summaries = buffer.summaries_for_anchors_with_payload::<Point, _, _>(anchor_iter);
iter::from_fn(move || {
let (start_point, start_base) = summaries.next()?;
let (end_point, end_base) = summaries.next()?;
let (mut end_point, end_base) = summaries.next()?;
let end_row = if end_point.column > 0 {
end_point.row + 1
} else {
end_point.row
};
if end_point.column > 0 {
end_point.row += 1;
}
Some(DiffHunk {
buffer_range: start_point.row..end_row,
associated_range: start_point.row..end_point.row,
diff_base_byte_range: start_base..end_base,
buffer_range: buffer.anchor_before(start_point)..buffer.anchor_after(end_point),
buffer_id: buffer.remote_id(),
})
})
}
@ -142,7 +181,7 @@ impl BufferDiff {
cursor.prev(buffer);
let hunk = cursor.item()?;
let range = hunk.buffer_range.to_point(buffer);
let range = hunk.associated_range.to_point(buffer);
let end_row = if range.end.column > 0 {
range.end.row + 1
} else {
@ -150,8 +189,10 @@ impl BufferDiff {
};
Some(DiffHunk {
buffer_range: range.start.row..end_row,
associated_range: range.start.row..end_row,
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
buffer_range: hunk.buffer_range.clone(),
buffer_id: hunk.buffer_id,
})
})
}
@ -269,8 +310,10 @@ impl BufferDiff {
let end = Point::new(buffer_row_range.end, 0);
let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end);
DiffHunk {
associated_range: buffer_range.clone(),
buffer_range,
diff_base_byte_range,
buffer_id: buffer.remote_id(),
}
}
}
@ -289,12 +332,12 @@ pub fn assert_hunks<Iter>(
let actual_hunks = diff_hunks
.map(|hunk| {
(
hunk.buffer_range.clone(),
hunk.associated_range.clone(),
&diff_base[hunk.diff_base_byte_range],
buffer
.text_for_range(
Point::new(hunk.buffer_range.start, 0)
..Point::new(hunk.buffer_range.end, 0),
Point::new(hunk.associated_range.start, 0)
..Point::new(hunk.associated_range.end, 0),
)
.collect::<String>(),
)