Skip to content

Commit

Permalink
Reset all changes overlapped by primary sel in ':reset-diff-change'
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
the-mikedavis committed Apr 6, 2024
1 parent 97afd67 commit c5c5597
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 23 deletions.
46 changes: 23 additions & 23 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

Expand Down
18 changes: 18 additions & 0 deletions helix-vcs/src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = &Hunk> {
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<u32> {
let hunk_range = if self.inverted {
|hunk: &Hunk| hunk.before.clone()
Expand Down

0 comments on commit c5c5597

Please sign in to comment.