Skip to content

Commit

Permalink
New commit review flow in project diff view (#25229)
Browse files Browse the repository at this point in the history
Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <[email protected]>
  • Loading branch information
ConradIrwin and iamnbutler authored Feb 21, 2025
1 parent 6b9397c commit 4871d3c
Show file tree
Hide file tree
Showing 18 changed files with 979 additions and 477 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions assets/keymaps/default-macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,14 @@
"escape": "git_panel::ToggleFocus"
}
},
{
"context": "GitCommit > Editor",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::Newline",
"cmd-enter": "git::Commit"
}
},
{
"context": "GitPanel > Editor",
"use_key_equivalents": true,
Expand Down
47 changes: 40 additions & 7 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12769,7 +12769,7 @@ impl Editor {
self.toggle_diff_hunks_in_ranges(ranges, cx);
}

fn diff_hunks_in_ranges<'a>(
pub fn diff_hunks_in_ranges<'a>(
&'a self,
ranges: &'a [Range<Anchor>],
buffer: &'a MultiBufferSnapshot,
Expand Down Expand Up @@ -12814,9 +12814,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let head = self.selections.newest_anchor().head();
self.stage_or_unstage_diff_hunks(true, &[head..head], cx);
self.go_to_next_hunk(&Default::default(), window, cx);
self.do_stage_or_unstage_and_next(true, window, cx);
}

pub fn unstage_and_next(
Expand All @@ -12825,9 +12823,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let head = self.selections.newest_anchor().head();
self.stage_or_unstage_diff_hunks(false, &[head..head], cx);
self.go_to_next_hunk(&Default::default(), window, cx);
self.do_stage_or_unstage_and_next(false, window, cx);
}

pub fn stage_or_unstage_diff_hunks(
Expand All @@ -12849,6 +12845,43 @@ impl Editor {
}
}

fn do_stage_or_unstage_and_next(
&mut self,
stage: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
let mut ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
if ranges.iter().any(|range| range.start != range.end) {
self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
return;
}

if !self.buffer().read(cx).is_singleton() {
if let Some((excerpt_id, buffer, range)) = self.active_excerpt(cx) {
ranges = vec![multi_buffer::Anchor::range_in_buffer(
excerpt_id,
buffer.read(cx).remote_id(),
range,
)];
self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
let snapshot = self.buffer().read(cx).snapshot(cx);
let mut point = ranges.last().unwrap().end.to_point(&snapshot);
if point.row < snapshot.max_row().0 {
point.row += 1;
point.column = 0;
point = snapshot.clip_point(point, Bias::Right);
self.change_selections(Some(Autoscroll::top_relative(6)), window, cx, |s| {
s.select_ranges([point..point]);
})
}
return;
}
}
self.stage_or_unstage_diff_hunks(stage, &ranges[..], cx);
self.go_to_next_hunk(&Default::default(), window, cx);
}

fn do_stage_or_unstage(
project: &Entity<Project>,
stage: bool,
Expand Down
204 changes: 140 additions & 64 deletions crates/editor/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4343,21 +4343,26 @@ impl EditorElement {
let y = display_row_range.start.as_f32() * line_height
+ text_hitbox.bounds.top()
- scroll_pixel_position.y;
let x = text_hitbox.bounds.right() - px(100.);

let mut element = diff_hunk_controls(
display_row_range.start.0,
status,
multi_buffer_range.clone(),
line_height,
&editor,
cx,
);
element.prepaint_as_root(
gpui::Point::new(x, y),
size(px(100.0), line_height).into(),
window,
cx,
);
let size =
element.layout_as_root(size(px(100.0), line_height).into(), window, cx);

let x = text_hitbox.bounds.right()
- self.style.scrollbar_width
- px(10.)
- size.width;

window.with_absolute_element_offset(gpui::Point::new(x, y), |window| {
element.prepaint(window, cx)
});
controls.push(element);
}
}
Expand Down Expand Up @@ -7750,7 +7755,7 @@ impl Element for EditorElement {
editor.last_position_map = Some(position_map.clone())
});

let hunk_controls = self.layout_diff_hunk_controls(
let diff_hunk_controls = self.layout_diff_hunk_controls(
start_row..end_row,
&row_infos,
&text_hitbox,
Expand Down Expand Up @@ -7790,7 +7795,7 @@ impl Element for EditorElement {
visible_cursors,
selections,
inline_completion_popover,
diff_hunk_controls: hunk_controls,
diff_hunk_controls,
mouse_context_menu,
test_indicators,
code_actions_indicator,
Expand Down Expand Up @@ -9117,6 +9122,7 @@ mod tests {

fn diff_hunk_controls(
row: u32,
status: &DiffHunkStatus,
hunk_range: Range<Anchor>,
line_height: Pixels,
editor: &Entity<Editor>,
Expand All @@ -9133,62 +9139,66 @@ fn diff_hunk_controls(
.rounded_b_lg()
.bg(cx.theme().colors().editor_background)
.gap_1()
.when(status.secondary == DiffHunkSecondaryStatus::None, |el| {
el.child(
Button::new("unstage", "Unstage")
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in(
"Unstage Hunk",
&::git::ToggleStaged,
&focus_handle,
window,
cx,
)
}
})
.on_click({
let editor = editor.clone();
move |_event, _, cx| {
editor.update(cx, |editor, cx| {
editor.stage_or_unstage_diff_hunks(
false,
&[hunk_range.start..hunk_range.start],
cx,
);
});
}
}),
)
})
.when(status.secondary != DiffHunkSecondaryStatus::None, |el| {
el.child(
Button::new("stage", "Stage")
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in(
"Stage Hunk",
&::git::ToggleStaged,
&focus_handle,
window,
cx,
)
}
})
.on_click({
let editor = editor.clone();
move |_event, _, cx| {
editor.update(cx, |editor, cx| {
editor.stage_or_unstage_diff_hunks(
true,
&[hunk_range.start..hunk_range.start],
cx,
);
});
}
}),
)
})
.child(
IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
// .disabled(!has_multiple_hunks)
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, window, cx)
}
})
.on_click({
let editor = editor.clone();
move |_event, window, cx| {
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(window, cx);
let position = hunk_range.end.to_point(&snapshot.buffer_snapshot);
editor.go_to_hunk_after_position(&snapshot, position, window, cx);
editor.expand_selected_diff_hunks(cx);
});
}
}),
)
.child(
IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
// .disabled(!has_multiple_hunks)
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in(
"Previous Hunk",
&GoToPrevHunk,
&focus_handle,
window,
cx,
)
}
})
.on_click({
let editor = editor.clone();
move |_event, window, cx| {
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(window, cx);
let point = hunk_range.start.to_point(&snapshot.buffer_snapshot);
editor.go_to_hunk_before_position(&snapshot, point, window, cx);
editor.expand_selected_diff_hunks(cx);
});
}
}),
)
.child(
IconButton::new("discard", IconName::Undo)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
Button::new("discard", "Restore")
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {
Expand All @@ -9212,5 +9222,71 @@ fn diff_hunk_controls(
}
}),
)
.when(
!editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
|el| {
el.child(
IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
// .disabled(!has_multiple_hunks)
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in(
"Next Hunk",
&GoToHunk,
&focus_handle,
window,
cx,
)
}
})
.on_click({
let editor = editor.clone();
move |_event, window, cx| {
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(window, cx);
let position =
hunk_range.end.to_point(&snapshot.buffer_snapshot);
editor
.go_to_hunk_after_position(&snapshot, position, window, cx);
editor.expand_selected_diff_hunks(cx);
});
}
}),
)
.child(
IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
// .disabled(!has_multiple_hunks)
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {
Tooltip::for_action_in(
"Previous Hunk",
&GoToPrevHunk,
&focus_handle,
window,
cx,
)
}
})
.on_click({
let editor = editor.clone();
move |_event, window, cx| {
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(window, cx);
let point =
hunk_range.start.to_point(&snapshot.buffer_snapshot);
editor.go_to_hunk_before_position(&snapshot, point, window, cx);
editor.expand_selected_diff_hunks(cx);
});
}
}),
)
},
)
.into_any_element()
}
Loading

0 comments on commit 4871d3c

Please sign in to comment.