diff --git a/inc/hotkeys.dat b/inc/hotkeys.dat index 6a53122..19e04e7 100644 --- a/inc/hotkeys.dat +++ b/inc/hotkeys.dat @@ -18,5 +18,7 @@ DAT_CAT_START(GlobalCommand, GLB, global, "Global Commands") DAT_CMD(GLB_TextPageUp, OS::Key::PageUp, OS::KeyMods::None, "Page up text") DAT_CMD(GLB_TextBeginning, OS::Key::Home, OS::KeyMods::Ctrl, "Beginning of text") DAT_CMD(GLB_TextEnd, OS::Key::End, OS::KeyMods::Ctrl, "End of text") + DAT_CMD(GLB_TextNextChange, OS::Key::Down, OS::KeyMods::Alt, "Jump to next change") + DAT_CMD(GLB_TextPrevChange, OS::Key::Up, OS::KeyMods::Alt, "Jump to previous change") DAT_CMD(GLB_Close, OS::Key::W, OS::KeyMods::Ctrl, "Close Gap / close command") DAT_CAT_END(GlobalCommand, GLB) \ No newline at end of file diff --git a/src/diff-text.cpp b/src/diff-text.cpp index fbb73e0..c444206 100644 --- a/src/diff-text.cpp +++ b/src/diff-text.cpp @@ -160,6 +160,37 @@ namespace Diff { return window.idx_buf[circular_window_mask(idx, window.cap)]; } + + bool is_change(const DiffTextView* widget, int64_t idx) + { + const EditType type = widget->diffs.lines[idx].type; + return type != EditType::Eq and type != EditType::Skip; + } + + // Head of the change region after 'from', or 'from' if there isn't one. + int64_t next_change_idx(const DiffTextView* widget, int64_t from) + { + const int64_t size = static_cast(widget->diffs.size); + int64_t idx = from; + // Step out of the region we're in, then on to the head of the next one. + while (idx < size and is_change(widget, idx)) ++idx; + while (idx < size and not is_change(widget, idx)) ++idx; + // Nothing below us; stay put rather than running off to the end of the file. + return idx < size ? idx : from; + } + + // Head of the change region before 'from', or 'from' if there isn't one. + int64_t prev_change_idx(const DiffTextView* widget, int64_t from) + { + // 'from' indexes the scroll view, which outruns 'diffs' when there is no diff to show. + int64_t idx = std::min(from, static_cast(widget->diffs.size)) - 1; + // Step back to the region above us, then on to its head. + while (idx >= 0 and not is_change(widget, idx)) --idx; + // Nothing above us; stay put rather than running off to the start of the file. + if (idx < 0) return from; + while (idx >= 0 and is_change(widget, idx)) --idx; + return idx + 1; + } } // namespace [anon] // Creation. @@ -534,6 +565,24 @@ namespace Diff widget->scroll->scroll_to(off); resp.scroll_changed = true; } + + if (hotkey(*state, Hotkey::GLB_TextNextChange)) + { + UI::Widgets::IndexedScrollOffset off = widget->scroll->position(); + off.offset.y = 0.f; + off.idx = std::min(next_change_idx(widget, off.idx), scroll_size.v_size - 1); + widget->scroll->scroll_to(off); + resp.scroll_changed = true; + } + + if (hotkey(*state, Hotkey::GLB_TextPrevChange)) + { + UI::Widgets::IndexedScrollOffset off = widget->scroll->position(); + off.offset.y = 0.f; + off.idx = std::max(prev_change_idx(widget, off.idx), int64_t(0)); + widget->scroll->scroll_to(off); + resp.scroll_changed = true; + } } font_ctx.render_whitespace(Config::system_effects().render_whitespace); font_ctx.whitespace_color(colors.whitespace);