╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────╮ │300 search: Array(search_route_max_len) = .{}, │ │301 }; │ │302 │ │303 - const user_segment = "/user/"; │ │304 - const repo_segment = "/repo/"; │ │305 - const fork_segment = "/fork/"; │ │303 + const home_segment = "home"; │ │304 const files_seg = "files"; │ │305 const commits_seg = "commits"; │ │306 // ends a commits route naming its commit's full message│ ╰─────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │323 // remainder of the url, so it must come last │ │324 const path_seg = "path:"; │ │325 │ │328 - // a repo route is "username/reponame", optionally followed by the files │ │326 + // a repo route is "username:reponame", optionally followed by the files │ │327 // tab's "/files/<refkind>:<refvalue>[/line:<n>][/path:<dir>]"; the bare │ │328 // pair means the default branch's files root. a local session's routes │ │329 // elide the identity: "" at the bare root, else starting at the tail's "/".│ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │355 │ │356 // the identity stored by every repo route. Local routes elide it.│ │357 pub const RepoIdentity = struct { │ │360 - identity: []const u8, // "owner/name" │ │358 + identity: []const u8, // "owner:name" │ │359 owner: []const u8, │ │360 name: []const u8, │ │361 │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │365 .owner = "", │ │366 .name = "", │ │367 }; │ │370 - if (identityElided(s)) { │ │371 - return result; │ │372 - } else { │ │373 - var id_segments = std.mem.splitScalar(u8, s, '/'); │ │374 - const owner = id_segments.next() orelse return null; │ │375 - const name = id_segments.next() orelse return null; │ │376 - if (owner.len == 0 or name.len == 0) return null; │ │377 - result.identity = s[0 .. owner.len + 1 + name.len]; │ │378 - result.owner = owner; │ │379 - result.name = name; │ │380 - } │ │368 + if (identityElided(s)) return result; │ │369 + const parsed = evt.parseOwnerRepoPath(s) orelse return null;│ │370 + result.identity = s; │ │371 + result.owner = parsed.owner; │ │372 + result.name = parsed.name; │ │373 return result; │ │374 } │ │375 }; │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │771 } }; │ │772 } │ │773 │ │782 - // build a `.repo_issues` route for "owner/name" (identity) showing │ │774 + // build a `.repo_issues` route for "owner:name" (identity) showing │ │775 // `status`'s list, filtered to the url-encoded `label` ("" = unfiltered) and │ │776 // rooted at the issue with hex event id `selected` ("" = the first window). │ │777 pub fn repoIssuesRoute(identity: []const u8, status: evt.Issue.Status, label: []const u8, selected: []const u8) ?RoutablePage {│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │1048 .home_about => "/", │ │1049 .home_users => |u| blk: { │ │1050 var out: std.Io.Writer.Allocating = .init(arena.allocator());│ │1059 - try out.writer.writeAll("/users"); │ │1051 + try out.writer.writeAll("/" ++ home_segment ++ "/users"); │ │1052 try writeListWindow(&out.writer, u.search.slice(), u.start); │ │1053 break :blk out.written(); │ │1054 }, │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │1063 - .home_repo_new => "/repo/new", │ │1064 - .home_user_new => "/user/new", │ │1065 - .home_user => "/user", │ │1066 - .not_found => "/not-found", │ │1055 + .home_repo_new => "/" ++ home_segment ++ "/repo/new", │ │1056 + .home_user_new => "/" ++ home_segment ++ "/user/new", │ │1057 + .home_user => "/" ++ home_segment ++ "/user", │ │1058 + .not_found => "/" ++ home_segment ++ "/not-found", │ │1059 .user_repos => |u| blk: { │ │1060 var out: std.Io.Writer.Allocating = .init(arena.allocator()); │ │1069 - try out.writer.print(user_segment ++ "{s}/repos", .{u.name.slice()});│ │1061 + try out.writer.print("/{s}/repos", .{u.name.slice()}); │ │1062 try writeListWindow(&out.writer, u.search.slice(), u.start); │ │1063 break :blk out.written(); │ │1064 }, │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1065 .user_forks => |u| if (u.start == 0) │ │1074 - try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}/forks", .{u.name.slice()}) │ │1066 + try std.fmt.allocPrint(arena.allocator(), "/{s}/forks", .{u.name.slice()}) │ │1067 else │ │1076 - try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}/forks/" ++ start_seg ++ "{d}", .{ u.name.slice(), u.start }), │ │1077 - .user_repo_new => |name| try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}/repo/new", .{name.slice()}), │ │1078 - .user_user_new => |name| try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}/user/new", .{name.slice()}), │ │1079 - .user_user => |name| try std.fmt.allocPrint(arena.allocator(), user_segment ++ "{s}/user", .{name.slice()}), │ │1068 + try std.fmt.allocPrint(arena.allocator(), "/{s}/forks/" ++ start_seg ++ "{d}", .{ u.name.slice(), u.start }), │ │1069 + .user_repo_new => |name| try std.fmt.allocPrint(arena.allocator(), "/{s}/repo/new", .{name.slice()}), │ │1070 + .user_user_new => |name| try std.fmt.allocPrint(arena.allocator(), "/{s}/user/new", .{name.slice()}), │ │1071 + .user_user => |name| try std.fmt.allocPrint(arena.allocator(), "/{s}/user", .{name.slice()}), │ │1072 .repo_files => |f| blk: { │ │1073 const prefix = try repoUrlPrefix(arena, f.name.slice()); │ │1074 if (f.ref_kind == null and f.patchrev_id.len == 0 and f.find.len == 0 and f.line == 0) break :blk if (prefix.len == 0) "/" else prefix;│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1240 .repo_user_new => |name| try std.fmt.allocPrint(arena.allocator(), "{s}/user/new", .{try repoUrlPrefix(arena, name.slice())}), │ │1241 .repo_repo => |name| try std.fmt.allocPrint(arena.allocator(), "{s}/repo", .{try repoUrlPrefix(arena, name.slice())}), │ │1242 .repo_user => |name| try std.fmt.allocPrint(arena.allocator(), "{s}/user", .{try repoUrlPrefix(arena, name.slice())}), │ │1251 - .fork_patch => |f| try std.fmt.allocPrint(arena.allocator(), fork_segment ++ "{s}/" ++ patch_seg ++ "{s}", .{ f.name.slice(), f.id.slice() }),│ │1243 + .fork_patch => |f| try std.fmt.allocPrint(arena.allocator(), "/{s}+{s}", .{ f.name.slice(), f.id.slice() }), │ │1244 .fork_diff => |d| blk: { │ │1245 var out: std.Io.Writer.Allocating = .init(arena.allocator()); │ │1254 - try out.writer.print(fork_segment ++ "{s}/" ++ patch_seg ++ "{s}/diff", .{ d.fork.name.slice(), d.fork.id.slice() }); │ │1246 + try out.writer.print("/{s}+{s}/diff", .{ d.fork.name.slice(), d.fork.id.slice() }); │ │1247 if (d.oid.len != 0) try out.writer.print("/object:{s}/" ++ base_seg ++ "{s}", .{ d.oid.slice(), d.base_oid.slice() }); │ │1248 if (d.start != 0) try out.writer.print("/" ++ start_seg ++ "{d}", .{d.start}); │ │1249 if (d.path.len != 0) try out.writer.print("/" ++ path_seg ++ "{s}", .{d.path.slice()}); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1251 }, │ │1252 .fork_files => |f| blk: { │ │1253 var out: std.Io.Writer.Allocating = .init(arena.allocator()); │ │1262 - try out.writer.print(fork_segment ++ "{s}/" ++ patch_seg ++ "{s}/" ++ files_seg, .{ f.fork.name.slice(), f.fork.id.slice() });│ │1254 + try out.writer.print("/{s}+{s}/" ++ files_seg, .{ f.fork.name.slice(), f.fork.id.slice() }); │ │1255 if (f.oid.len != 0) try out.writer.print("/object:{s}", .{f.oid.slice()}); │ │1256 if (f.find.len != 0) try out.writer.print("/" ++ find_seg ++ "{s}", .{f.find.slice()}); │ │1257 if (f.line != 0) try out.writer.print("/" ++ line_seg ++ "{d}", .{f.line}); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯