╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │14 │ │15 // how many commits a page shows before a "next" link appears. │ │16 const page_size = 20; │ │17 -// how many diff hunks a commit shows per page; "load more" reveals another page.│ │17 +// how many diff hunks one window of a commit's diff shows; "next"/"previous" │ │18 +// move to the adjacent window. │ │19 const diff_page = 10; │ │20 │ │21 pub const Hunk = struct { │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │30 date: []const u8, // "YYYY-MM-DD" │ │31 message: []const u8, // first line only │ │32 hunks: []const Hunk, │ │32 - // how many hunks `hunks` covers; the resume cursor for "load more". │ │33 - shown_hunks: usize, │ │34 - // true when the diff was truncated at the budget and more hunks remain.│ │33 + // the hunk index this window starts at (0 = the first window). │ │34 + window_start: usize, │ │35 + // whether hunks exist before this window / after it. │ │36 + has_prev: bool, │ │37 has_more: bool, │ │38 }; │ │39 │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │51 event_id: *const [evt.event_id_size]u8, │ │52 identity: []const u8, │ │53 start_oid: []const u8, │ │52 - // how many hunks the selected commit (the first one, == start_oid) shows;│ │53 - // 0 means the default page. raised by "load more" via the url. │ │54 + // the hunk index the selected commit's (the first one, == start_oid) diff│ │55 + // window starts at; 0 is the first window. moved by "next"/"previous". │ │56 after: usize, │ │57 ) !Self { │ │58 const aa = arena.allocator(); │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │107 .date = try formatDate(aa, md.timestamp), │ │108 .message = try aa.dupe(u8, firstLine(md.message orelse "")),│ │109 .hunks = &.{}, │ │108 - .shown_hunks = 0, │ │110 + .window_start = 0, │ │111 + .has_prev = false, │ │112 .has_more = false, │ │113 }; │ │114 count += 1; │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │120 // renders from the snapshot), so gate it out of the wasm build. │ │121 if (!builtin.cpu.arch.isWasm()) { │ │122 for (buf[0..count], oids[0..count], 0..) |*commit, oid, i| { │ │120 - // the selected commit (the first, == start_oid) shows `after` hunks │ │121 - // when the url asks; the rest show one default page. │ │122 - const max_hunks = if (i == 0 and after > 0) after else diff_page; │ │123 - const rendered = renderCommitDiff(io, gpa, aa, &repo, oid, max_hunks) catch │ │124 - RenderedDiff{ .hunks = &.{}, .shown_hunks = 0, .has_more = false }; │ │123 + // the selected commit (the first, == start_oid) shows the window the │ │124 + // url asks for; the rest show their first window. │ │125 + const start = if (i == 0) after else 0; │ │126 + const rendered = renderCommitDiff(io, gpa, aa, &repo, oid, start, diff_page) catch│ │127 + RenderedDiff{ .hunks = &.{}, .has_prev = false, .has_more = false }; │ │128 commit.hunks = rendered.hunks; │ │126 - commit.shown_hunks = rendered.shown_hunks; │ │129 + commit.window_start = start; │ │130 + commit.has_prev = rendered.has_prev; │ │131 commit.has_more = rendered.has_more; │ │132 } │ │133 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────╮ │141 │ │142 const RenderedDiff = struct {│ │143 hunks: []const Hunk, │ │140 - shown_hunks: usize, │ │144 + has_prev: bool, │ │145 has_more: bool, │ │146 }; │ │147 │ ╰──────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │144 -// render a commit's diff against its first parent into `arena`-owned hunks,│ │145 -// emitting at most `max_hunks` hunks (has_more is set when more remain) │ │148 +// render the window [start, start+len) of a commit's diff against its first│ │149 +// parent into `arena`-owned hunks. has_prev/has_more flag adjacent windows.│ │150 fn renderCommitDiff( │ │151 io: std.Io, │ │152 gpa: std.mem.Allocator, │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮ │153 arena: std.mem.Allocator, │ │154 repo: *rp.Repo(.xit, .{}), │ │155 oid: [xit.hash.hexLen(.sha1)]u8, │ │152 - max_hunks: usize, │ │156 + start: usize, │ │157 + len: usize, │ │158 ) !RenderedDiff { │ │154 - const empty = RenderedDiff{ .hunks = &.{}, .shown_hunks = 0, .has_more = false }; │ │159 + const empty = RenderedDiff{ .hunks = &.{}, .has_prev = false, .has_more = false }; │ │160 │ │161 // load the commit so we can diff it against its first parent. │ │157 - var start = [_][xit.hash.hexLen(.sha1)]u8{oid}; │ │158 - var commit_iter = repo.log(io, gpa, start[0..1]) catch return empty; │ │162 + var start_oids = [_][xit.hash.hexLen(.sha1)]u8{oid}; │ │163 + var commit_iter = repo.log(io, gpa, start_oids[0..1]) catch return empty; │ │164 defer commit_iter.deinit(); │ │165 const commit_object = (commit_iter.next(gpa) catch return empty) orelse return empty;│ │166 defer commit_object.deinit(); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │173 var file_iter = repo.filePairs(io, gpa, .{ .tree = .{ .tree_diff = &tree_diff } }) catch return empty;│ │174 │ │175 var hunks: std.ArrayList(Hunk) = .empty; │ │176 + var index: usize = 0; // running hunk index across all files │ │177 var has_more = false; │ │178 │ │179 file_loop: while (file_iter.next() catch null) |pair_val| { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────╮ │189 var hunk = hunk_val; │ │190 defer hunk.deinit(gpa); │ │191 │ │186 - if (hunks.items.len >= max_hunks) { │ │192 + const i = index; │ │193 + index += 1; │ │194 + if (i < start) continue; // before this window│ │195 + if (hunks.items.len >= len) { │ │196 has_more = true; │ │197 break :file_loop; │ │198 } │ ╰───────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯