Skip to content

Commit

Permalink
[WIP] feat: show list of patches that already have been Reviewed-by
Browse files Browse the repository at this point in the history
Signed-off-by: David Tadokoro <[email protected]>
  • Loading branch information
davidbtadokoro committed Nov 11, 2024
1 parent bb0d654 commit fe2acb5
Showing 1 changed file with 136 additions and 18 deletions.
154 changes: 136 additions & 18 deletions src/ui/details_actions.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,91 @@
use std::cmp::Ordering;

use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
text::{Line, Span, ToSpan},
widgets::{Block, Borders, Padding, Paragraph, Wrap},
Frame,
};

use crate::app::{screens::details_actions::PatchsetAction, App};

fn render_details_and_actions(f: &mut Frame, app: &App, details_chunk: Rect, actions_chunk: Rect) {
/// Get list of indexes representing the patches that were replied w/ the
/// "Reviewed-by:" tag, or that are staged to.
///
/// # Arguments
///
/// * `app`: Immutable reference to `patch-hub` model
///
/// # Returns
///
/// An owned `Vec<Span>` that is an ordered comma-separated list of the indexes.
/// Staged indexes are appended w/ `*` and colored `Color::DarkGrey`. If there
/// are no patches that were neither replied or staged w/ the tag, the return
/// string will be empty.
fn reviewed_by_list(app: &App) -> Vec<Span> {
let patchset_details_and_actions = app.patchset_details_and_actions_state.as_ref().unwrap();

let mut patches_to_reply = String::new();
let number_offset = if patchset_details_and_actions.has_cover_letter {
0
} else {
1
};

let mut replied_inds: Vec<usize> = Vec::new();
if let Some(already_reviewed_by) = app.reviewed_patchsets.get(
&patchset_details_and_actions
.representative_patch
.message_id()
.href,
) {
replied_inds = already_reviewed_by
.iter()
.cloned()
.map(|i| i + number_offset)
.collect();
replied_inds.sort_unstable();
}

let mut staged_inds: Vec<usize> = Vec::new();
if let Some(true) = patchset_details_and_actions
.patchset_actions
.get(&PatchsetAction::ReplyWithReviewedBy)
{
patches_to_reply.push('(');
let number_offset = if patchset_details_and_actions.has_cover_letter {
0
} else {
1
};
let patches_to_reply_numbers: Vec<usize> = patchset_details_and_actions
staged_inds = patchset_details_and_actions
.patches_to_reply
.iter()
.enumerate()
.filter_map(|(i, &val)| if val { Some(i + number_offset) } else { None })
.collect();
for number in patches_to_reply_numbers {
patches_to_reply.push_str(&format!("{number},"));
staged_inds.sort_unstable();
}

let reviewed_by_inds = merge_reviewed_by_inds(replied_inds, staged_inds);

let mut reviewed_by_list = Vec::new();
// let mut reviewed_by_list = String::new();

for (index, is_replied) in reviewed_by_inds {
let mut entry = index.to_string();
let mut entry_color = Style::default().fg(Color::White);
if !is_replied {
entry.push('*');
entry_color = entry_color.fg(Color::DarkGray);
}
patches_to_reply = format!("{})", &patches_to_reply[..patches_to_reply.len() - 1]);
reviewed_by_list.push(Span::styled(entry, entry_color));
reviewed_by_list.push(Span::styled(
", ".to_string(),
Style::default().fg(Color::White),
));
}
reviewed_by_list.pop();

reviewed_by_list
}

fn render_details_and_actions(f: &mut Frame, app: &App, details_chunk: Rect, actions_chunk: Rect) {
let patchset_details_and_actions = app.patchset_details_and_actions_state.as_ref().unwrap();

let patchset_details = &patchset_details_and_actions.representative_patch;
let mut patchset_details = vec![
Expand Down Expand Up @@ -72,11 +125,15 @@ fn render_details_and_actions(f: &mut Frame, app: &App, details_chunk: Rect, act
),
]),
];
if !patches_to_reply.is_empty() {
patchset_details.push(Line::from(vec![
Span::styled("Reviewed-by*: ", Style::default().fg(Color::Cyan)),
Span::styled(patches_to_reply, Style::default().fg(Color::DarkGray)),
]));
let mut reviewed_by = reviewed_by_list(app);
if !reviewed_by.is_empty() {
let mut reviewed_by_line = vec![
Span::styled("Reviewed-by: ", Style::default().fg(Color::Cyan)),
'('.to_span(),
];
reviewed_by_line.append(&mut reviewed_by);
reviewed_by_line.push(')'.to_span());
patchset_details.push(Line::from(reviewed_by_line));
}

let patchset_details = Paragraph::new(patchset_details)
Expand Down Expand Up @@ -224,3 +281,64 @@ pub fn keys_hint() -> Span<'static> {
Style::default().fg(Color::Red),
)
}

/// Receives two `usize` slices representing the patch indexes tagged w/
/// "Reviewed-by", preceding those that were replied over those that are only
/// staged.
///
/// # Arguments
///
/// * `replied_inds`: Ordered slice of patch indexes that were replied
/// * `staged_inds`: Ordered slice of patch indexes that staged to be replied
///
/// # Returns
///
/// This function always returns a `Vec<(usize, bool)>` (empty or not). The
/// tuples `(index, is_replied)` encode the precedence, in which `is_replied ==
/// true` means that patch of number `index` was replied w/ the tag.
fn merge_reviewed_by_inds(
mut replied_inds: Vec<usize>,
mut staged_inds: Vec<usize>,
) -> Vec<(usize, bool)> {
let mut reviewed_by_inds = Vec::new();
let mut i = 0;
let mut j = 0;

// Don't assume the caller ordered the vectors beforehand
replied_inds.sort();
staged_inds.sort();

while (i != replied_inds.len()) && (j != staged_inds.len()) {
match replied_inds[i].cmp(&staged_inds[j]) {
Ordering::Less => {
reviewed_by_inds.push((replied_inds[i], true));
i += 1;
}
Ordering::Equal => {
reviewed_by_inds.push((replied_inds[i], true));
i += 1;
j += 1;
}
Ordering::Greater => {
reviewed_by_inds.push((staged_inds[j], false));
j += 1;
}
}
}

let mut remaining_inds: Vec<(usize, bool)>;
if i != replied_inds.len() {
remaining_inds = replied_inds[i..]
.iter()
.map(|&index| (index, true))
.collect();
} else {
remaining_inds = staged_inds[j..]
.iter()
.map(|&index| (index, false))
.collect();
}
reviewed_by_inds.append(&mut remaining_inds);

reviewed_by_inds
}

0 comments on commit fe2acb5

Please sign in to comment.