feat: add persistent pull request sort and filter menus

This commit is contained in:
Valdemar Lorenzen
2026-10-21 13:30:38 +02:00
parent 37b47ebc8a
commit fbcb519e13
22 changed files with 1557 additions and 181 deletions
+95 -8
View File
@@ -3367,12 +3367,79 @@ fn draw_diff(
} else {
"After"
};
let title = if pull_requests {
let title = if let Some(menu) = &diff.pull_request_menu {
if menu.sort {
" Sort pull requests ".into()
} else {
" Filter pull requests ".into()
}
} else if pull_requests {
" Pull requests ".into()
} else {
format!(" Diff · Choose {stage} · {} ", diff.comparison_mode.label())
};
let content = diff_panel(frame, panel, &title, "", "", true);
if let Some(menu) = &diff.pull_request_menu {
panels.picker_scroll.revisions = None;
let configured = editor
.is_some_and(|editor| editor.pull_request_configured[usize::from(!menu.sort)]);
let mut lines = vec![Line::from("↑/↓ or Ctrl+k/j to select")];
if configured {
lines.push(Line::styled(
"Configured override on next startup",
Style::default().fg(Color::Yellow),
));
}
let count = if menu.sort { 4 } else { 3 };
let fields = menu.draft.ordered_fields();
for index in 0..count {
let text = if menu.sort {
let field = fields[index];
let rule = menu.draft.sort.get(&field).copied().unwrap_or_default();
let rank = rule.precedence.map_or("–".into(), |rank| rank.to_string());
let direction = if !field.is_date() {
""
} else if rule.direction == crate::pull_requests::Direction::Desc {
" · newest first"
} else {
" · oldest first"
};
format!("[{rank}] {}{direction}", field.label())
} else {
let filter = crate::pull_requests::ReviewFilter::ALL[index];
format!(
"[{}] {}",
if menu.draft.filters.contains(&filter) {
"x"
} else {
" "
},
filter.label()
)
};
lines.push(Line::styled(
format!("{} {text}", if index == menu.selected { ">" } else { " " }),
if index == menu.selected {
Style::default()
.bg(Color::Blue)
.add_modifier(Modifier::BOLD)
} else {
Style::default()
},
));
}
lines.push(Line::from(if menu.sort {
"1–4 rank · Backspace clear rank"
} else {
"Space toggle · checked buckets OR"
}));
if menu.sort {
lines.push(Line::from("←/→ date direction · unranked default"));
}
lines.push(Line::from("Enter Apply · Esc Cancel · Ctrl+s/f switch"));
frame.render_widget(Paragraph::new(lines), content);
return;
}
let pull_request_notice = (pull_requests && diff.phase == Phase::Before)
.then(|| diff.pull_request_notice())
.flatten();
@@ -3425,14 +3492,34 @@ fn draw_diff(
.as_ref()
.map_or("not selected", |revision| revision.label.as_str());
let stage_text = if pull_requests {
let sort = match diff.pull_request_priority {
crate::diff::PullRequestReviewScope::All => "Newest".into(),
scope => format!("{} first", scope.label()),
let filters = &diff.pull_request_preferences.filters;
let review = if filters.len() == 3 {
"All".into()
} else if filters.is_empty() {
"None".into()
} else {
filters
.iter()
.map(|filter| match filter {
crate::pull_requests::ReviewFilter::Requested => "Requested",
crate::pull_requests::ReviewFilter::Approved => "Approved",
crate::pull_requests::ReviewFilter::Other => "Other",
})
.collect::<Vec<_>>()
.join("+")
};
format!(
"Review: {} · Ctrl+f{}Sort: {sort} · Ctrl+s",
diff.pull_request_filter.label(),
"Review: {review} · Ctrl+f{}Sort settings · Ctrl+s{}",
if stacked_review_controls { "\n" } else { " " },
if editor.is_some_and(|editor| editor
.pull_request_configured
.iter()
.any(|&configured| configured))
{
" · configured"
} else {
""
},
)
} else {
format!(
@@ -3531,8 +3618,8 @@ fn draw_diff(
diff.category,
&diff.query,
diff.comparison_mode as u8,
diff.pull_request_filter,
diff.pull_request_priority,
&diff.pull_request_preferences.filters,
&diff.pull_request_preferences.sort,
diff.catalog
.as_ref()
.map(|catalog| (catalog.entries.as_ptr(), catalog.entries.len())),