Disable diff hunks for untracked files, even w/ no newline at eof (#25980)

This fixes an issue where diff hunks were shown for untracked files, but
only if the files did not end with a newline.

Release Notes:

- N/A
This commit is contained in:
Max Brunsfeld 2025-03-03 22:18:27 -08:00 committed by GitHub
parent 11b79d0ab9
commit 563baf682e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 34 additions and 41 deletions

View file

@ -56,8 +56,8 @@ pub enum DiffHunkSecondaryStatus {
/// A diff hunk resolved to rows in the buffer. /// A diff hunk resolved to rows in the buffer.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiffHunk { pub struct DiffHunk {
/// The buffer range, expressed in terms of rows. /// The buffer range as points.
pub row_range: Range<u32>, pub range: Range<Point>,
/// The range in the buffer to which this hunk corresponds. /// The range in the buffer to which this hunk corresponds.
pub buffer_range: Range<Anchor>, pub buffer_range: Range<Anchor>,
/// The range in the buffer's diff base text to which this hunk corresponds. /// The range in the buffer's diff base text to which this hunk corresponds.
@ -362,6 +362,7 @@ impl BufferDiffInner {
pending_hunks = secondary.pending_hunks.clone(); pending_hunks = secondary.pending_hunks.clone();
} }
let max_point = buffer.max_point();
let mut summaries = buffer.summaries_for_anchors_with_payload::<Point, _, _>(anchor_iter); let mut summaries = buffer.summaries_for_anchors_with_payload::<Point, _, _>(anchor_iter);
iter::from_fn(move || loop { iter::from_fn(move || loop {
let (start_point, (start_anchor, start_base)) = summaries.next()?; let (start_point, (start_anchor, start_base)) = summaries.next()?;
@ -371,7 +372,7 @@ impl BufferDiffInner {
continue; continue;
} }
if end_point.column > 0 { if end_point.column > 0 && end_point < max_point {
end_point.row += 1; end_point.row += 1;
end_point.column = 0; end_point.column = 0;
end_anchor = buffer.anchor_before(end_point); end_anchor = buffer.anchor_before(end_point);
@ -416,7 +417,7 @@ impl BufferDiffInner {
} }
return Some(DiffHunk { return Some(DiffHunk {
row_range: start_point.row..end_point.row, range: start_point..end_point,
diff_base_byte_range: start_base..end_base, diff_base_byte_range: start_base..end_base,
buffer_range: start_anchor..end_anchor, buffer_range: start_anchor..end_anchor,
secondary_status, secondary_status,
@ -442,14 +443,9 @@ impl BufferDiffInner {
let hunk = cursor.item()?; let hunk = cursor.item()?;
let range = hunk.buffer_range.to_point(buffer); let range = hunk.buffer_range.to_point(buffer);
let end_row = if range.end.column > 0 {
range.end.row + 1
} else {
range.end.row
};
Some(DiffHunk { Some(DiffHunk {
row_range: range.start.row..end_row, range,
diff_base_byte_range: hunk.diff_base_byte_range.clone(), diff_base_byte_range: hunk.diff_base_byte_range.clone(),
buffer_range: hunk.buffer_range.clone(), buffer_range: hunk.buffer_range.clone(),
// The secondary status is not used by callers of this method. // The secondary status is not used by callers of this method.
@ -1136,12 +1132,10 @@ pub fn assert_hunks<Iter>(
let actual_hunks = diff_hunks let actual_hunks = diff_hunks
.map(|hunk| { .map(|hunk| {
( (
hunk.row_range.clone(), hunk.range.clone(),
&diff_base[hunk.diff_base_byte_range.clone()], &diff_base[hunk.diff_base_byte_range.clone()],
buffer buffer
.text_for_range( .text_for_range(hunk.range.clone())
Point::new(hunk.row_range.start, 0)..Point::new(hunk.row_range.end, 0),
)
.collect::<String>(), .collect::<String>(),
hunk.status(), hunk.status(),
) )
@ -1150,7 +1144,14 @@ pub fn assert_hunks<Iter>(
let expected_hunks: Vec<_> = expected_hunks let expected_hunks: Vec<_> = expected_hunks
.iter() .iter()
.map(|(r, s, h, status)| (r.clone(), *s, h.to_string(), *status)) .map(|(r, old_text, new_text, status)| {
(
Point::new(r.start, 0)..Point::new(r.end, 0),
*old_text,
new_text.to_string(),
*status,
)
})
.collect(); .collect();
assert_eq!(actual_hunks, expected_hunks); assert_eq!(actual_hunks, expected_hunks);

View file

@ -13733,7 +13733,7 @@ impl Editor {
buffer_range: hunk.buffer_range, buffer_range: hunk.buffer_range,
diff_base_byte_range: hunk.diff_base_byte_range, diff_base_byte_range: hunk.diff_base_byte_range,
secondary_status: hunk.secondary_status, secondary_status: hunk.secondary_status,
row_range: 0..0, // unused range: Point::zero()..Point::zero(), // unused
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
&buffer_snapshot, &buffer_snapshot,
@ -16041,9 +16041,9 @@ impl Editor {
if let Some(buffer) = multi_buffer.buffer(buffer_id) { if let Some(buffer) = multi_buffer.buffer(buffer_id) {
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.edit( buffer.edit(
changes.into_iter().map(|(range, text)| { changes
(range, text.to_string().map(Arc::<str>::from)) .into_iter()
}), .map(|(range, text)| (range, text.to_string())),
None, None,
cx, cx,
); );
@ -17161,17 +17161,14 @@ impl EditorSnapshot {
for hunk in self.buffer_snapshot.diff_hunks_in_range( for hunk in self.buffer_snapshot.diff_hunks_in_range(
Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0), Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
) { ) {
// Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it // Include deleted hunks that are adjacent to the query range, because
// when the caret is just above or just below the deleted hunk. // otherwise they would be missed.
let allow_adjacent = hunk.status().is_deleted(); let mut intersects_range = hunk.row_range.overlaps(&query_rows);
let related_to_selection = if allow_adjacent { if hunk.status().is_deleted() {
hunk.row_range.overlaps(&query_rows) intersects_range |= hunk.row_range.start == query_rows.end;
|| hunk.row_range.start == query_rows.end intersects_range |= hunk.row_range.end == query_rows.start;
|| hunk.row_range.end == query_rows.start }
} else { if intersects_range {
hunk.row_range.overlaps(&query_rows)
};
if related_to_selection {
if !processed_buffer_rows if !processed_buffer_rows
.entry(hunk.buffer_id) .entry(hunk.buffer_id)
.or_default() .or_default()

View file

@ -3498,17 +3498,12 @@ impl MultiBufferSnapshot {
if hunk.is_created_file() && !self.all_diff_hunks_expanded { if hunk.is_created_file() && !self.all_diff_hunks_expanded {
return None; return None;
} }
Some(( Some((hunk.range.clone(), hunk))
Point::new(hunk.row_range.start, 0)..Point::new(hunk.row_range.end, 0),
hunk,
))
}), }),
) )
}) })
.filter_map(move |(range, hunk, excerpt)| { .filter_map(move |(range, hunk, excerpt)| {
if range.start != range.end if range.start != range.end && range.end == query_range.start && !hunk.range.is_empty()
&& range.end == query_range.start
&& !hunk.row_range.is_empty()
{ {
return None; return None;
} }
@ -3841,12 +3836,12 @@ impl MultiBufferSnapshot {
&excerpt.buffer, &excerpt.buffer,
) { ) {
let hunk_range = hunk.buffer_range.to_offset(&excerpt.buffer); let hunk_range = hunk.buffer_range.to_offset(&excerpt.buffer);
if hunk.row_range.end >= buffer_end_row { if hunk.range.end >= Point::new(buffer_end_row, 0) {
continue; continue;
} }
let hunk_start = Point::new(hunk.row_range.start, 0); let hunk_start = hunk.range.start;
let hunk_end = Point::new(hunk.row_range.end, 0); let hunk_end = hunk.range.end;
cursor.seek_to_buffer_position_in_current_excerpt(&DimensionPair { cursor.seek_to_buffer_position_in_current_excerpt(&DimensionPair {
key: hunk_range.start, key: hunk_range.start,

View file

@ -2274,7 +2274,7 @@ impl ReferenceMultibuffer {
} }
if !hunk.buffer_range.start.is_valid(&buffer) { if !hunk.buffer_range.start.is_valid(&buffer) {
log::trace!("skipping hunk with deleted start: {:?}", hunk.row_range); log::trace!("skipping hunk with deleted start: {:?}", hunk.range);
continue; continue;
} }