Skip to content

Commit

Permalink
Fix issues with adjacent diff hunks (#25367)
Browse files Browse the repository at this point in the history
Closes #ISSUE

Release Notes:

- Fixed being unable to toggle diff hunks with the mouse in some cases
  • Loading branch information
cole-miller authored Feb 22, 2025
1 parent 4118f42 commit ef53f7a
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 39 deletions.
13 changes: 6 additions & 7 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12990,14 +12990,13 @@ impl Editor {
})
}

fn toggle_diff_hunks_in_ranges_narrow(
&mut self,
ranges: Vec<Range<Anchor>>,
cx: &mut Context<'_, Editor>,
) {
fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
self.buffer.update(cx, |buffer, cx| {
let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
buffer.expand_or_collapse_diff_hunks_narrow(ranges, expand, cx);
let snapshot = buffer.snapshot(cx);
let excerpt_id = range.end.excerpt_id;
let point_range = range.to_point(&snapshot);
let expand = !buffer.single_hunk_is_expanded(range, cx);
buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
})
}

Expand Down
157 changes: 157 additions & 0 deletions crates/editor/src/editor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14821,6 +14821,163 @@ async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
);
}

#[gpui::test]
async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;

let diff_base = r#"
a
b
c
"#
.unindent();

cx.set_state(
&r#"
ˇA
b
C
"#
.unindent(),
);
cx.set_diff_base(&diff_base);
cx.update_editor(|editor, window, cx| {
editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
});
executor.run_until_parked();

let both_hunks_expanded = r#"
- a
+ ˇA
b
- c
+ C
"#
.unindent();

cx.assert_state_with_diff(both_hunks_expanded.clone());

let hunk_ranges = cx.update_editor(|editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let hunks = editor
.diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
.collect::<Vec<_>>();
let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
let buffer_id = hunks[0].buffer_id;
hunks
.into_iter()
.map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
.collect::<Vec<_>>()
});
assert_eq!(hunk_ranges.len(), 2);

cx.update_editor(|editor, _, cx| {
editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
});
executor.run_until_parked();

let second_hunk_expanded = r#"
ˇA
b
- c
+ C
"#
.unindent();

cx.assert_state_with_diff(second_hunk_expanded);

cx.update_editor(|editor, _, cx| {
editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
});
executor.run_until_parked();

cx.assert_state_with_diff(both_hunks_expanded.clone());

cx.update_editor(|editor, _, cx| {
editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
});
executor.run_until_parked();

let first_hunk_expanded = r#"
- a
+ ˇA
b
C
"#
.unindent();

cx.assert_state_with_diff(first_hunk_expanded);

cx.update_editor(|editor, _, cx| {
editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
});
executor.run_until_parked();

cx.assert_state_with_diff(both_hunks_expanded);

cx.set_state(
&r#"
ˇA
b
"#
.unindent(),
);
cx.run_until_parked();

// TODO this cursor position seems bad
cx.assert_state_with_diff(
r#"
- ˇa
+ A
b
"#
.unindent(),
);

cx.update_editor(|editor, window, cx| {
editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx);
});

cx.assert_state_with_diff(
r#"
- ˇa
+ A
b
- c
"#
.unindent(),
);

let hunk_ranges = cx.update_editor(|editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let hunks = editor
.diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
.collect::<Vec<_>>();
let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
let buffer_id = hunks[0].buffer_id;
hunks
.into_iter()
.map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
.collect::<Vec<_>>()
});
assert_eq!(hunk_ranges.len(), 2);

cx.update_editor(|editor, _, cx| {
editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
});
executor.run_until_parked();

cx.assert_state_with_diff(
r#"
- ˇa
+ A
b
"#
.unindent(),
);
}

#[gpui::test]
fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
init_test(cx, |_| {});
Expand Down
2 changes: 1 addition & 1 deletion crates/editor/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ impl EditorElement {
let mut modifiers = event.modifiers;

if let Some(hovered_hunk) = hovered_hunk {
editor.toggle_diff_hunks_in_ranges_narrow(vec![hovered_hunk], cx);
editor.toggle_single_diff_hunk(hovered_hunk, cx);
cx.notify();
return;
} else if gutter_hitbox.is_hovered(window) {
Expand Down
60 changes: 29 additions & 31 deletions crates/multi_buffer/src/multi_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2388,6 +2388,23 @@ impl MultiBuffer {
.is_some()
}

pub fn single_hunk_is_expanded(&self, range: Range<Anchor>, cx: &App) -> bool {
let snapshot = self.read(cx);
let mut cursor = snapshot.diff_transforms.cursor::<usize>(&());
let offset_range = range.to_offset(&snapshot);
cursor.seek(&offset_range.start, Bias::Right, &());
while let Some(item) = cursor.item() {
if *cursor.start() >= offset_range.end && *cursor.start() > offset_range.start {
break;
}
if item.hunk_info().is_some() {
return true;
}
cursor.next(&());
}
false
}

pub fn has_expanded_diff_hunks_in_ranges(&self, ranges: &[Range<Anchor>], cx: &App) -> bool {
let snapshot = self.read(cx);
let mut cursor = snapshot.diff_transforms.cursor::<usize>(&());
Expand All @@ -2411,9 +2428,9 @@ impl MultiBuffer {
false
}

fn expand_or_collapse_diff_hunks_internal(
pub fn expand_or_collapse_diff_hunks_inner(
&mut self,
ranges: impl Iterator<Item = (Range<Point>, ExcerptId)>,
ranges: impl IntoIterator<Item = (Range<Point>, ExcerptId)>,
expand: bool,
cx: &mut Context<Self>,
) {
Expand Down Expand Up @@ -2459,42 +2476,23 @@ impl MultiBuffer {
});
}

pub fn expand_or_collapse_diff_hunks_narrow(
&mut self,
ranges: Vec<Range<Anchor>>,
expand: bool,
cx: &mut Context<Self>,
) {
let snapshot = self.snapshot.borrow().clone();
self.expand_or_collapse_diff_hunks_internal(
ranges
.iter()
.map(move |range| (range.to_point(&snapshot), range.end.excerpt_id)),
expand,
cx,
);
}

pub fn expand_or_collapse_diff_hunks(
&mut self,
ranges: Vec<Range<Anchor>>,
expand: bool,
cx: &mut Context<Self>,
) {
let snapshot = self.snapshot.borrow().clone();
self.expand_or_collapse_diff_hunks_internal(
ranges.iter().map(move |range| {
let end_excerpt_id = range.end.excerpt_id;
let range = range.to_point(&snapshot);
let mut peek_end = range.end;
if range.end.row < snapshot.max_row().0 {
peek_end = Point::new(range.end.row + 1, 0);
};
(range.start..peek_end, end_excerpt_id)
}),
expand,
cx,
);
let ranges = ranges.iter().map(move |range| {
let end_excerpt_id = range.end.excerpt_id;
let range = range.to_point(&snapshot);
let mut peek_end = range.end;
if range.end.row < snapshot.max_row().0 {
peek_end = Point::new(range.end.row + 1, 0);
};
(range.start..peek_end, end_excerpt_id)
});
self.expand_or_collapse_diff_hunks_inner(ranges, expand, cx);
}

pub fn resize_excerpt(
Expand Down

0 comments on commit ef53f7a

Please sign in to comment.