╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │291 const diff_index: usize = 1; │ │292 const list_max_width: usize = 40; │ │293 const diff_min_width: usize = 40; │ │294 - // how many rows a page up/down jumps, and the scroll-wheel step. │ │295 - const page_rows = 10; │ │294 │ │295 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │296 var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .horiz });│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │544 .arrow_right => try self.focusDiff(root_focus), │ │545 .arrow_down => try self.moveSelection(root_focus, 1), │ │546 .arrow_up => try self.moveSelection(root_focus, -1), │ │549 - .page_down => try self.moveSelection(root_focus, page_rows), │ │550 - .page_up => try self.moveSelection(root_focus, -page_rows), │ │547 + .page_down => try self.moveSelection(root_focus, 10), │ │548 + .page_up => try self.moveSelection(root_focus, -10), │ │549 + .end => try self.moveSelection(root_focus, @intCast(self.listBox().children.count())), │ │550 + .home => try self.moveSelection(root_focus, -@as(isize, @intCast(self.listBox().children.count()))),│ │551 .mouse => |mouse| switch (mouse.action) { │ │552 .scroll => |dir| try self.moveSelection(root_focus, if (dir == .up) -1 else 1), │ │553 else => {}, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │572 }, │ │573 .arrow_up => try self.moveDiff(root_focus, -1), │ │574 .arrow_down => try self.moveDiff(root_focus, 1), │ │575 - .page_up => try self.pageDiff(root_focus, -page_rows), │ │576 - .page_down => try self.pageDiff(root_focus, page_rows), │ │575 + .page_up => try self.pageDiff(root_focus, -10), │ │576 + .page_down => try self.pageDiff(root_focus, 10), │ │577 + .home => try self.jumpDiff(root_focus, false), │ │578 + .end => try self.jumpDiff(root_focus, true), │ │579 // Enter / a click on a "previous"/"next" row follow its "a:" link in │ │580 // the host (it reloads the adjacent window), so they don't reach here.│ │581 .mouse => |mouse| switch (mouse.action) { │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │586 } │ │587 } │ │588 │ │587 - // move focus one hunk in `dir` (+1 down, -1 up). on the web the browser owns│ │588 - // scrolling, so just step focus to the adjacent hunk and let scrollToFocus │ │589 - // bring it into view. on the terminal, if the adjacent hunk is already │ │590 - // visible just move focus; otherwise scroll one line toward it (focusing it │ │591 - // once it appears) so scrolling never skips past an on-screen hunk. │ │592 - fn moveDiff(self: *View, root_focus: *Focus, dir: isize) !void { │ │589 + // move one step in `delta` (+ down, - up). in the terminal, `delta` is a │ │590 + // line count unless there is a new hunk visible (in which case it is a │ │591 + // hunk count). on the web `delta` is always a hunk count. │ │592 + fn moveDiff(self: *View, root_focus: *Focus, delta: isize) !void { │ │593 const inner = self.diffInner(); │ │594 const keys = inner.children.keys(); │ │595 if (keys.len == 0) return; │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │596 const cur: usize = if (root_focus.grandchild_id) |g| (inner.children.getIndex(g) orelse 0) else 0;│ │597 - const target = @as(isize, @intCast(cur)) + dir; │ │597 + const target = @as(isize, @intCast(cur)) + delta; │ │598 const in_range = target >= 0 and target < @as(isize, @intCast(keys.len)); │ │599 │ │600 - if (!self.session.is_terminal) { │ │601 - if (in_range) try root_focus.setFocus(keys[@intCast(target)]); │ │602 - return; │ │603 - } │ │600 + if (self.session.is_terminal) { │ │601 + if (in_range and self.hunkVisible(@intCast(target))) { │ │602 + try root_focus.setFocus(keys[@intCast(target)]); │ │603 + return; │ │604 + } │ │605 │ │605 - if (in_range and self.hunkVisible(@intCast(target))) { │ │606 - try root_focus.setFocus(keys[@intCast(target)]); │ │607 - return; │ │606 + const sc = self.diffScroll(); │ │607 + sc.y += delta * 5; // magnify because it's a line count │ │608 + self.clampDiffScroll(); │ │609 + if (in_range and self.hunkVisible(@intCast(target))) { │ │610 + try root_focus.setFocus(keys[@intCast(target)]); │ │611 + } │ │612 + } else { │ │613 + if (in_range) { │ │614 + try root_focus.setFocus(keys[@intCast(target)]); │ │615 + } │ │616 } │ │609 - │ │610 - const sc = self.diffScroll(); │ │611 - sc.y += dir; │ │612 - self.clampDiffScroll(); │ │613 - if (in_range and self.hunkVisible(@intCast(target))) │ │614 - try root_focus.setFocus(keys[@intCast(target)]); │ │617 } │ │618 │ │619 // page a fixed number of lines, then focus the leading visible hunk │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │618 - // (bottom-most when paging down, top-most when paging up). on the web the│ │619 - // browser scrolls, so jump focus by a page's worth of hunks instead. │ │620 + // (bottom-most when paging down, top-most when paging up). in the │ │621 + // terminal, `delta` is a line count, and on the web it's a hunk count. │ │622 fn pageDiff(self: *View, root_focus: *Focus, delta: isize) !void { │ │621 - if (!self.session.is_terminal) { │ │623 + if (self.session.is_terminal) { │ │624 + const sc = self.diffScroll(); │ │625 + sc.y += delta * 5; // magnify because it's a line count │ │626 + self.clampDiffScroll(); │ │627 + try self.focusVisible(root_focus, delta > 0); │ │628 + } else { │ │629 const inner = self.diffInner(); │ │630 const keys = inner.children.keys(); │ │631 if (keys.len == 0) return; │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │632 const cur: isize = if (root_focus.grandchild_id) |g| @intCast(inner.children.getIndex(g) orelse 0) else 0;│ │633 const target: usize = @intCast(std.math.clamp(cur + delta, 0, @as(isize, @intCast(keys.len - 1)))); │ │634 try root_focus.setFocus(keys[target]); │ │628 - return; │ │635 + } │ │636 + } │ │637 + │ │638 + // jump to the first or last hunk. on the web the browser scrolls to the │ │639 + // focused hunk; on the terminal pin the scroll to the top/bottom too. │ │640 + fn jumpDiff(self: *View, root_focus: *Focus, to_end: bool) !void { │ │641 + const inner = self.diffInner(); │ │642 + const keys = inner.children.keys(); │ │643 + if (keys.len == 0) return; │ │644 + if (self.session.is_terminal) { │ │645 + const sc = self.diffScroll(); │ │646 + sc.y = if (to_end) std.math.maxInt(isize) else 0; │ │647 + self.clampDiffScroll(); │ │648 } │ │630 - const sc = self.diffScroll(); │ │631 - sc.y += delta; │ │632 - self.clampDiffScroll(); │ │633 - try self.focusVisible(root_focus, delta > 0); │ │649 + try root_focus.setFocus(if (to_end) keys[keys.len - 1] else keys[0]); │ │650 } │ │651 │ │652 // whether hunk `index` is at least partly within the diff viewport, per the │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯