╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────╮ │182 const repo_segment = "/repo/"; │ │183 const files_seg = "files"; │ │184 const commits_seg = "commits"; │ │185 + // ends a commits route naming its commit's full message │ │186 + const message_seg = "message"; │ │187 // the `Params` key spellings the url emitters use │ │188 const tag_filter_seg = @tagName(Params.ParamKey.tag) ++ ":";│ │189 const start_seg = @tagName(Params.ParamKey.start) ++ ":"; │ ╰─────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │251 name: Array(repo_identity_max_len), │ │252 ref_or_oid: ?RefOrOid = null, │ │253 value: Array(ref_route_max_len) = .{}, │ │252 - start: usize = 0, │ │253 - // the file the diff pane is filtered to ("" = every file). │ │254 - path: Array(repo_route_max_len) = .{}, │ │254 + // what the pane shows for the commit the log walks from. │ │255 + content: Content = .{ .diff = .{} }, │ │256 + │ │257 + // a diff window and the message are alternatives, so the window's │ │258 + // params can't be named alongside the message. │ │259 + pub const Content = union(enum) { │ │260 + diff: struct { │ │261 + // the hunk index the window starts at. │ │262 + start: usize = 0, │ │263 + // the file the diff is filtered to ("" = every file). │ │264 + path: Array(repo_route_max_len) = .{}, │ │265 + }, │ │266 + message, │ │267 + }; │ │268 }; │ │269 │ │270 // build a `.repo_files` route (a null ref_kind = the bare default-branch│ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │291 .name = Array(repo_identity_max_len).from(identity) orelse return null, │ │292 .ref_or_oid = ref_or_oid, │ │293 .value = Array(ref_route_max_len).from(value) orelse return null, │ │281 - .start = start, │ │282 - .path = Array(repo_route_max_len).from(path) orelse return null, │ │294 + .content = .{ .diff = .{ │ │295 + .start = start, │ │296 + .path = Array(repo_route_max_len).from(path) orelse return null, │ │297 + } }, │ │298 + } }; │ │299 + } │ │300 + │ │301 + // build a `.repo_commits` route showing the ref/oid's commit message in │ │302 + // full, which is the only content that page renders. │ │303 + pub fn repoCommitMessageRoute(identity: []const u8, ref_or_oid: RefOrOid, value: []const u8) ?RoutablePage {│ │304 + if (value.len == 0) return null; │ │305 + return .{ .repo_commits = .{ │ │306 + .name = Array(repo_identity_max_len).from(identity) orelse return null, │ │307 + .ref_or_oid = ref_or_oid, │ │308 + .value = Array(ref_route_max_len).from(value) orelse return null, │ │309 + .content = .message, │ │310 } }; │ │311 } │ │312 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │423 var out: std.Io.Writer.Allocating = .init(arena.allocator()); │ │424 try out.writer.print("{s}/" ++ commits_seg, .{prefix}); │ │425 if (c.ref_or_oid) |kind| if (c.value.len != 0) try out.writer.print("/{s}:{s}", .{ @tagName(kind), c.value.slice() });│ │399 - if (c.start != 0) try out.writer.print("/" ++ start_seg ++ "{d}", .{c.start}); │ │400 - if (c.path.len != 0) try out.writer.print("/" ++ path_seg ++ "{s}", .{c.path.slice()}); │ │426 + switch (c.content) { │ │427 + .diff => |d| { │ │428 + if (d.start != 0) try out.writer.print("/" ++ start_seg ++ "{d}", .{d.start}); │ │429 + if (d.path.len != 0) try out.writer.print("/" ++ path_seg ++ "{s}", .{d.path.slice()}); │ │430 + }, │ │431 + .message => try out.writer.print("/" ++ message_seg, .{}), │ │432 + } │ │433 break :blk out.written(); │ │434 }, │ │435 .repo_refs => |r| blk: { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │580 params.scanPairs(&segments) catch return null; │ │581 if (!params.only(&.{ .start, .branch, .tag, .object })) return null; │ │582 const start = params.start() orelse return null; │ │551 - const file = pathValue(segments.rest()) orelse return null; │ │552 - const ref = (params.ref() catch return null) orelse │ │583 + const ref = (params.ref() catch return null) orelse { │ │584 + const file = pathValue(segments.rest()) orelse return null; │ │585 return if (file.len == 0) repoCommitsRoute(pair, null, "", start, "") else null; │ │586 + }; │ │587 + // the message page names its commit and nothing else │ │588 + if (std.mem.eql(u8, segments.rest(), message_seg)) │ │589 + return if (start == 0) repoCommitMessageRoute(pair, ref.kind, ref.value) else null;│ │590 + const file = pathValue(segments.rest()) orelse return null; │ │591 return repoCommitsRoute(pair, ref.kind, ref.value, start, file); │ │592 } │ │593 return null; // unknown sub-path │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │609 .repo_commits => |a_c| std.mem.eql(u8, a_c.name.slice(), b.repo_commits.name.slice()) and │ │610 a_c.ref_or_oid == b.repo_commits.ref_or_oid and │ │611 std.mem.eql(u8, a_c.value.slice(), b.repo_commits.value.slice()) and │ │575 - a_c.start == b.repo_commits.start and │ │576 - std.mem.eql(u8, a_c.path.slice(), b.repo_commits.path.slice()), │ │612 + switch (a_c.content) { │ │613 + .diff => |a_d| switch (b.repo_commits.content) { │ │614 + .diff => |b_d| a_d.start == b_d.start and std.mem.eql(u8, a_d.path.slice(), b_d.path.slice()), │ │615 + .message => false, │ │616 + }, │ │617 + .message => std.meta.activeTag(b.repo_commits.content) == .message, │ │618 + }, │ │619 .repo_refs => |a_r| std.mem.eql(u8, a_r.name.slice(), b.repo_refs.name.slice()) and a_r.kind == b.repo_refs.kind and std.mem.eql(u8, a_r.from.slice(), b.repo_refs.from.slice()),│ │620 .repo_issues => |a_i| std.mem.eql(u8, a_i.name.slice(), b.repo_issues.name.slice()) and │ │621 std.mem.eql(u8, a_i.tag.slice(), b.repo_issues.tag.slice()) and │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯