╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Commits.zig ──────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
661 662 // render the next budget of hunks for the displayed commit and append them. 663 // the TUI reopens the repo here; the wasm path has no repo and the web host 664 // drives "load more" via the GET endpoint instead, so the repo code is 665 // comptime-elided from the wasm build. 666 fn loadMore(self: *View, allocator: std.mem.Allocator, root_focus: *Focus) !void { 667 // the wasm build has no repo; it drives "load more" via the GET endpoint. 668 if (!builtin.cpu.arch.isWasm()) try self.loadMoreNative(allocator, root_focus); 669 } 670 671 fn loadMoreNative(self: *View, allocator: std.mem.Allocator, root_focus: *Focus) !void { 672 const io = self.session.io orelse return; 673 const repos_dir = self.session.repos_dir orelse return; 674 const sel = self.diffed_index orelse return; 675 const commit = self.data.commits[sel]; 676 if (commit.oid.len != xit.hash.hexLen(.sha1)) return; 677 678 const aa = self.session.page_arena.allocator(); 679 const gpa = self.session.page_arena.child_allocator; 680 const repo_path = try std.fs.path.join(aa, &.{ repos_dir, self.data.repo_dir_hex }); 681 var repo = rp.Repo(.xit, .{}).open(io, gpa, .{ .path = repo_path }) catch return; 682 defer repo.deinit(io, gpa); 683 684 var oid: [xit.hash.hexLen(.sha1)]u8 = undefined; 685 @memcpy(&oid, commit.oid); 686 const rendered = renderCommitDiff(io, gpa, aa, &repo, oid, self.shown_hunks, diff_budget) catch return; 687 688 const inner = self.diffInner(); 689 if (self.load_more_id) |id| { 690 if (inner.children.fetchOrderedRemove(id)) |kv| { 691 var w = kv.value.widget; 692 w.deinit(allocator); 693 } 694 self.load_more_id = null; 695 } 696 // focus the last already-shown hunk (the one just above where "load 697 // more" was). it's already in the focus tree from the last build, so the 698 // focus lands; the freshly-appended hunks aren't in the tree until the 699 // next build, so focusing one here would silently no-op. the user scrolls 700 // down into the new hunks from here. 701 const shown = inner.children.count(); // old hunk count ("load more" removed) 702 for (rendered.hunks) |hunk| try self.addHunkBox(allocator, inner, hunk.lines); 703 self.shown_hunks = rendered.shown_hunks; 704 self.has_more = rendered.has_more; 705 if (rendered.has_more) try self.addLoadMore(allocator, inner, commit.oid, rendered.shown_hunks); 706 if (shown > 0) try root_focus.setFocus(inner.children.keys()[shown - 1]); 707 } 708 709 pub fn clearGrid(self: *View) void { 710 self.box.clearGrid(); 711 } 712 713 pub fn getGrid(self: View) ?Grid { 714 return self.box.getGrid(); 715 } 716 717 pub fn getFocus(self: *View) *Focus { 718 return self.box.getFocus(); 719 } 720 721 // for the parent's "scroll up at the top jumps to the header" check. in the 722 // diff pane that means the first hunk is focused and the diff can't scroll up 723 // any further; otherwise up-arrow stays in the pane (scrolling or moving 724 // between hunks). when the list holds focus, report its selection directly. 725 pub fn getSelectedIndex(self: *View) ?usize { 726 if (self.diffActive()) { 727 const inner = self.diffInner(); 728 const cid = inner.getFocus().child_id orelse return 1; 729 const first_focused = inner.children.count() > 0 and cid == inner.children.keys()[0]; 730 return if (first_focused and self.diffScroll().y == 0) 0 else 1; 731 } 732 const lb = self.listBox(); 733 const cid = lb.getFocus().child_id orelse return null; 734 return lb.children.getIndex(cid); 735 } 736 }; 737 738 // the "a:" link for the commits page starting at `start_oid` within `identity`. 739 fn commitsPageLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, start_oid: []const u8) ![]const u8 { 740 const route = ui.RoutablePage.repoCommitsRoute(identity, start_oid) orelse return error.RouteTooLong; 741 const url = try route.urlAlloc(page_arena); 742 return std.fmt.allocPrint(page_arena.allocator(), "a:{s}", .{url}); 743 } 744