@@ -14,9 +14,9 @@ use ui::{
use util::RangeExt;
use crate::{
- editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, BlockDisposition,
- BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow, DisplaySnapshot,
- Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk, RevertFile,
+ editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyDiffHunk,
+ BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow,
+ DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk, RevertFile,
RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
};
@@ -238,19 +238,14 @@ impl Editor {
cx: &mut ViewContext<'_, Editor>,
) -> Option<()> {
let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
- let multi_buffer_row_range = hunk
- .multi_buffer_range
- .start
- .to_point(&multi_buffer_snapshot)
- ..hunk.multi_buffer_range.end.to_point(&multi_buffer_snapshot);
- let hunk_start = hunk.multi_buffer_range.start;
- let hunk_end = hunk.multi_buffer_range.end;
+ let hunk_range = hunk.multi_buffer_range.clone();
+ let hunk_point_range = hunk_range.to_point(&multi_buffer_snapshot);
let buffer = self.buffer().clone();
let snapshot = self.snapshot(cx);
let (diff_base_buffer, deleted_text_lines) = buffer.update(cx, |buffer, cx| {
- let hunk = buffer_diff_hunk(&snapshot.buffer_snapshot, multi_buffer_row_range.clone())?;
- let mut buffer_ranges = buffer.range_to_buffer_ranges(multi_buffer_row_range, cx);
+ let hunk = buffer_diff_hunk(&snapshot.buffer_snapshot, hunk_point_range.clone())?;
+ let mut buffer_ranges = buffer.range_to_buffer_ranges(hunk_point_range, cx);
if buffer_ranges.len() == 1 {
let (buffer, _, _) = buffer_ranges.pop()?;
let diff_base_buffer = diff_base_buffer
@@ -275,7 +270,7 @@ impl Editor {
probe
.hunk_range
.start
- .cmp(&hunk_start, &multi_buffer_snapshot)
+ .cmp(&hunk_range.start, &multi_buffer_snapshot)
}) {
Ok(_already_present) => return None,
Err(ix) => ix,
@@ -295,7 +290,7 @@ impl Editor {
}
DiffHunkStatus::Added => {
self.highlight_rows::<DiffRowHighlight>(
- hunk_start..hunk_end,
+ hunk_range.clone(),
added_hunk_color(cx),
false,
cx,
@@ -304,7 +299,7 @@ impl Editor {
}
DiffHunkStatus::Modified => {
self.highlight_rows::<DiffRowHighlight>(
- hunk_start..hunk_end,
+ hunk_range.clone(),
added_hunk_color(cx),
false,
cx,
@@ -323,7 +318,7 @@ impl Editor {
block_insert_index,
ExpandedHunk {
blocks,
- hunk_range: hunk_start..hunk_end,
+ hunk_range,
status: hunk.status,
folded: false,
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
@@ -333,11 +328,47 @@ impl Editor {
Some(())
}
+ fn apply_changes_in_range(
+ &mut self,
+ range: Range<Anchor>,
+ cx: &mut ViewContext<'_, Editor>,
+ ) -> Option<()> {
+ let (buffer, range, _) = self
+ .buffer
+ .read(cx)
+ .range_to_buffer_ranges(range, cx)
+ .into_iter()
+ .next()?;
+
+ buffer.update(cx, |branch_buffer, cx| {
+ branch_buffer.merge_into_base(Some(range), cx);
+ });
+
+ None
+ }
+
+ pub(crate) fn apply_all_changes(&self, cx: &mut ViewContext<Self>) {
+ let buffers = self.buffer.read(cx).all_buffers();
+ for branch_buffer in buffers {
+ branch_buffer.update(cx, |branch_buffer, cx| {
+ branch_buffer.merge_into_base(None, cx);
+ });
+ }
+ }
+
fn hunk_header_block(
&self,
hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Editor>,
) -> BlockProperties<Anchor> {
+ let is_branch_buffer = self
+ .buffer
+ .read(cx)
+ .point_to_buffer_offset(hunk.multi_buffer_range.start, cx)
+ .map_or(false, |(buffer, _, _)| {
+ buffer.read(cx).diff_base_buffer().is_some()
+ });
+
let border_color = cx.theme().colors().border_variant;
let gutter_color = match hunk.status {
DiffHunkStatus::Added => cx.theme().status().created,
@@ -388,6 +419,10 @@ impl Editor {
.pr_6()
.size_full()
.justify_between()
+ .border_t_1()
+ .pl_6()
+ .pr_6()
+ .border_color(border_color)
.child(
h_flex()
.gap_1()
@@ -411,43 +446,10 @@ impl Editor {
let hunk = hunk.clone();
move |_event, cx| {
editor.update(cx, |editor, cx| {
- let snapshot = editor.snapshot(cx);
- let position = hunk
- .multi_buffer_range
- .end
- .to_point(
- &snapshot.buffer_snapshot,
- );
- if let Some(hunk) = editor
- .go_to_hunk_after_position(
- &snapshot, position, cx,
- )
- {
- let multi_buffer_start = snapshot
- .buffer_snapshot
- .anchor_before(Point::new(
- hunk.row_range.start.0,
- 0,
- ));
- let multi_buffer_end = snapshot
- .buffer_snapshot
- .anchor_after(Point::new(
- hunk.row_range.end.0,
- 0,
- ));
- editor.expand_diff_hunk(
- None,
- &HoveredHunk {
- multi_buffer_range:
- multi_buffer_start
- ..multi_buffer_end,
- status: hunk_status(&hunk),
- diff_base_byte_range: hunk
- .diff_base_byte_range,
- },
- cx,
- );
- }
+ editor.go_to_subsequent_hunk(
+ hunk.multi_buffer_range.end,
+ cx,
+ );
});
}
}),
@@ -472,43 +474,10 @@ impl Editor {
let hunk = hunk.clone();
move |_event, cx| {
editor.update(cx, |editor, cx| {
- let snapshot = editor.snapshot(cx);
- let position = hunk
- .multi_buffer_range
- .start
- .to_point(
- &snapshot.buffer_snapshot,
- );
- let hunk = editor
- .go_to_hunk_before_position(
- &snapshot, position, cx,
- );
- if let Some(hunk) = hunk {
- let multi_buffer_start = snapshot
- .buffer_snapshot
- .anchor_before(Point::new(
- hunk.row_range.start.0,
- 0,
- ));
- let multi_buffer_end = snapshot
- .buffer_snapshot
- .anchor_after(Point::new(
- hunk.row_range.end.0,
- 0,
- ));
- editor.expand_diff_hunk(
- None,
- &HoveredHunk {
- multi_buffer_range:
- multi_buffer_start
- ..multi_buffer_end,
- status: hunk_status(&hunk),
- diff_base_byte_range: hunk
- .diff_base_byte_range,
- },
- cx,
- );
- }
+ editor.go_to_preceding_hunk(
+ hunk.multi_buffer_range.start,
+ cx,
+ );
});
}
}),
@@ -558,6 +527,36 @@ impl Editor {
}
}),
)
+ .when(is_branch_buffer, |this| {
+ this.child(
+ IconButton::new("apply", IconName::Check)
+ .shape(IconButtonShape::Square)
+ .icon_size(IconSize::Small)
+ .tooltip({
+ let focus_handle = editor.focus_handle(cx);
+ move |cx| {
+ Tooltip::for_action_in(
+ "Apply Hunk",
+ &ApplyDiffHunk,
+ &focus_handle,
+ cx,
+ )
+ }
+ })
+ .on_click({
+ let editor = editor.clone();
+ let hunk = hunk.clone();
+ move |_event, cx| {
+ editor.update(cx, |editor, cx| {
+ editor.apply_changes_in_range(
+ hunk.multi_buffer_range.clone(),
+ cx,
+ );
+ });
+ }
+ }),
+ )
+ })
.child({
let focus = editor.focus_handle(cx);
PopoverMenu::new("hunk-controls-dropdown")
@@ -597,31 +596,29 @@ impl Editor {
}),
)
.child(
- div().child(
- IconButton::new("collapse", IconName::Close)
- .shape(IconButtonShape::Square)
- .icon_size(IconSize::Small)
- .tooltip({
- let focus_handle = editor.focus_handle(cx);
- move |cx| {
- Tooltip::for_action_in(
- "Collapse Hunk",
- &ToggleHunkDiff,
- &focus_handle,
- cx,
- )
- }
- })
- .on_click({
- let editor = editor.clone();
- let hunk = hunk.clone();
- move |_event, cx| {
- editor.update(cx, |editor, cx| {
- editor.toggle_hovered_hunk(&hunk, cx);
- });
- }
- }),
- ),
+ IconButton::new("collapse", IconName::Close)
+ .shape(IconButtonShape::Square)
+ .icon_size(IconSize::Small)
+ .tooltip({
+ let focus_handle = editor.focus_handle(cx);
+ move |cx| {
+ Tooltip::for_action_in(
+ "Collapse Hunk",
+ &ToggleHunkDiff,
+ &focus_handle,
+ cx,
+ )
+ }
+ })
+ .on_click({
+ let editor = editor.clone();
+ let hunk = hunk.clone();
+ move |_event, cx| {
+ editor.update(cx, |editor, cx| {
+ editor.toggle_hovered_hunk(&hunk, cx);
+ });
+ }
+ }),
),
)
.into_any_element()
@@ -876,6 +873,51 @@ impl Editor {
}
})
}
+
+ fn go_to_subsequent_hunk(&mut self, position: Anchor, cx: &mut ViewContext<Self>) {
+ let snapshot = self.snapshot(cx);
+ let position = position.to_point(&snapshot.buffer_snapshot);
+ if let Some(hunk) = self.go_to_hunk_after_position(&snapshot, position, cx) {
+ let multi_buffer_start = snapshot
+ .buffer_snapshot
+ .anchor_before(Point::new(hunk.row_range.start.0, 0));
+ let multi_buffer_end = snapshot
+ .buffer_snapshot
+ .anchor_after(Point::new(hunk.row_range.end.0, 0));
+ self.expand_diff_hunk(
+ None,
+ &HoveredHunk {
+ multi_buffer_range: multi_buffer_start..multi_buffer_end,
+ status: hunk_status(&hunk),
+ diff_base_byte_range: hunk.diff_base_byte_range,
+ },
+ cx,
+ );
+ }
+ }
+
+ fn go_to_preceding_hunk(&mut self, position: Anchor, cx: &mut ViewContext<Self>) {
+ let snapshot = self.snapshot(cx);
+ let position = position.to_point(&snapshot.buffer_snapshot);
+ let hunk = self.go_to_hunk_before_position(&snapshot, position, cx);
+ if let Some(hunk) = hunk {
+ let multi_buffer_start = snapshot
+ .buffer_snapshot
+ .anchor_before(Point::new(hunk.row_range.start.0, 0));
+ let multi_buffer_end = snapshot
+ .buffer_snapshot
+ .anchor_after(Point::new(hunk.row_range.end.0, 0));
+ self.expand_diff_hunk(
+ None,
+ &HoveredHunk {
+ multi_buffer_range: multi_buffer_start..multi_buffer_end,
+ status: hunk_status(&hunk),
+ diff_base_byte_range: hunk.diff_base_byte_range,
+ },
+ cx,
+ );
+ }
+ }
}
fn to_diff_hunk(
@@ -18,7 +18,7 @@ pub struct ProposedChangesEditor {
editor: View<Editor>,
_subscriptions: Vec<Subscription>,
_recalculate_diffs_task: Task<Option<()>>,
- recalculate_diffs_tx: mpsc::UnboundedSender<Model<Buffer>>,
+ recalculate_diffs_tx: mpsc::UnboundedSender<RecalculateDiff>,
}
pub struct ProposedChangesBuffer<T> {
@@ -30,6 +30,11 @@ pub struct ProposedChangesEditorToolbar {
current_editor: Option<View<ProposedChangesEditor>>,
}
+struct RecalculateDiff {
+ buffer: Model<Buffer>,
+ debounce: bool,
+}
+
impl ProposedChangesEditor {
pub fn new<T: ToOffset>(
buffers: Vec<ProposedChangesBuffer<T>>,
@@ -63,16 +68,18 @@ impl ProposedChangesEditor {
recalculate_diffs_tx,
_recalculate_diffs_task: cx.spawn(|_, mut cx| async move {
let mut buffers_to_diff = HashSet::default();
- while let Some(buffer) = recalculate_diffs_rx.next().await {
- buffers_to_diff.insert(buffer);
+ while let Some(mut recalculate_diff) = recalculate_diffs_rx.next().await {
+ buffers_to_diff.insert(recalculate_diff.buffer);
- loop {
+ while recalculate_diff.debounce {
cx.background_executor()
.timer(Duration::from_millis(250))
.await;
let mut had_further_changes = false;
- while let Ok(next_buffer) = recalculate_diffs_rx.try_next() {
- buffers_to_diff.insert(next_buffer?);
+ while let Ok(next_recalculate_diff) = recalculate_diffs_rx.try_next() {
+ let next_recalculate_diff = next_recalculate_diff?;
+ recalculate_diff.debounce &= next_recalculate_diff.debounce;
+ buffers_to_diff.insert(next_recalculate_diff.buffer);
had_further_changes = true;
}
if !had_further_changes {
@@ -99,19 +106,24 @@ impl ProposedChangesEditor {
event: &BufferEvent,
_cx: &mut ViewContext<Self>,
) {
- if let BufferEvent::Edited = event {
- self.recalculate_diffs_tx.unbounded_send(buffer).ok();
- }
- }
-
- fn apply_all_changes(&self, cx: &mut ViewContext<Self>) {
- let buffers = self.editor.read(cx).buffer.read(cx).all_buffers();
- for branch_buffer in buffers {
- if let Some(base_buffer) = branch_buffer.read(cx).diff_base_buffer() {
- base_buffer.update(cx, |base_buffer, cx| {
- base_buffer.merge(&branch_buffer, None, cx)
- });
+ match event {
+ BufferEvent::Operation { .. } => {
+ self.recalculate_diffs_tx
+ .unbounded_send(RecalculateDiff {
+ buffer,
+ debounce: true,
+ })
+ .ok();
+ }
+ BufferEvent::DiffBaseChanged => {
+ self.recalculate_diffs_tx
+ .unbounded_send(RecalculateDiff {
+ buffer,
+ debounce: false,
+ })
+ .ok();
}
+ _ => (),
}
}
}
@@ -208,7 +220,9 @@ impl Render for ProposedChangesEditorToolbar {
Button::new("apply-changes", "Apply All").on_click(move |_, cx| {
if let Some(editor) = &editor {
editor.update(cx, |editor, cx| {
- editor.apply_all_changes(cx);
+ editor.editor.update(cx, |editor, cx| {
+ editor.apply_all_changes(cx);
+ })
});
}
})