╭ changed ──────────────╮ │src/ui/Repo/Patches.zig│ ╰───────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │624 return empty; │ │625 }; │ │626 │ │631 - // the sorted sets to window, per status: the label's sets when filtered, │ │632 - // else the top-level per-status sets. a missing set is an empty window. │ │633 - var open_set: ?DB.SortedSet(.read_only) = null; │ │634 - var closed_set: ?DB.SortedSet(.read_only) = null; │ │635 - var merged_set: ?DB.SortedSet(.read_only) = null; │ │636 - if (labeled) { │ │637 - if (try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.label_status_to_id_set_key))) |label_to_patches_cursor| {│ │638 - const label_to_patches = try DB.SortedMap(.read_only).init(label_to_patches_cursor); │ │639 - const decoded = std.Uri.percentDecodeInPlace(try aa.dupe(u8, empty.label)); │ │640 - open_set = try thread.labelStatusSet(Self, DB, label_to_patches, decoded, .open); │ │641 - closed_set = try thread.labelStatusSet(Self, DB, label_to_patches, decoded, .closed); │ │642 - merged_set = try thread.labelStatusSet(Self, DB, label_to_patches, decoded, .merged); │ │643 - } │ │644 - } else if (try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.status_to_id_set_key))) |status_to_patches_cursor| { │ │645 - const status_to_patches = try DB.SortedMap(.read_only).init(status_to_patches_cursor); │ │646 - open_set = try thread.statusSet(Self, DB, status_to_patches, .open); │ │647 - closed_set = try thread.statusSet(Self, DB, status_to_patches, .closed); │ │648 - merged_set = try thread.statusSet(Self, DB, status_to_patches, .merged); │ │649 - } else if (strict) return error.NotFound; │ │627 + // each status's listing intersects every label's set. │ │628 + // patches have no assignees, so an assignee term lists nothing. │ │629 + const filters = try srch_thrd.parseFilters(aa, empty.search orelse ""); │ │630 + const extra_sets: ?[]const DB.SortedSet(.read_only) = if (filters.assignees.len == 0) &.{} else null; │ │631 + const open_sets = try thread.statusSets(Self, repo_opts.hash, aa, haxy_moment, filters.labels, extra_sets, .open); │ │632 + const closed_sets = try thread.statusSets(Self, repo_opts.hash, aa, haxy_moment, filters.labels, extra_sets, .closed); │ │633 + const merged_sets = try thread.statusSets(Self, repo_opts.hash, aa, haxy_moment, filters.labels, extra_sets, .merged); │ │634 │ │635 const event_id_to_patch_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, evt.Patch.record_map_key)) orelse { │ │636 if (strict) return error.NotFound; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │668 if (!try set.contains(order_key)) return error.NotFound; │ │669 conflicts_root = order_key; │ │670 } else { │ │687 - // the named patch must be in its windowed set (a label url can name │ │688 - // an patch that doesn't carry the label). │ │689 - const set = (switch (patch_event.event.status) { │ │690 - .open => open_set, │ │691 - .closed => closed_set, │ │692 - .merged => merged_set, │ │693 - }) orelse return error.NotFound; │ │694 - if (!try set.contains(order_key)) return error.NotFound; │ │671 + // the named patch must be in every set its window intersects (a │ │672 + // filtered url can name a patch the filters leave out). │ │673 + const sets = switch (patch_event.event.status) { │ │674 + .open => open_sets, │ │675 + .closed => closed_sets, │ │676 + .merged => merged_sets, │ │677 + }; │ │678 + if (!try thread.inEverySet(DB, sets, order_key)) return error.NotFound;│ │679 │ │680 switch (patch_event.event.status) { │ │681 .open => open_root = order_key, │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │707 } │ │708 } │ │709 const thread_comments_start = if (empty.comment_id.len == 0) comments_start else 0; │ │726 - var open_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, open_set, open_root, conflict_set, empty.selected_id, thread_comments_start, empty.search, null, viewer); │ │727 - var closed_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, closed_set, closed_root, conflict_set, empty.selected_id, thread_comments_start, empty.search, null, viewer); │ │728 - var merged_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, merged_set, merged_root, conflict_set, empty.selected_id, thread_comments_start, empty.search, null, viewer); │ │729 - var conflicts_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, conflict_set, conflicts_root, conflict_set, empty.selected_id, thread_comments_start, null, null, viewer); │ │710 + var open_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, open_sets, open_root, conflict_set, empty.selected_id, thread_comments_start, filters.words, viewer); │ │711 + var closed_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, closed_sets, closed_root, conflict_set, empty.selected_id, thread_comments_start, filters.words, viewer); │ │712 + var merged_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, merged_sets, merged_root, conflict_set, empty.selected_id, thread_comments_start, filters.words, viewer); │ │713 + var conflicts_window = try thread.loadWindow(Self, .patch, repo_opts.hash, arena, admin_moment, haxy_moment, event_id_to_patch, if (conflict_set) |*set| set[0..1] else &.{}, conflicts_root, conflict_set, empty.selected_id, thread_comments_start, "", viewer);│ │714 try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, session.users_dir, haxy_moment, repo, &open_window); │ │715 try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, session.users_dir, haxy_moment, repo, &closed_window); │ │716 try setPatchDetails(repo_kind, repo_opts, io, arena, admin_moment, session.users_dir, haxy_moment, repo, &merged_window); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │727 return .{ │ │728 .identity = empty.identity, │ │729 .default_target_branch = empty.default_target_branch,│ │746 - .label = empty.label, │ │730 .search = empty.search, │ │731 .selected_id = empty.selected_id, │ │732 .comment_id = empty.comment_id, │ ╰──────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 995 │ │ 996 // tabs switching between the patches page's views │ │ 997 pub fn initHeader(allocator: std.mem.Allocator, session: *ui.Session, data: *const Self) !Header {│ │1015 - var header = try Header.init(allocator, session, data.search, null); │ │ 998 + var header = try Header.init(allocator, session, data.search, " search "); │ │ 999 errdefer header.deinit(allocator); │ │1000 const selected_index = View.viewIndex(data.view); │ │1001 const page_selected = std.meta.activeTag(session.data.current_page) == .repo_patches; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1003 // a list tab per status, labeled with its listing's patch count │ │1004 const status_labels = [_][]const u8{ open_tab_label, closed_tab_label, merged_tab_label }; │ │1005 for ([_]Status{ .open, .closed, .merged }, status_labels, 0..) |status, status_label, index| { │ │1023 - const route = try thread.searchRoute(ui.RoutablePage.repoPatchesRoute(data.identity, status, data.label, ""), data.search);│ │1006 + const route = try thread.searchRoute(ui.RoutablePage.repoPatchesRoute(data.identity, status, ""), data.search); │ │1007 const link = try ui.inPageTabLink(session, route, page_selected and selected_index == index); │ │1008 var label_buf: [64]u8 = undefined; │ │1009 const label = try thread.countLabel(&label_buf, status_label, data.window(status).*); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1012 │ │1013 // labels tab │ │1014 { │ │1032 - const labels_route = try thread.searchRoute(ui.RoutablePage.repoThreadLabelsRoute(.patch, data.identity, data.label), data.search);│ │1015 + const labels_route = try thread.searchRoute(ui.RoutablePage.repoThreadLabelsRoute(.patch, data.identity), data.search); │ │1016 const labels_link = try ui.inPageTabLink(session, labels_route, page_selected and selected_index == View.viewIndex(.labels)); │ │1017 try header.addTab(allocator, labels_tab_label, labels_link, View.viewIndex(.labels)); │ │1018 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯