feat: add Issues 2.0 fields to issue view
Display new issue metadata in TTY view: - Issue type on state line (gray, before Open/Closed) - Type, Parent, Blocked by, Blocking metadata rows - Sub-issues section with completion progress (X/Y, Z%) - Cross-repo references show full owner/repo#N format All new fields included in defaultFields and JSON export. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
c00257386e
commit
7dae882c9d
2 changed files with 76 additions and 2 deletions
|
|
@ -92,6 +92,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
|
|||
var defaultFields = []string{
|
||||
"number", "url", "state", "createdAt", "title", "body", "author", "milestone",
|
||||
"assignees", "labels", "reactionGroups", "lastComment", "stateReason",
|
||||
"issueType", "parent", "subIssues", "subIssuesSummary", "blockedBy", "blocking",
|
||||
}
|
||||
|
||||
func viewRun(opts *ViewOptions) error {
|
||||
|
|
@ -219,9 +220,15 @@ func printHumanIssuePreview(opts *ViewOptions, baseRepo ghrepo.Interface, issue
|
|||
|
||||
// Header (Title and State)
|
||||
fmt.Fprintf(out, "%s %s#%d\n", cs.Bold(issue.Title), ghrepo.FullName(baseRepo), issue.Number)
|
||||
|
||||
// State line — include issue type prefix when present
|
||||
stateLine := issueStateTitleWithColor(cs, issue)
|
||||
if issue.IssueType != nil {
|
||||
stateLine = cs.Muted(issue.IssueType.Name) + " · " + stateLine
|
||||
}
|
||||
fmt.Fprintf(out,
|
||||
"%s • %s opened %s • %s\n",
|
||||
issueStateTitleWithColor(cs, issue),
|
||||
"%s · %s opened %s · %s\n",
|
||||
stateLine,
|
||||
issue.Author.DisplayName(),
|
||||
text.FuzzyAgo(opts.Now(), issue.CreatedAt),
|
||||
text.Pluralize(issue.Comments.TotalCount, "comment"),
|
||||
|
|
@ -242,6 +249,22 @@ func printHumanIssuePreview(opts *ViewOptions, baseRepo ghrepo.Interface, issue
|
|||
fmt.Fprint(out, cs.Bold("Labels: "))
|
||||
fmt.Fprintln(out, labels)
|
||||
}
|
||||
if issue.IssueType != nil {
|
||||
fmt.Fprint(out, cs.Bold("Type: "))
|
||||
fmt.Fprintln(out, issue.IssueType.Name)
|
||||
}
|
||||
if issue.Parent != nil {
|
||||
fmt.Fprint(out, cs.Bold("Parent: "))
|
||||
fmt.Fprintln(out, formatLinkedIssueRef(baseRepo, issue.Parent)+" "+issue.Parent.Title)
|
||||
}
|
||||
if blockedBy := formatLinkedIssueList(baseRepo, issue.BlockedBy.Nodes); blockedBy != "" {
|
||||
fmt.Fprint(out, cs.Bold("Blocked by: "))
|
||||
fmt.Fprintln(out, blockedBy)
|
||||
}
|
||||
if blocking := formatLinkedIssueList(baseRepo, issue.Blocking.Nodes); blocking != "" {
|
||||
fmt.Fprint(out, cs.Bold("Blocking: "))
|
||||
fmt.Fprintln(out, blocking)
|
||||
}
|
||||
if projects := issueProjectList(*issue); projects != "" {
|
||||
fmt.Fprint(out, cs.Bold("Projects: "))
|
||||
fmt.Fprintln(out, projects)
|
||||
|
|
@ -266,6 +289,30 @@ func printHumanIssuePreview(opts *ViewOptions, baseRepo ghrepo.Interface, issue
|
|||
}
|
||||
fmt.Fprintf(out, "\n%s\n", md)
|
||||
|
||||
// Sub-issues section
|
||||
if issue.SubIssuesSummary.Total > 0 {
|
||||
fmt.Fprintf(out, "%s · %d/%d (%d%%)\n",
|
||||
cs.Bold("Sub-issues"),
|
||||
issue.SubIssuesSummary.Completed,
|
||||
issue.SubIssuesSummary.Total,
|
||||
int(issue.SubIssuesSummary.PercentCompleted),
|
||||
)
|
||||
for _, sub := range issue.SubIssues.Nodes {
|
||||
stateColor := cs.Green
|
||||
stateLabel := "Open"
|
||||
if sub.State == "CLOSED" {
|
||||
stateColor = cs.Magenta
|
||||
stateLabel = "Closed"
|
||||
}
|
||||
fmt.Fprintf(out, "%s %s %s\n",
|
||||
stateColor(stateLabel),
|
||||
formatLinkedIssueRef(baseRepo, &sub),
|
||||
sub.Title,
|
||||
)
|
||||
}
|
||||
fmt.Fprintln(out)
|
||||
}
|
||||
|
||||
// Comments
|
||||
if issue.Comments.TotalCount > 0 {
|
||||
preview := !opts.Comments
|
||||
|
|
@ -282,6 +329,27 @@ func printHumanIssuePreview(opts *ViewOptions, baseRepo ghrepo.Interface, issue
|
|||
return nil
|
||||
}
|
||||
|
||||
// formatLinkedIssueRef formats an issue reference, using just #N for same-repo
|
||||
// or owner/repo#N for cross-repo references.
|
||||
func formatLinkedIssueRef(baseRepo ghrepo.Interface, issue *api.LinkedIssue) string {
|
||||
if issue.Repository.NameWithOwner != "" && issue.Repository.NameWithOwner != ghrepo.FullName(baseRepo) {
|
||||
return fmt.Sprintf("%s#%d", issue.Repository.NameWithOwner, issue.Number)
|
||||
}
|
||||
return fmt.Sprintf("%s#%d", ghrepo.FullName(baseRepo), issue.Number)
|
||||
}
|
||||
|
||||
// formatLinkedIssueList formats a comma-separated list of linked issue references with titles.
|
||||
func formatLinkedIssueList(baseRepo ghrepo.Interface, issues []api.LinkedIssue) string {
|
||||
if len(issues) == 0 {
|
||||
return ""
|
||||
}
|
||||
parts := make([]string, len(issues))
|
||||
for i, issue := range issues {
|
||||
parts[i] = formatLinkedIssueRef(baseRepo, &issue) + " " + issue.Title
|
||||
}
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
|
||||
func issueStateTitleWithColor(cs *iostreams.ColorScheme, issue *api.Issue) string {
|
||||
colorFunc := cs.ColorFromString(prShared.ColorForIssueState(*issue))
|
||||
state := "Open"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ func TestJSONFields(t *testing.T) {
|
|||
"url",
|
||||
"isPinned",
|
||||
"stateReason",
|
||||
"issueType",
|
||||
"parent",
|
||||
"subIssues",
|
||||
"subIssuesSummary",
|
||||
"blockedBy",
|
||||
"blocking",
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue