fix: avoid merging unrelated comments into replies

This commit is contained in:
2026-04-08 23:20:41 +08:00
parent 9036dccabd
commit a61be1b4c4
3 changed files with 117 additions and 42 deletions
+2
View File
@@ -46,6 +46,8 @@ pub struct ReviewCommentDto {
pub path: Option<String>,
pub line: Option<i64>,
pub pull_request_review_id: Option<i64>,
#[serde(default, alias = "in_reply_to", alias = "in_reply_to_id", alias = "reply_to")]
pub in_reply_to: Option<i64>,
pub original_position: Option<u64>,
pub position: Option<u64>,
pub commit_id: Option<String>,
+36 -42
View File
@@ -48,59 +48,53 @@ pub fn normalize_bundle(repo: &str, bundle: PullBundleDto) -> PrReviewDocument {
}
fn normalize_threads(comments: Vec<ReviewCommentDto>) -> Vec<CommentThread> {
let mut grouped: BTreeMap<String, Vec<ReviewCommentDto>> = BTreeMap::new();
let mut all = comments;
all.sort_by_key(comment_sort_key);
for comment in comments {
let key = thread_key(&comment);
grouped.entry(key).or_default().push(comment);
let comment_ids: std::collections::BTreeSet<i64> = all.iter().map(|comment| comment.id).collect();
let mut roots: Vec<ReviewCommentDto> = Vec::new();
let mut children: BTreeMap<i64, Vec<ReviewCommentDto>> = BTreeMap::new();
for comment in all {
if let Some(parent_id) = comment.in_reply_to
&& comment_ids.contains(&parent_id)
{
children.entry(parent_id).or_default().push(comment);
continue;
}
roots.push(comment);
}
grouped
roots
.into_iter()
.map(|(thread_id, mut group)| {
group.sort_by_key(comment_sort_key);
let root = group.remove(0);
let file_path = root
.path
.clone()
.or_else(|| group.iter().find_map(|comment| comment.path.clone()));
let line = root
.line
.or_else(|| group.iter().find_map(|comment| comment.line));
.map(|root| {
let mut replies = Vec::new();
collect_replies(root.id, &mut children, &mut replies);
CommentThread {
thread_id,
file_path,
line,
thread_id: format!("comment-{}", root.id),
file_path: root.path.clone(),
line: root.line,
root_comment: to_comment_item(root),
replies: group.into_iter().map(to_comment_item).collect(),
replies: replies.into_iter().map(to_comment_item).collect(),
}
})
.collect()
}
fn thread_key(comment: &ReviewCommentDto) -> String {
match comment.pull_request_review_id {
Some(review_id) => format!(
"review-{review_id}-{path}-{line}-{position}-{commit}",
path = comment.path.as_deref().unwrap_or(""),
line = comment
.line
.map(|value| value.to_string())
.unwrap_or_default(),
position = comment
.original_position
.or(comment.position)
.map(|value| value.to_string())
.unwrap_or_default(),
commit = comment
.original_commit_id
.as_deref()
.or(comment.commit_id.as_deref())
.unwrap_or("")
),
None => format!("comment-{}", comment.id),
fn collect_replies(
parent_id: i64,
children: &mut BTreeMap<i64, Vec<ReviewCommentDto>>,
out: &mut Vec<ReviewCommentDto>,
) {
let Some(mut direct_replies) = children.remove(&parent_id) else {
return;
};
direct_replies.sort_by_key(comment_sort_key);
for reply in direct_replies {
let reply_id = reply.id;
out.push(reply);
collect_replies(reply_id, children, out);
}
}