From c5c55978794036437402febea8b259f9e544f615 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 4 Apr 2024 12:26:39 -0400 Subject: [PATCH] Reset all changes overlapped by primary sel in ':reset-diff-change' This is useful for resetting multiple changes at once. For example you might use 'maf' or even '%' to select a larger region and reset all changes within. The original behavior of resetting the change on the current line is retained when the primary selection is 1-width since we look for chunks in the line range of the primary selection. --- helix-term/src/commands/typed.rs | 46 ++++++++++++++++---------------- helix-vcs/src/diff.rs | 18 +++++++++++++ 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 5d7057da6b39b..92c4ce1b22bbc 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2304,37 +2304,37 @@ fn reset_diff_change( let diff = handle.load(); let doc_text = doc.text().slice(..); - let line = doc.selection(view.id).primary().cursor_line(doc_text); - - let Some(hunk_idx) = diff.hunk_at(line as u32, true) else { - bail!("There is no change at the cursor") - }; - let hunk = diff.nth_hunk(hunk_idx); + let (start_line, end_line) = doc.selection(view.id).primary().line_range(doc_text); let diff_base = diff.diff_base(); - let before_start = diff_base.line_to_char(hunk.before.start as usize); - let before_end = diff_base.line_to_char(hunk.before.end as usize); - let text: Tendril = diff - .diff_base() - .slice(before_start..before_end) - .chunks() - .collect(); - let anchor = doc_text.line_to_char(hunk.after.start as usize); + let mut changes = 0; + let transaction = Transaction::change( doc.text(), - [( - anchor, - doc_text.line_to_char(hunk.after.end as usize), - (!text.is_empty()).then_some(text), - )] - .into_iter(), + diff.hunks_in_range(start_line as u32, end_line as u32) + .map(|hunk| { + changes += 1; + let start = diff_base.line_to_char(hunk.before.start as usize); + let end = diff_base.line_to_char(hunk.before.end as usize); + let text: Tendril = diff_base.slice(start..end).chunks().collect(); + ( + doc_text.line_to_char(hunk.after.start as usize), + doc_text.line_to_char(hunk.after.end as usize), + Some(text).filter(|t| !t.is_empty()), + ) + }), ); + if changes == 0 { + bail!("There is no change under the primary selection"); + } + drop(diff); // make borrow check happy doc.apply(&transaction, view.id); - // select inserted text - let text_len = before_end - before_start; - doc.set_selection(view.id, Selection::single(anchor, anchor + text_len)); doc.append_changes_to_history(view); view.ensure_cursor_in_view(doc, scrolloff); + cx.editor.set_status(format!( + "Reset {changes} change{}", + if changes == 1 { "" } else { "s" } + )); Ok(()) } diff --git a/helix-vcs/src/diff.rs b/helix-vcs/src/diff.rs index c72deb7ea7f37..eee77b8b33685 100644 --- a/helix-vcs/src/diff.rs +++ b/helix-vcs/src/diff.rs @@ -259,6 +259,24 @@ impl Diff<'_> { } } + /// Iterates over all hunks that overlap with the given line range. + pub fn hunks_in_range(&self, start_line: u32, end_line: u32) -> impl Iterator { + let hunk_range = if self.inverted { + |hunk: &Hunk| hunk.before.clone() + } else { + |hunk: &Hunk| hunk.after.clone() + }; + + let first = self + .diff + .hunks + .partition_point(|hunk| hunk_range(hunk).end < start_line); + + self.diff.hunks[first..] + .iter() + .take_while(move |hunk| hunk_range(hunk).start <= end_line) + } + pub fn hunk_at(&self, line: u32, include_removal: bool) -> Option { let hunk_range = if self.inverted { |hunk: &Hunk| hunk.before.clone()