Gutter-highlight removed and added portions of expanded diff hunks separately (#24834)

cc @iamnbutler 

Release Notes:

- Color gutter highlights separately for removed and deleted portions of
git modification hunks

Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Cole Miller 2025-02-14 10:31:30 -05:00 committed by GitHub
parent 4aae0e2f6c
commit 39c9b1f170
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -79,17 +79,18 @@ use workspace::{item::Item, notifications::NotifyTaskExt, Workspace};
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
/// Note that for a "modified" MultiBufferDiffHunk, there are two DisplayDiffHunks,
/// one for the deleted portion and one for the added portion.
#[derive(Debug, Clone, PartialEq, Eq)]
enum DisplayDiffHunk {
Folded {
display_row: DisplayRow,
},
Unfolded {
diff_base_byte_range: Range<usize>,
display_row_range: Range<DisplayRow>,
multi_buffer_range: Range<Anchor>,
status: DiffHunkStatus,
contains_expanded: bool,
expanded: bool,
},
}
@ -104,7 +105,7 @@ struct SelectionLayout {
}
impl SelectionLayout {
fn new<T: ToPoint + ToDisplayPoint + Clone>(
fn new<T: multi_buffer::ToPoint + ToDisplayPoint + Clone>(
selection: Selection<T>,
line_mode: bool,
cursor_shape: CursorShape,
@ -1557,36 +1558,97 @@ impl EditorElement {
let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
let hunk_display_start = snapshot.point_to_display_point(hunk_start_point, Bias::Left);
let hunk_added_start_at =
Anchor::in_buffer(hunk.excerpt_id, hunk.buffer_id, hunk.buffer_range.start);
let hunk_deleted_to_added_break = snapshot.point_to_display_point(
hunk_added_start_at.to_point(&snapshot.buffer_snapshot),
Bias::Right,
);
let hunk_display_end = snapshot.point_to_display_point(hunk_end_point, Bias::Right);
let display_hunk = if hunk_display_start.column() != 0 {
DisplayDiffHunk::Folded {
display_row: hunk_display_start.row(),
}
if hunk_display_start.column() != 0 {
display_hunks.push((
DisplayDiffHunk::Folded {
display_row: hunk_display_start.row(),
},
None,
));
} else {
let mut end_row = hunk_display_end.row();
if hunk_display_end.column() > 0 {
end_row.0 += 1;
}
let start_row = hunk_display_start.row();
let contains_expanded = snapshot
.row_infos(start_row)
.take(end_row.0 as usize - start_row.0 as usize)
.any(|row_info| row_info.diff_status.is_some());
DisplayDiffHunk::Unfolded {
status: hunk.status(),
diff_base_byte_range: hunk.diff_base_byte_range,
display_row_range: hunk_display_start.row()..end_row,
multi_buffer_range: Anchor::range_in_buffer(
hunk.excerpt_id,
hunk.buffer_id,
hunk.buffer_range,
),
contains_expanded,
let deleted_count = snapshot
.buffer_snapshot
.row_infos(hunk.row_range.start)
.take(hunk.row_range.end.0 as usize - hunk.row_range.start.0 as usize)
.take_while(|row_info| {
matches!(row_info.diff_status, Some(DiffHunkStatus::Removed(_)))
})
.count();
let has_added = snapshot
.buffer_snapshot
.row_infos(hunk.row_range.start)
.take(hunk.row_range.end.0 as usize - hunk.row_range.start.0 as usize)
.any(|row_info| matches!(row_info.diff_status, Some(DiffHunkStatus::Added(_))));
let expanded = deleted_count > 0 || has_added;
if deleted_count > 0 && has_added {
display_hunks.push((
DisplayDiffHunk::Unfolded {
status: DiffHunkStatus::Removed(hunk.secondary_status),
display_row_range: hunk_display_start.row()
..hunk_display_start.row() + DisplayRow(deleted_count as u32),
multi_buffer_range: Anchor::range_in_buffer(
hunk.excerpt_id,
hunk.buffer_id,
hunk.buffer_range.clone(),
),
expanded,
},
None,
));
display_hunks.push((
DisplayDiffHunk::Unfolded {
status: DiffHunkStatus::Added(hunk.secondary_status),
display_row_range: hunk_display_start.row()
+ DisplayRow(deleted_count as u32)
..end_row,
multi_buffer_range: Anchor::range_in_buffer(
hunk.excerpt_id,
hunk.buffer_id,
hunk.buffer_range,
),
expanded,
},
None,
));
} else {
let status = if expanded && matches!(hunk.status(), DiffHunkStatus::Modified(_))
{
if hunk_display_start.row() < hunk_deleted_to_added_break.row() {
DiffHunkStatus::Removed(hunk.secondary_status)
} else {
DiffHunkStatus::Added(hunk.secondary_status)
}
} else {
hunk.status()
};
display_hunks.push((
DisplayDiffHunk::Unfolded {
status,
display_row_range: hunk_display_start.row()..end_row,
multi_buffer_range: Anchor::range_in_buffer(
hunk.excerpt_id,
hunk.buffer_id,
hunk.buffer_range,
),
expanded,
},
None,
));
}
};
display_hunks.push((display_hunk, None));
}
let git_gutter_setting = ProjectSettings::get_global(cx)
@ -1924,7 +1986,8 @@ impl EditorElement {
if tasks.offset.0 < offset_range_start || tasks.offset.0 >= offset_range_end {
return None;
}
let multibuffer_point = tasks.offset.0.to_point(&snapshot.buffer_snapshot);
let multibuffer_point =
multi_buffer::ToPoint::to_point(&tasks.offset.0, &snapshot.buffer_snapshot);
let multibuffer_row = MultiBufferRow(multibuffer_point.row);
let buffer_folded = snapshot
.buffer_snapshot
@ -4579,7 +4642,7 @@ impl EditorElement {
DisplayDiffHunk::Unfolded {
status,
display_row_range,
contains_expanded,
expanded,
..
} => hitbox.as_ref().map(|hunk_hitbox| match status {
DiffHunkStatus::Added(secondary_status) => (
@ -4587,14 +4650,14 @@ impl EditorElement {
cx.theme().colors().version_control_added.opacity(0.7),
corners,
secondary_status,
*contains_expanded,
*expanded,
),
DiffHunkStatus::Modified(secondary_status) => (
hunk_hitbox.bounds,
cx.theme().colors().version_control_modified.opacity(0.7),
corners,
secondary_status,
*contains_expanded,
*expanded,
),
DiffHunkStatus::Removed(secondary_status)
if !display_row_range.is_empty() =>
@ -4604,7 +4667,7 @@ impl EditorElement {
cx.theme().colors().version_control_deleted.opacity(0.7),
corners,
secondary_status,
*contains_expanded,
*expanded,
)
}
DiffHunkStatus::Removed(secondary_status) => (
@ -4618,7 +4681,7 @@ impl EditorElement {
cx.theme().colors().version_control_deleted.opacity(0.7),
Corners::all(1. * line_height),
secondary_status,
*contains_expanded,
*expanded,
),
}),
};
@ -4628,16 +4691,15 @@ impl EditorElement {
background_color,
corner_radii,
secondary_status,
contains_expanded,
expanded,
)) = hunk_to_paint
{
let background = if *secondary_status != DiffHunkSecondaryStatus::None
&& contains_expanded
{
pattern_slash(background_color, line_height.0 / 2.5)
} else {
solid_color(background_color)
};
let background =
if *secondary_status != DiffHunkSecondaryStatus::None && expanded {
pattern_slash(background_color, line_height.0 / 2.5)
} else {
solid_color(background_color)
};
window.paint_quad(quad(
hunk_bounds,