X Tutup
Skip to content

Commit a77f3dd

Browse files
authored
Merge pull request cli#2575 from cli/pr-view-comments
Add ability to view comments with `pr view`
2 parents 1e4fa60 + dee7077 commit a77f3dd

File tree

9 files changed

+921
-156
lines changed

9 files changed

+921
-156
lines changed

api/queries_comments.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,39 @@ func CommentsForIssue(client *Client, repo ghrepo.Interface, issue *Issue) (*Com
6363

6464
return &Comments{Nodes: comments, TotalCount: len(comments)}, nil
6565
}
66+
67+
func CommentsForPullRequest(client *Client, repo ghrepo.Interface, pr *PullRequest) (*Comments, error) {
68+
type response struct {
69+
Repository struct {
70+
PullRequest struct {
71+
Comments Comments `graphql:"comments(first: 100, after: $endCursor)"`
72+
} `graphql:"pullRequest(number: $number)"`
73+
} `graphql:"repository(owner: $owner, name: $repo)"`
74+
}
75+
76+
variables := map[string]interface{}{
77+
"owner": githubv4.String(repo.RepoOwner()),
78+
"repo": githubv4.String(repo.RepoName()),
79+
"number": githubv4.Int(pr.Number),
80+
"endCursor": (*githubv4.String)(nil),
81+
}
82+
83+
gql := graphQLClient(client.http, repo.RepoHost())
84+
85+
var comments []Comment
86+
for {
87+
var query response
88+
err := gql.QueryNamed(context.Background(), "CommentsForPullRequest", &query, variables)
89+
if err != nil {
90+
return nil, err
91+
}
92+
93+
comments = append(comments, query.Repository.PullRequest.Comments.Nodes...)
94+
if !query.Repository.PullRequest.Comments.PageInfo.HasNextPage {
95+
break
96+
}
97+
variables["endCursor"] = githubv4.String(query.Repository.PullRequest.Comments.PageInfo.EndCursor)
98+
}
99+
100+
return &Comments{Nodes: comments, TotalCount: len(comments)}, nil
101+
}

api/queries_pr.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ type PullRequest struct {
137137
Milestone struct {
138138
Title string
139139
}
140+
Comments Comments
141+
ReactionGroups ReactionGroups
140142
}
141143

142144
type NotFoundError struct {
@@ -603,6 +605,30 @@ func PullRequestByNumber(client *Client, repo ghrepo.Interface, number int) (*Pu
603605
milestone{
604606
title
605607
}
608+
comments(last: 1) {
609+
nodes {
610+
author {
611+
login
612+
}
613+
authorAssociation
614+
body
615+
createdAt
616+
includesCreatedEdit
617+
reactionGroups {
618+
content
619+
users {
620+
totalCount
621+
}
622+
}
623+
}
624+
totalCount
625+
}
626+
reactionGroups {
627+
content
628+
users {
629+
totalCount
630+
}
631+
}
606632
}
607633
}
608634
}`
@@ -712,6 +738,30 @@ func PullRequestForBranch(client *Client, repo ghrepo.Interface, baseBranch, hea
712738
milestone{
713739
title
714740
}
741+
comments(last: 1) {
742+
nodes {
743+
author {
744+
login
745+
}
746+
authorAssociation
747+
body
748+
createdAt
749+
includesCreatedEdit
750+
reactionGroups {
751+
content
752+
users {
753+
totalCount
754+
}
755+
}
756+
}
757+
totalCount
758+
}
759+
reactionGroups {
760+
content
761+
users {
762+
totalCount
763+
}
764+
}
715765
}
716766
}
717767
}

pkg/cmd/issue/view/view.go

Lines changed: 4 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ func viewRun(opts *ViewOptions) error {
114114
}
115115

116116
if opts.Comments {
117-
return printRawIssueComments(opts.IO.Out, issue)
117+
fmt.Fprint(opts.IO.Out, prShared.RawCommentList(issue.Comments))
118+
return nil
118119
}
119120

120121
return printRawIssuePreview(opts.IO.Out, issue)
@@ -140,26 +141,6 @@ func printRawIssuePreview(out io.Writer, issue *api.Issue) error {
140141
return nil
141142
}
142143

143-
func printRawIssueComments(out io.Writer, issue *api.Issue) error {
144-
var b strings.Builder
145-
for _, comment := range issue.Comments.Nodes {
146-
fmt.Fprint(&b, rawIssueComment(comment))
147-
}
148-
fmt.Fprint(out, b.String())
149-
return nil
150-
}
151-
152-
func rawIssueComment(comment api.Comment) string {
153-
var b strings.Builder
154-
fmt.Fprintf(&b, "author:\t%s\n", comment.Author.Login)
155-
fmt.Fprintf(&b, "association:\t%s\n", strings.ToLower(comment.AuthorAssociation))
156-
fmt.Fprintf(&b, "edited:\t%t\n", comment.IncludesCreatedEdit)
157-
fmt.Fprintln(&b, "--")
158-
fmt.Fprintln(&b, comment.Body)
159-
fmt.Fprintln(&b, "--")
160-
return b.String()
161-
}
162-
163144
func printHumanIssuePreview(io *iostreams.IOStreams, issue *api.Issue) error {
164145
out := io.Out
165146
now := time.Now()
@@ -177,7 +158,7 @@ func printHumanIssuePreview(io *iostreams.IOStreams, issue *api.Issue) error {
177158
)
178159

179160
// Reactions
180-
if reactions := reactionGroupList(issue.ReactionGroups); reactions != "" {
161+
if reactions := prShared.ReactionGroupList(issue.ReactionGroups); reactions != "" {
181162
fmt.Fprint(out, reactions)
182163
fmt.Fprintln(out)
183164
}
@@ -215,7 +196,7 @@ func printHumanIssuePreview(io *iostreams.IOStreams, issue *api.Issue) error {
215196

216197
// Comments
217198
if issue.Comments.TotalCount > 0 {
218-
comments, err := issueCommentList(io, issue.Comments)
199+
comments, err := prShared.CommentList(io, issue.Comments)
219200
if err != nil {
220201
return err
221202
}
@@ -228,75 +209,6 @@ func printHumanIssuePreview(io *iostreams.IOStreams, issue *api.Issue) error {
228209
return nil
229210
}
230211

231-
func issueCommentList(io *iostreams.IOStreams, comments api.Comments) (string, error) {
232-
var b strings.Builder
233-
cs := io.ColorScheme()
234-
retrievedCount := len(comments.Nodes)
235-
hiddenCount := comments.TotalCount - retrievedCount
236-
237-
if hiddenCount > 0 {
238-
fmt.Fprint(&b, cs.Gray(fmt.Sprintf("———————— Not showing %s ————————", utils.Pluralize(hiddenCount, "comment"))))
239-
fmt.Fprintf(&b, "\n\n\n")
240-
}
241-
242-
for i, comment := range comments.Nodes {
243-
last := i+1 == retrievedCount
244-
cmt, err := formatIssueComment(io, comment, last)
245-
if err != nil {
246-
return "", err
247-
}
248-
fmt.Fprint(&b, cmt)
249-
if last {
250-
fmt.Fprintln(&b)
251-
}
252-
}
253-
254-
if hiddenCount > 0 {
255-
fmt.Fprint(&b, cs.Gray("Use --comments to view the full conversation"))
256-
fmt.Fprintln(&b)
257-
}
258-
259-
return b.String(), nil
260-
}
261-
262-
func formatIssueComment(io *iostreams.IOStreams, comment api.Comment, newest bool) (string, error) {
263-
var b strings.Builder
264-
cs := io.ColorScheme()
265-
266-
// Header
267-
fmt.Fprint(&b, cs.Bold(comment.Author.Login))
268-
if comment.AuthorAssociation != "NONE" {
269-
fmt.Fprint(&b, cs.Bold(fmt.Sprintf(" (%s)", strings.ToLower(comment.AuthorAssociation))))
270-
}
271-
fmt.Fprint(&b, cs.Bold(fmt.Sprintf(" • %s", utils.FuzzyAgoAbbr(time.Now(), comment.CreatedAt))))
272-
if comment.IncludesCreatedEdit {
273-
fmt.Fprint(&b, cs.Bold(" • edited"))
274-
}
275-
if newest {
276-
fmt.Fprint(&b, cs.Bold(" • "))
277-
fmt.Fprint(&b, cs.CyanBold("Newest comment"))
278-
}
279-
fmt.Fprintln(&b)
280-
281-
// Reactions
282-
if reactions := reactionGroupList(comment.ReactionGroups); reactions != "" {
283-
fmt.Fprint(&b, reactions)
284-
fmt.Fprintln(&b)
285-
}
286-
287-
// Body
288-
if comment.Body != "" {
289-
style := markdown.GetStyle(io.TerminalTheme())
290-
md, err := markdown.Render(comment.Body, style, "")
291-
if err != nil {
292-
return "", err
293-
}
294-
fmt.Fprint(&b, md)
295-
}
296-
297-
return b.String(), nil
298-
}
299-
300212
func issueStateTitleWithColor(cs *iostreams.ColorScheme, state string) string {
301213
colorFunc := cs.ColorFromString(prShared.ColorForState(state))
302214
return colorFunc(strings.Title(strings.ToLower(state)))
@@ -339,27 +251,3 @@ func issueProjectList(issue api.Issue) string {
339251
}
340252
return list
341253
}
342-
343-
func reactionGroupList(rgs api.ReactionGroups) string {
344-
var rs []string
345-
346-
for _, rg := range rgs {
347-
if r := formatReactionGroup(rg); r != "" {
348-
rs = append(rs, r)
349-
}
350-
}
351-
352-
return strings.Join(rs, " • ")
353-
}
354-
355-
func formatReactionGroup(rg api.ReactionGroup) string {
356-
c := rg.Count()
357-
if c == 0 {
358-
return ""
359-
}
360-
e := rg.Emoji()
361-
if e == "" {
362-
return ""
363-
}
364-
return fmt.Sprintf("%v %s", c, e)
365-
}

pkg/cmd/pr/shared/comments.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package shared
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/cli/cli/api"
9+
"github.com/cli/cli/pkg/iostreams"
10+
"github.com/cli/cli/pkg/markdown"
11+
"github.com/cli/cli/utils"
12+
)
13+
14+
func RawCommentList(comments api.Comments) string {
15+
var b strings.Builder
16+
for _, comment := range comments.Nodes {
17+
fmt.Fprint(&b, formatRawComment(comment))
18+
}
19+
return b.String()
20+
}
21+
22+
func formatRawComment(comment api.Comment) string {
23+
var b strings.Builder
24+
fmt.Fprintf(&b, "author:\t%s\n", comment.Author.Login)
25+
fmt.Fprintf(&b, "association:\t%s\n", strings.ToLower(comment.AuthorAssociation))
26+
fmt.Fprintf(&b, "edited:\t%t\n", comment.IncludesCreatedEdit)
27+
fmt.Fprintln(&b, "--")
28+
fmt.Fprintln(&b, comment.Body)
29+
fmt.Fprintln(&b, "--")
30+
return b.String()
31+
}
32+
33+
func CommentList(io *iostreams.IOStreams, comments api.Comments) (string, error) {
34+
var b strings.Builder
35+
cs := io.ColorScheme()
36+
retrievedCount := len(comments.Nodes)
37+
hiddenCount := comments.TotalCount - retrievedCount
38+
39+
if hiddenCount > 0 {
40+
fmt.Fprint(&b, cs.Gray(fmt.Sprintf("———————— Not showing %s ————————", utils.Pluralize(hiddenCount, "comment"))))
41+
fmt.Fprintf(&b, "\n\n\n")
42+
}
43+
44+
for i, comment := range comments.Nodes {
45+
last := i+1 == retrievedCount
46+
cmt, err := formatComment(io, comment, last)
47+
if err != nil {
48+
return "", err
49+
}
50+
fmt.Fprint(&b, cmt)
51+
if last {
52+
fmt.Fprintln(&b)
53+
}
54+
}
55+
56+
if hiddenCount > 0 {
57+
fmt.Fprint(&b, cs.Gray("Use --comments to view the full conversation"))
58+
fmt.Fprintln(&b)
59+
}
60+
61+
return b.String(), nil
62+
}
63+
64+
func formatComment(io *iostreams.IOStreams, comment api.Comment, newest bool) (string, error) {
65+
var b strings.Builder
66+
cs := io.ColorScheme()
67+
68+
// Header
69+
fmt.Fprint(&b, cs.Bold(comment.Author.Login))
70+
if comment.AuthorAssociation != "NONE" {
71+
fmt.Fprint(&b, cs.Bold(fmt.Sprintf(" (%s)", strings.ToLower(comment.AuthorAssociation))))
72+
}
73+
fmt.Fprint(&b, cs.Bold(fmt.Sprintf(" • %s", utils.FuzzyAgoAbbr(time.Now(), comment.CreatedAt))))
74+
if comment.IncludesCreatedEdit {
75+
fmt.Fprint(&b, cs.Bold(" • edited"))
76+
}
77+
if newest {
78+
fmt.Fprint(&b, cs.Bold(" • "))
79+
fmt.Fprint(&b, cs.CyanBold("Newest comment"))
80+
}
81+
fmt.Fprintln(&b)
82+
83+
// Reactions
84+
if reactions := ReactionGroupList(comment.ReactionGroups); reactions != "" {
85+
fmt.Fprint(&b, reactions)
86+
fmt.Fprintln(&b)
87+
}
88+
89+
// Body
90+
if comment.Body != "" {
91+
style := markdown.GetStyle(io.TerminalTheme())
92+
md, err := markdown.Render(comment.Body, style, "")
93+
if err != nil {
94+
return "", err
95+
}
96+
fmt.Fprint(&b, md)
97+
}
98+
99+
return b.String(), nil
100+
}

0 commit comments

Comments
 (0)
X Tutup