╭ changed ──────╮ │src/ui/Repo.zig│ ╰───────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────╮ │15 pub const Header = @import("./Repo/Header.zig"); │ │16 pub const Files = @import("./Repo/Files.zig"); │ │17 pub const Commits = @import("./Repo/Commits.zig");│ │18 +pub const Diff = @import("./Repo/Diff.zig"); │ │19 pub const Refs = @import("./Repo/Refs.zig"); │ │20 pub const Issues = @import("./Repo/Issues.zig"); │ │21 pub const Patches = @import("./Repo/Patches.zig");│ ╰──────────────────────────────────────────────────────╯ ╭──────────────────────────╮ │29 header: Header, │ │30 repo: evt.Repo.Record,│ │31 files: Files, │ │31 -commits: Commits, │ │32 +changes: Changes, │ │33 refs: Refs, │ │34 issues: Issues, │ │35 patches: Patches, │ ╰──────────────────────────╯ ╭────────────────────────────────────────╮ │41 │ │42 const Self = @This(); │ │43 │ │44 +const Changes = union(enum) { │ │45 + commits: Commits, │ │46 + diff: Diff, │ │47 +}; │ │48 + │ │49 pub fn init( │ │50 arena: *std.heap.ArenaAllocator,│ │51 session: *ui.Session, │ ╰────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │54 const DB = evt.AdminDB; │ │55 const hash_kind = evt.admin_repo_opts.hash; │ │56 │ │51 - // every repo route stores its identity as "owner/name" (or elides it in │ │52 - // local mode); files and commits carry their remaining fields directly. │ │57 + // every repo route stores its identity as "owner/name" (or elides it in local mode) │ │58 const name_str = route.repoIdentity() orelse return error.UnexpectedRoute; │ │59 const repo_identity = ui.RoutablePage.RepoIdentity.parse(name_str) orelse return error.NotFound;│ │60 const location = ui.RoutablePage.RepoLocation{ .repo = repo_identity.identity }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │56 - // the files and commits tabs share one ref/oid: whichever the incoming route│ │57 - // names (it rides on the route's target tab), or the default branch when │ │58 - // neither tab is targeted. building both views at it keeps switching tabs │ │59 - // (even by key, without a reload) on the same ref. a null ref means the │ │60 - // default branch (Files/Commits.init resolve it). the directory and the diff│ │61 - // window only apply to their own tab. │ │61 + // files, commits and diff share the requested ref, or the default branch. │ │62 + // directories and hunk windows only apply to their own tab. │ │63 const requested_ref_or_oid: ?ui.RoutablePage.RefOrOid = switch (route) { │ │64 .repo_files => |f| f.ref_kind, │ │65 .repo_commits => |c| c.ref_or_oid, │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────╮ │66 + .repo_diff => |d| d.ref_or_oid, │ │67 else => null, │ │68 }; │ │69 const requested_ref_value: []const u8 = switch (route) {│ ╰────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────╮ │70 .repo_files => |*f| f.ref_value.slice(), │ │71 .repo_commits => |*c| c.value.slice(), │ │72 + .repo_diff => |*d| d.value.slice(), │ │73 else => "", │ │74 }; │ │75 + const patchrev_id: []const u8 = switch (route) {│ │76 + .repo_files => |*f| f.patchrev_id.slice(), │ │77 + .repo_diff => |*d| d.patchrev_id.slice(), │ │78 + else => "", │ │79 + }; │ │80 const files_dir = switch (route) { │ │81 .repo_files => |*f| f.path.slice(), │ │82 else => "", │ ╰────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │226 } │ │227 } │ │228 │ │221 - // open the on-disk repo once and read every tab's data from it. files and │ │222 - // commits resolve the same requested ref (the default branch when the │ │223 - // route named none), so they end up viewing the same one. no filesystem │ │224 - // (wasm), nowhere to look, or a failed open: empty tabs. │ │225 - const files, const commits, const refs, var issues, var patches, var discussions, const events = blk: {│ │229 + // open the repo once for every tab. files and changes share a ref or revision. │ │230 + // no filesystem (wasm), nowhere to look, or a failed open: empty tabs. │ │231 + const files, const changes, const refs, var issues, var patches, var discussions, const events = blk: {│ │232 read: { │ │233 const io = session.io orelse break :read; │ │234 const src = source orelse break :read; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │244 try evt.consume(.local, .repo, repo_kind, opened.self_repo_opts, io, gpa, opened, evt.events_ref, &.{}); │ │245 try pch.refreshBranches(.local, repo_kind, opened.self_repo_opts, io, gpa, opened, null, null); │ │246 } │ │241 - const files_data = try Files.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, location, requested_ref_or_oid, requested_ref_value, files_dir, files_line); │ │247 + const files_data = if (patchrev_id.len != 0) │ │248 + try Files.initPatchRev(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, location, patchrev_id, files_dir, files_line) │ │249 + else │ │250 + try Files.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, location, requested_ref_or_oid, requested_ref_value, files_dir, files_line); │ │251 + const changes_data: Changes = if (route == .repo_diff) │ │252 + .{ .diff = try Diff.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, route.repo_diff) } │ │253 + else if (patchrev_id.len != 0) diff: { │ │254 + const diff_route = ui.RoutablePage.repoPatchRevDiffRoute(repo_identity.identity, patchrev_id, 0, "") orelse return error.RouteTooLong; │ │255 + break :diff .{ .diff = try Diff.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, diff_route.repo_diff) }; │ │256 + } else .{ .commits = try Commits.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, session.haxy_moment, location, requested_ref_or_oid, requested_ref_value, commits_content, commits_base_oid) };│ │257 const target_branch = if (files_data.ref_or_oid == .branch) files_data.ref_or_oid_value else ""; │ │258 break :blk .{ │ │259 files_data, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │245 - try Commits.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, session.haxy_moment, location, requested_ref_or_oid, requested_ref_value, commits_content, commits_base_oid), │ │260 + changes_data, │ │261 try Refs.init(repo_kind, opened.self_repo_opts, arena, opened, io, gpa, repo_identity.identity, refs_kind, refs_from), │ │262 try Issues.init(repo_kind, opened.self_repo_opts, arena, opened, io, session.haxy_moment, repo_identity.identity, issues_tag, issues_selected, issues_comment, issues_comments_start, issues_theirs, issues_view), │ │263 try Patches.init(repo_kind, opened.self_repo_opts, arena, opened, io, session.haxy_moment, session, repo_id_maybe, repo_identity.identity, target_branch, patches_tag, patches_selected, patches_comment, patches_comments_start, patches_theirs, patches_view),│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯