Skip to content

Commit

Permalink
add command and keybding to jump to next/prev hunk
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalkuthe committed Nov 20, 2022
1 parent b982b9c commit 39dab15
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 0 deletions.
4 changes: 4 additions & 0 deletions book/src/keymap.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaire
| `]t` | Go to previous test (**TS**) | `goto_prev_test` |
| `]p` | Go to next paragraph | `goto_next_paragraph` |
| `[p` | Go to previous paragraph | `goto_prev_paragraph` |
| `]g` | Go to next change | `goto_next_change` |
| `[g` | Go to previous change | `goto_prev_change` |
| `]G` | Go to first change | `goto_first_change` |
| `[G` | Go to last change | `goto_last_change` |
| `[Space` | Add newline above | `add_newline_above` |
| `]Space` | Add newline below | `add_newline_below` |

Expand Down
65 changes: 65 additions & 0 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub(crate) mod lsp;
pub(crate) mod typed;

pub use dap::*;
use helix_vcs::Hunk;
pub use lsp::*;
use tui::text::Spans;
pub use typed::*;
Expand Down Expand Up @@ -308,6 +309,10 @@ impl MappableCommand {
goto_last_diag, "Goto last diagnostic",
goto_next_diag, "Goto next diagnostic",
goto_prev_diag, "Goto previous diagnostic",
goto_next_change, "Goto next change",
goto_prev_change, "Goto previous change",
goto_first_change, "Goto first change",
goto_last_change, "Goto last change",
goto_line_start, "Goto line start",
goto_line_end, "Goto line end",
goto_next_buffer, "Goto next buffer",
Expand Down Expand Up @@ -2916,6 +2921,66 @@ fn goto_prev_diag(cx: &mut Context) {
goto_pos(editor, pos);
}

fn goto_first_change(cx: &mut Context) {
goto_first_hunk_impl(cx, false);
}

fn goto_last_change(cx: &mut Context) {
goto_first_hunk_impl(cx, true);
}

fn goto_first_hunk_impl(cx: &mut Context, reverse: bool) {
let editor = &mut cx.editor;
let (_, doc) = current!(editor);
if let Some(handle) = doc.diff_handle() {
let hunk = {
let hunks = handle.hunks();
let idx = if reverse {
hunks.len().saturating_sub(1)
} else {
0
};
hunks.nth_hunk(idx)
};
if hunk != Hunk::NONE {
let pos = doc.text().line_to_char(hunk.after.start as usize);
goto_pos(editor, pos)
}
}
}

fn goto_next_change(cx: &mut Context) {
goto_next_change_impl::<false>(cx)
}

fn goto_prev_change(cx: &mut Context) {
goto_next_change_impl::<true>(cx)
}

fn goto_next_change_impl<const REVERSE: bool>(cx: &mut Context) {
let editor = &mut cx.editor;
let (view, doc) = current!(editor);

let cursor_pos = doc
.selection(view.id)
.primary()
.cursor(doc.text().slice(..));

let cursor_line = doc.text().char_to_line(cursor_pos);

let next_changed_line = doc.diff_handle().and_then(|diff_handle| {
let hunks = diff_handle.hunks();
let hunk_idx = hunks.next_hunk::<REVERSE>(cursor_line as u32)?;
let line = hunks.nth_hunk(hunk_idx).after.start;
Some(line)
});

if let Some(next_change_line) = next_changed_line {
let pos = doc.text().line_to_char(next_change_line as usize);
goto_pos(cx.editor, pos)
}
}

pub mod insert {
use super::*;
pub type Hook = fn(&Rope, &Selection, char) -> Option<Transaction>;
Expand Down
4 changes: 4 additions & 0 deletions helix-term/src/keymap/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ pub fn default() -> HashMap<Mode, Keymap> {
"[" => { "Left bracket"
"d" => goto_prev_diag,
"D" => goto_first_diag,
"g" => goto_prev_change,
"G" => goto_first_change,
"f" => goto_prev_function,
"c" => goto_prev_class,
"a" => goto_prev_parameter,
Expand All @@ -111,6 +113,8 @@ pub fn default() -> HashMap<Mode, Keymap> {
"]" => { "Right bracket"
"d" => goto_next_diag,
"D" => goto_last_diag,
"g" => goto_next_change,
"G" => goto_last_change,
"f" => goto_next_function,
"c" => goto_next_class,
"a" => goto_next_parameter,
Expand Down
60 changes: 60 additions & 0 deletions helix-vcs/src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,64 @@ impl FileHunks<'_> {
None => Hunk::NONE,
}
}

pub fn len(&self) -> u32 {
self.hunks.len() as u32
}

fn next_hunk_impl<const INVERT: bool, const REVERSE: bool>(&self, line: u32) -> Option<u32> {
let hunk_range = |hunk: &Hunk| {
if INVERT {
hunk.before.clone()
} else {
hunk.after.clone()
}
};
let res = self.hunks.binary_search_by_key(&line, |hunk| {
let range = hunk_range(hunk);
if REVERSE {
range.end
} else {
range.start
}
});

if REVERSE {
match res {
// Search found a hunk that ends exactly at this line (so it does not include the current line).
// We can usually just return that hunk, howver a specical special case for empty hunk is necessary
// which represents a pure removal.
// Removals are technically empty but are still shown as single line hunks
// and as such we must jump to the previus hunk (if it exists) if we are already inside the removal
Ok(pos) if !hunk_range(&self.hunks[pos]).is_empty() => Some(pos as u32),

// No hunk ends exactly at this line, so the search returns
// the position where a hunk ending at this line should be inserted.
// That position before this one is exactly the position of the previous hunk
Err(0) | Ok(0) => None,
Err(pos) | Ok(pos) => Some(pos as u32 - 1),
}
} else {
match res {
// Search found a hunk that starts exactly at this line, return the next hunk if it exists.
Ok(pos) if pos + 1 == self.hunks.len() => None,
Ok(pos) => Some(pos as u32 + 1),

// No hunk starts exactly at this line, so the search returns
// the position where a hunk starting at this line should be inserted.
// That position is exactly the position of the next hunk or the end
// of the list if no such hunk exists
Err(pos) if pos == self.hunks.len() => None,
Err(pos) => Some(pos as u32),
}
}
}

pub fn next_hunk<const REVERSE: bool>(&self, line: u32) -> Option<u32> {
if self.inverted {
self.next_hunk_impl::<true, REVERSE>(line)
} else {
self.next_hunk_impl::<false, REVERSE>(line)
}
}
}

0 comments on commit 39dab15

Please sign in to comment.