Skip to content
Source
Sign inSign up

pullRequestDiffComments.ts

106 lines4.0 KB

1import type { PullRequestReviewPosition, PullRequestReviewThread } from "@t3tools/contracts";
2import type { NativeReviewDiffRow } from "../diffs/nativeReviewDiffSurface";
3import type { NativeReviewDiffData } from "../review/nativeReviewDiffAdapter";
4import type { PendingReviewComment } from "./pullRequestReviewDrafts";
5​
6export function reviewPosition(row: NativeReviewDiffRow): PullRequestReviewPosition | null {
7 if (row.kind !== "line") return null;
8 if (row.change === "add" && row.newLineNumber)
9 return { kind: "added", newLine: row.newLineNumber };
10 if (row.change === "delete" && row.oldLineNumber)
11 return { kind: "deleted", oldLine: row.oldLineNumber };
12 if (row.oldLineNumber && row.newLineNumber)
13 return {
14 kind: "context",
15 oldLine: row.oldLineNumber,
16 newLine: row.newLineNumber,
17 side: "right",
18 };
19 return null;
21export function discussionCommentCountLabel(count: number) {
22 return `${count} ${count === 1 ? "comment" : "comments"}`;
24export function reviewPositionLabel(position: PullRequestReviewPosition) {
25 const oldSide =
26 position.kind === "deleted" || (position.kind === "context" && position.side === "left");
27 return `${oldSide ? "Old line" : "Line"} ${reviewLine(position)}`;
29function reviewLine(position: PullRequestReviewPosition) {
30 if (position.kind === "deleted" || (position.kind === "context" && position.side === "left"))
31 return position.oldLine;
32 return position.newLine;
34export function annotatePullRequestDiff(
35 data: NativeReviewDiffData,
36 threads: readonly PullRequestReviewThread[],
37 drafts: readonly PendingReviewComment[],
38 headSha: string | undefined,
39): NativeReviewDiffData {
40 if (threads.length === 0 && drafts.length === 0) return data;
41 const anchors = new Map<string, NativeReviewDiffRow[]>();
42 const firstByPath = new Map<string, NativeReviewDiffRow>();
43 const lines = new Map<string, NativeReviewDiffRow>();
44 const paths = new Map(data.files.map((file) => [file.id, file.path]));
45 for (const original of data.rows) {
46 const row = {
47 ...original,
48 filePath: original.filePath ?? (original.fileId ? paths.get(original.fileId) : undefined),
49 };
50 if (row.kind === "file" && row.filePath) firstByPath.set(row.filePath, row);
51 if (row.kind !== "line" || !row.filePath) continue;
52 if (row.oldLineNumber)
53 lines.set(JSON.stringify([row.filePath, "left", row.oldLineNumber]), row);
54 if (row.newLineNumber)
55 lines.set(JSON.stringify([row.filePath, "right", row.newLineNumber]), row);
56 }
57 const attach = (
58 anchor: NativeReviewDiffRow | undefined,
59 id: string,
60 body: string,
61 label: string,
62 ) => {
63 if (!anchor) return;
64 const rows = anchors.get(anchor.id) ?? [];
65 rows.push({
66 kind: "comment",
67 id,
68 fileId: anchor.fileId,
69 filePath: anchor.filePath,
70 commentText: body,
71 commentRangeLabel: label,
72 });
73 anchors.set(anchor.id, rows);
74 };
75 for (const thread of threads) {
76 if (thread.isOutdated) continue;
77 const anchor =
78 thread.line === null
79 ? firstByPath.get(thread.path)
80 : lines.get(JSON.stringify([thread.path, thread.side, thread.line]));
81 const first = thread.comments[0];
82 attach(
83 anchor,
84 `discussion:${thread.id}`,
85 `${first?.author?.login ?? "Discussion"}${thread.isResolved ? " · Resolved" : ""}\n${first?.body ?? ""}\n${discussionCommentCountLabel(thread.commentCount ?? thread.comments.length)} · Tap to open`,
86 thread.line === null ? "file" : String(thread.line),
87 );
88 }
89 for (const draft of drafts) {
90 if (draft.headSha !== headSha) continue;
91 const side =
92 draft.position.kind === "deleted"
93 ? "left"
94 : draft.position.kind === "context"
95 ? draft.position.side
96 : "right";
97 const line = reviewLine(draft.position);
98 attach(
99 lines.get(JSON.stringify([draft.path, side, line])),
100 `draft:${draft.id}`,
101 `Pending review · Tap to edit\n${draft.body}`,
102 String(line),
103 );
104 }
105 return { ...data, rows: data.rows.flatMap((row) => [row, ...(anchors.get(row.id) ?? [])]) };