╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │46 else => return error.UnexpectedRoute, │ │47 }, │ │48 .repo => switch (route) { │ │49 - .repo, .repo_commits, .repo_refs, .repo_settings, .repo_auth => .{ .repo = try Repo.init(arena, session, route) }, │ │49 + .repo_files, .repo_commits, .repo_refs, .repo_settings, .repo_auth => .{ .repo = try Repo.init(arena, session, route) },│ │50 else => return error.UnexpectedRoute, │ │51 }, │ │52 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │72 user: struct { name: Array(evt.User.name_max_len), after: usize = 0 }, │ │73 user_settings: Array(evt.User.name_max_len), │ │74 user_auth: Array(evt.User.name_max_len), │ │75 - repo: Array(repo_route_max_len), │ │75 + repo_files: struct { name: Array(repo_route_max_len), after: usize = 0 }, │ │76 repo_commits: struct { name: Array(repo_route_max_len), after: usize = 0 }, │ │77 repo_refs: struct { name: Array(repo_route_max_len), kind: RefKind = .branch, after: usize = 0 },│ │78 repo_settings: Array(repo_route_max_len), │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │103 // mean the files root of the repo's default branch. │ │104 pub const repo_route_max_len = 1024; │ │105 │ │106 - // the parts of a `.repo` files route. all slices point into the route's │ │106 + // the parts of a `.repo_files` route. all slices point into the route's │ │107 // stored string. ref_kind is null (and ref_value/dir empty) for the bare│ │108 // default-branch root; dir is "" at a ref's root. │ │109 pub const RepoFiles = struct { │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │114 ref_value: []const u8, │ │115 dir: []const u8, │ │116 │ │117 - // parse a `.repo` route's stored string ("owner/name" or │ │117 + // parse a `.repo_files` route's stored string ("owner/name" or │ │118 // "owner/name/files/<refkind>/<refvalue>[/<dir>]"). │ │119 pub fn parse(s: []const u8) ?RepoFiles { │ │120 const s1 = std.mem.indexOfScalar(u8, s, '/') orelse return null;│ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │150 } │ │151 }; │ │152 │ │153 - // build a `.repo` files route for "owner/name" (identity) at `dir` ("" = the │ │154 - // ref's root) pinned to `ref_kind`/`ref_value`. a null ref_kind yields the │ │155 - // bare default-branch root. null if the result doesn't fit the inline name. │ │156 - pub fn repoFilesRoute(identity: []const u8, ref_kind: ?RefOrOid, ref_value: []const u8, dir: []const u8) ?RoutablePage { │ │157 - const kind = ref_kind orelse return .{ .repo = Array(repo_route_max_len).from(identity) orelse return null }; │ │153 + // build a `.repo_files` route for "owner/name" (identity) at `dir` ("" = the │ │154 + // ref's root) pinned to `ref_kind`/`ref_value`, showing the file content │ │155 + // window starting at line `after` (0 = the first window, dropped from the │ │156 + // url). a null ref_kind yields the bare default-branch root. null if the │ │157 + // result doesn't fit the inline name. │ │158 + pub fn repoFilesRoute(identity: []const u8, ref_kind: ?RefOrOid, ref_value: []const u8, dir: []const u8, after: usize) ?RoutablePage { │ │159 + const kind = ref_kind orelse return .{ .repo_files = .{ .name = Array(repo_route_max_len).from(identity) orelse return null, .after = after } };│ │160 var buf: [repo_route_max_len]u8 = undefined; │ │161 const s = if (dir.len == 0) │ │162 std.fmt.bufPrint(&buf, "{s}/" ++ files_seg ++ "/{s}/{s}", .{ identity, @tagName(kind), ref_value }) catch return null │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │163 else │ │164 std.fmt.bufPrint(&buf, "{s}/" ++ files_seg ++ "/{s}/{s}/{s}", .{ identity, @tagName(kind), ref_value, dir }) catch return null;│ │163 - return .{ .repo = Array(repo_route_max_len).from(s) orelse return null }; │ │165 + return .{ .repo_files = .{ .name = Array(repo_route_max_len).from(s) orelse return null, .after = after } }; │ │166 } │ │167 │ │168 // the ref/oid a `.repo_commits` route's stored string walks from. kind is │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │227 .home_settings => "/settings", │ │228 .home_auth => "/auth", │ │229 .user, .user_settings, .user_auth => @compileError("user routes are dynamic; use urlAlloc"), │ │228 - .repo, .repo_commits, .repo_refs, .repo_settings, .repo_auth => @compileError("repo routes are dynamic; use urlAlloc"), │ │230 + .repo_files, .repo_commits, .repo_refs, .repo_settings, .repo_auth => @compileError("repo routes are dynamic; use urlAlloc"),│ │231 }; │ │232 } │ │233 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │241 try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}?after={d}", .{ u.name.slice(), u.after }), │ │242 .user_settings => |name| try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}/settings", .{name.slice()}),│ │243 .user_auth => |name| try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}/auth", .{name.slice()}), │ │242 - .repo => |name| try std.fmt.allocPrint(arena.allocator(), repo_segment ++ "{s}", .{name.slice()}), │ │244 + .repo_files => |f| if (f.after == 0) │ │245 + try std.fmt.allocPrint(arena.allocator(), repo_segment ++ "{s}", .{f.name.slice()}) │ │246 + else │ │247 + try std.fmt.allocPrint(arena.allocator(), repo_segment ++ "{s}?after={d}", .{ f.name.slice(), f.after }), │ │248 .repo_commits => |c| if (c.after == 0) │ │249 try std.fmt.allocPrint(arena.allocator(), repo_segment ++ "{s}", .{c.name.slice()}) │ │250 else │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────╮ │263 return switch (self) { │ │264 .home_users, .home_repos, .home_settings, .home_auth => .home, │ │265 .user, .user_settings, .user_auth => .user, │ │261 - .repo, .repo_commits, .repo_refs, .repo_settings, .repo_auth => .repo, │ │266 + .repo_files, .repo_commits, .repo_refs, .repo_settings, .repo_auth => .repo,│ │267 }; │ │268 } │ │269 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │312 } }; │ │313 if (std.mem.eql(u8, sub, "settings")) return .{ .repo_settings = Array(repo_route_max_len).from(pair) orelse return null }; │ │314 if (std.mem.eql(u8, sub, "auth")) return .{ .repo_auth = Array(repo_route_max_len).from(pair) orelse return null }; │ │310 - if (std.mem.eql(u8, sub, files_seg)) return repoFilesRoute(pair, null, "", ""); // trailing /files == default-branch root │ │315 + if (std.mem.eql(u8, sub, files_seg)) return repoFilesRoute(pair, null, "", "", after); // trailing /files == default-branch root│ │316 if (std.mem.startsWith(u8, sub, files_seg ++ "/")) { │ │317 // "files/<refkind>/<refvalue>[/<dir>]" │ │318 const ref_part = sub[files_seg.len + 1 ..]; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯