╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1094 try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ patch_seg ++ "{s}/" ++ comment_seg ++ "{s}", .{ prefix, p.selected.slice(), p.comment.slice() }) │ │1095 else │ │1096 try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ patch_seg ++ "{s}/" ++ comment_seg ++ "{s}/" ++ start_seg ++ "{d}", .{ prefix, p.selected.slice(), p.comment.slice(), p.comments_start });│ │1063 - if (p.selected.len != 0) break :blk if (p.comments_start == 0) │ │1064 - try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ patch_seg ++ "{s}", .{ prefix, p.selected.slice() }) │ │1065 - else │ │1066 - try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ patch_seg ++ "{s}/" ++ start_seg ++ "{d}", .{ prefix, p.selected.slice(), p.comments_start }); │ │1067 - break :blk if (p.tag.len == 0) │ │1068 - try std.fmt.allocPrint(arena.allocator(), "{s}/patches/{s}", .{ prefix, @tagName(p.view) }) │ │1069 - else │ │1070 - try std.fmt.allocPrint(arena.allocator(), "{s}/patches/{s}/" ++ tag_filter_seg ++ "{s}", .{ prefix, @tagName(p.view), p.tag.slice() }); │ │1097 + var out: std.Io.Writer.Allocating = .init(arena.allocator()); │ │1098 + // the list the thread is selected in stays filtered, so paging │ │1099 + // and reloading keep the tag and the query │ │1100 + if (p.selected.len != 0) { │ │1101 + try out.writer.print("{s}/" ++ patch_seg ++ "{s}", .{ prefix, p.selected.slice() }); │ │1102 + try writeListFilters(&out.writer, p.tag.slice(), p.search.slice()); │ │1103 + if (p.comments_start != 0) try out.writer.print("/" ++ start_seg ++ "{d}", .{p.comments_start}); │ │1104 + break :blk out.written(); │ │1105 + } │ │1106 + try out.writer.print("{s}/patches/{s}", .{ prefix, @tagName(p.view) }); │ │1107 + try writeListFilters(&out.writer, p.tag.slice(), p.search.slice()); │ │1108 + break :blk out.written(); │ │1109 }, │ │1110 .repo_discussions => |t| blk: { │ │1111 const prefix = try repoUrlPrefix(arena, t.name.slice()); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1124 try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ discuss_seg ++ "{s}/" ++ comment_seg ++ "{s}", .{ prefix, t.selected.slice(), t.comment.slice() }) │ │1125 else │ │1126 try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ discuss_seg ++ "{s}/" ++ comment_seg ++ "{s}/" ++ start_seg ++ "{d}", .{ prefix, t.selected.slice(), t.comment.slice(), t.comments_start });│ │1089 - if (t.selected.len != 0) break :blk if (t.comments_start == 0) │ │1090 - try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ discuss_seg ++ "{s}", .{ prefix, t.selected.slice() }) │ │1091 - else │ │1092 - try std.fmt.allocPrint(arena.allocator(), "{s}/" ++ discuss_seg ++ "{s}/" ++ start_seg ++ "{d}", .{ prefix, t.selected.slice(), t.comments_start }); │ │1093 - break :blk if (t.tag.len == 0) │ │1094 - try std.fmt.allocPrint(arena.allocator(), "{s}/discussions/{s}", .{ prefix, @tagName(t.view) }) │ │1095 - else │ │1096 - try std.fmt.allocPrint(arena.allocator(), "{s}/discussions/{s}/" ++ tag_filter_seg ++ "{s}", .{ prefix, @tagName(t.view), t.tag.slice() }); │ │1127 + var out: std.Io.Writer.Allocating = .init(arena.allocator()); │ │1128 + // the list the thread is selected in stays filtered, so paging │ │1129 + // and reloading keep the tag and the query │ │1130 + if (t.selected.len != 0) { │ │1131 + try out.writer.print("{s}/" ++ discuss_seg ++ "{s}", .{ prefix, t.selected.slice() }); │ │1132 + try writeListFilters(&out.writer, t.tag.slice(), t.search.slice()); │ │1133 + if (t.comments_start != 0) try out.writer.print("/" ++ start_seg ++ "{d}", .{t.comments_start}); │ │1134 + break :blk out.written(); │ │1135 + } │ │1136 + try out.writer.print("{s}/discussions/{s}", .{ prefix, @tagName(t.view) }); │ │1137 + try writeListFilters(&out.writer, t.tag.slice(), t.search.slice()); │ │1138 + break :blk out.written(); │ │1139 }, │ │1140 .repo_events => |e| blk: { │ │1141 const prefix = try repoUrlPrefix(arena, e.name.slice()); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1413 } │ │1414 return null; │ │1415 } │ │1374 - if (!params.only(&.{.start})) return null; │ │1375 - return repoThreadCommentsRoute(.issue, pair, issue_id, params.start() orelse return null); │ │1416 + if (!params.only(&.{ .start, .tag, .search })) return null; │ │1417 + return withEncodedFilters(repoThreadCommentsRoute(.issue, pair, issue_id, params.start() orelse return null), params.values.get(.tag) orelse "", params.values.get(.search) orelse "");│ │1418 } │ │1419 if (std.mem.startsWith(u8, tab, patch_seg)) { │ │1420 const patch_id = tab[patch_seg.len..]; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1450 } │ │1451 return null; │ │1452 } │ │1411 - if (!params.only(&.{.start})) return null; │ │1412 - return repoThreadCommentsRoute(.patch, pair, patch_id, params.start() orelse return null); │ │1453 + if (!params.only(&.{ .start, .tag, .search })) return null; │ │1454 + return withEncodedFilters(repoThreadCommentsRoute(.patch, pair, patch_id, params.start() orelse return null), params.values.get(.tag) orelse "", params.values.get(.search) orelse "");│ │1455 } │ │1456 if (std.mem.startsWith(u8, tab, discuss_seg)) { │ │1457 const discussion_id = tab[discuss_seg.len..]; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1481 if (std.mem.eql(u8, tail, "description")) return if (params.only(&.{})) repoThreadDescriptionRoute(.discuss, pair, discussion_id) else null; │ │1482 return null; │ │1483 } │ │1442 - if (!params.only(&.{.start})) return null; │ │1443 - return repoThreadCommentsRoute(.discuss, pair, discussion_id, params.start() orelse return null); │ │1484 + if (!params.only(&.{ .start, .tag, .search })) return null; │ │1485 + return withEncodedFilters(repoThreadCommentsRoute(.discuss, pair, discussion_id, params.start() orelse return null), params.values.get(.tag) orelse "", params.values.get(.search) orelse "");│ │1486 } │ │1487 if (std.mem.eql(u8, tab, "issues")) { │ │1488 // the word is a list view; a filter with neither is never emitted. │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1495 if (!params.only(&.{.start})) return null; │ │1496 return repoIssuesConflictsRoute(pair, params.values.get(.start) orelse ""); │ │1497 } │ │1456 - if (!params.only(&.{.tag})) return null; │ │1457 - if (std.meta.stringToEnum(evt.Issue.Status, w)) |status| return repoIssuesRoute(pair, status, tag_value, ""); │ │1458 - if (std.mem.eql(u8, w, "tags")) return repoThreadTagsRoute(.issue, pair, tag_value); │ │1459 - if (std.mem.eql(u8, w, "new")) return if (tag_value.len == 0) repoThreadNewRoute(.issue, pair) else null; │ │1498 + if (!params.only(&.{ .tag, .search })) return null; │ │1499 + const search_value = params.values.get(.search) orelse ""; │ │1500 + if (std.meta.stringToEnum(evt.Issue.Status, w)) |status| return withEncodedSearch(repoIssuesRoute(pair, status, tag_value, ""), search_value);│ │1501 + if (std.mem.eql(u8, w, "tags")) return withEncodedSearch(repoThreadTagsRoute(.issue, pair, tag_value), search_value); │ │1502 + if (std.mem.eql(u8, w, "new")) return if (tag_value.len == 0 and search_value.len == 0) repoThreadNewRoute(.issue, pair) else null; │ │1503 return null; │ │1504 } │ │1505 if (std.mem.eql(u8, tab, "patches")) { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1510 if (!params.only(&.{.start})) return null; │ │1511 return repoPatchesConflictsRoute(pair, params.values.get(.start) orelse ""); │ │1512 } │ │1470 - if (!params.only(&.{.tag})) return null; │ │1471 - if (std.meta.stringToEnum(evt.Patch.StatusKind, view)) |status| return repoPatchesRoute(pair, status, tag_value, ""); │ │1472 - if (std.mem.eql(u8, view, "tags")) return repoThreadTagsRoute(.patch, pair, tag_value); │ │1473 - if (std.mem.eql(u8, view, "new")) return if (tag_value.len == 0) repoThreadNewRoute(.patch, pair) else null; │ │1474 - if (std.mem.eql(u8, view, "drafts")) return if (tag_value.len == 0) repoPatchesDraftsRoute(pair) else null; │ │1513 + if (!params.only(&.{ .tag, .search })) return null; │ │1514 + const search_value = params.values.get(.search) orelse ""; │ │1515 + if (std.meta.stringToEnum(evt.Patch.StatusKind, view)) |status| return withEncodedSearch(repoPatchesRoute(pair, status, tag_value, ""), search_value);│ │1516 + if (std.mem.eql(u8, view, "tags")) return withEncodedSearch(repoThreadTagsRoute(.patch, pair, tag_value), search_value); │ │1517 + if (std.mem.eql(u8, view, "new")) return if (tag_value.len == 0 and search_value.len == 0) repoThreadNewRoute(.patch, pair) else null; │ │1518 + if (std.mem.eql(u8, view, "drafts")) return if (tag_value.len == 0 and search_value.len == 0) repoPatchesDraftsRoute(pair) else null; │ │1519 return null; │ │1520 } │ │1521 if (std.mem.eql(u8, tab, "discussions")) { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1522 const word = params.scanTail(&segments) catch return null; │ │1523 const tag_value = params.values.get(.tag) orelse ""; │ │1524 const view = word orelse return if (params.only(&.{})) repoDiscussionsRoute(pair, "", "") else null; │ │1481 - if (!params.only(&.{.tag})) return null; │ │1482 - if (std.mem.eql(u8, view, "recent")) return repoDiscussionsRoute(pair, tag_value, ""); │ │1483 - if (std.mem.eql(u8, view, "tags")) return repoThreadTagsRoute(.discuss, pair, tag_value); │ │1484 - if (std.mem.eql(u8, view, "new")) return if (tag_value.len == 0) repoThreadNewRoute(.discuss, pair) else null; │ │1525 + if (!params.only(&.{ .tag, .search })) return null; │ │1526 + const search_value = params.values.get(.search) orelse ""; │ │1527 + if (std.mem.eql(u8, view, "recent")) return withEncodedSearch(repoDiscussionsRoute(pair, tag_value, ""), search_value); │ │1528 + if (std.mem.eql(u8, view, "tags")) return withEncodedSearch(repoThreadTagsRoute(.discuss, pair, tag_value), search_value); │ │1529 + if (std.mem.eql(u8, view, "new")) return if (tag_value.len == 0 and search_value.len == 0) repoThreadNewRoute(.discuss, pair) else null;│ │1530 return null; │ │1531 } │ │1532 if (std.mem.eql(u8, tab, "events")) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1647 .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()),│ │1648 .repo_issues => |a_i| std.mem.eql(u8, a_i.name.slice(), b.repo_issues.name.slice()) and │ │1649 std.mem.eql(u8, a_i.tag.slice(), b.repo_issues.tag.slice()) and │ │1650 + std.mem.eql(u8, a_i.search.slice(), b.repo_issues.search.slice()) and │ │1651 std.mem.eql(u8, a_i.selected.slice(), b.repo_issues.selected.slice()) and │ │1652 std.mem.eql(u8, a_i.theirs.slice(), b.repo_issues.theirs.slice()) and │ │1653 a_i.view == b.repo_issues.view and │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1655 std.mem.eql(u8, a_i.comment.slice(), b.repo_issues.comment.slice()), │ │1656 .repo_discussions => |a_t| std.mem.eql(u8, a_t.name.slice(), b.repo_discussions.name.slice()) and│ │1657 std.mem.eql(u8, a_t.tag.slice(), b.repo_discussions.tag.slice()) and │ │1658 + std.mem.eql(u8, a_t.search.slice(), b.repo_discussions.search.slice()) and │ │1659 std.mem.eql(u8, a_t.selected.slice(), b.repo_discussions.selected.slice()) and │ │1660 a_t.view == b.repo_discussions.view and │ │1661 a_t.comments_start == b.repo_discussions.comments_start and │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯