Improved list output (#281)

remove unused debug var

move outputList into a struct

so we can add additional functionality for all list output

rename list output to table.go

make table sortable

sort milestones

sort milestones descending

remove unnecessary if

Co-authored-by: Norwin Roosen <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/281
Reviewed-by: khmarbaise <khmarbaise@noreply.gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-Authored-By: Norwin <noerw@noreply.gitea.io>
Co-Committed-By: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
Norwin 2020-12-10 06:04:36 +08:00 committed by 6543
parent 4a11cf455f
commit a91168fd36
11 changed files with 141 additions and 170 deletions

View File

@ -26,20 +26,14 @@ func IssueDetails(issue *gitea.Issue) {
// IssuesList prints a listing of issues
func IssuesList(issues []*gitea.Issue, output string) {
var values [][]string
headers := []string{
t := tableWithHeader(
"Index",
"Title",
"State",
"Author",
"Milestone",
"Updated",
}
if len(issues) == 0 {
outputList(output, headers, values)
return
}
)
for _, issue := range issues {
author := issue.Poster.FullName
@ -50,38 +44,29 @@ func IssuesList(issues []*gitea.Issue, output string) {
if issue.Milestone != nil {
mile = issue.Milestone.Title
}
values = append(
values,
[]string{
strconv.FormatInt(issue.Index, 10),
issue.Title,
string(issue.State),
author,
mile,
FormatTime(issue.Updated),
},
t.addRow(
strconv.FormatInt(issue.Index, 10),
issue.Title,
string(issue.State),
author,
mile,
FormatTime(issue.Updated),
)
}
outputList(output, headers, values)
t.print(output)
}
// IssuesPullsList prints a listing of issues & pulls
// TODO combine with IssuesList
func IssuesPullsList(issues []*gitea.Issue, output string) {
var values [][]string
headers := []string{
t := tableWithHeader(
"Index",
"State",
"Kind",
"Author",
"Updated",
"Title",
}
if len(issues) == 0 {
outputList(output, headers, values)
return
}
)
for _, issue := range issues {
name := issue.Poster.FullName
@ -92,18 +77,15 @@ func IssuesPullsList(issues []*gitea.Issue, output string) {
if issue.PullRequest != nil {
kind = "Pull"
}
values = append(
values,
[]string{
strconv.FormatInt(issue.Index, 10),
string(issue.State),
kind,
name,
FormatTime(issue.Updated),
issue.Title,
},
t.addRow(
strconv.FormatInt(issue.Index, 10),
string(issue.State),
kind,
name,
FormatTime(issue.Updated),
issue.Title,
)
}
outputList(output, headers, values)
t.print(output)
}

View File

@ -14,33 +14,24 @@ import (
// LabelsList prints a listing of labels
func LabelsList(labels []*gitea.Label, output string) {
var values [][]string
headers := []string{
t := tableWithHeader(
"Index",
"Color",
"Name",
"Description",
}
if len(labels) == 0 {
outputList(output, headers, values)
return
}
)
p := termenv.ColorProfile()
for _, label := range labels {
color := termenv.String(label.Color)
values = append(
values,
[]string{
strconv.FormatInt(label.ID, 10),
fmt.Sprint(color.Background(p.Color("#" + label.Color))),
label.Name,
label.Description,
},
t.addRow(
strconv.FormatInt(label.ID, 10),
fmt.Sprint(color.Background(p.Color("#"+label.Color))),
label.Name,
label.Description,
)
}
outputList(output, headers, values)
t.print(output)
}

View File

@ -33,24 +33,23 @@ func LoginDetails(login *config.Login, output string) {
// LoginsList prints a listing of logins
func LoginsList(logins []config.Login, output string) {
var values [][]string
headers := []string{
t := tableWithHeader(
"Name",
"URL",
"SSHHost",
"User",
"Default",
}
)
for _, l := range logins {
values = append(values, []string{
t.addRow(
l.Name,
l.URL,
l.GetSSHHost(),
l.User,
fmt.Sprint(l.Default),
})
)
}
outputList(output, headers, values)
t.print(output)
}

View File

@ -25,7 +25,6 @@ func MilestoneDetails(milestone *gitea.Milestone) {
// MilestonesList prints a listing of milestones
func MilestonesList(miles []*gitea.Milestone, output string, state gitea.StateType) {
headers := []string{
"Title",
}
@ -37,7 +36,7 @@ func MilestonesList(miles []*gitea.Milestone, output string, state gitea.StateTy
"DueDate",
)
var values [][]string
t := table{headers: headers}
for _, m := range miles {
var deadline = ""
@ -56,8 +55,9 @@ func MilestonesList(miles []*gitea.Milestone, output string, state gitea.StateTy
fmt.Sprintf("%d/%d", m.OpenIssues, m.ClosedIssues),
deadline,
)
values = append(values, item)
t.addRowSlice(item)
}
outputList(output, headers, values)
t.sort(0, true)
t.print(output)
}

View File

@ -12,7 +12,6 @@ import (
// NotificationsList prints a listing of notification threads
func NotificationsList(news []*gitea.NotificationThread, output string, showRepository bool) {
var values [][]string
headers := []string{
"Type",
"Index",
@ -22,6 +21,8 @@ func NotificationsList(news []*gitea.NotificationThread, output string, showRepo
headers = append(headers, "Repository")
}
t := table{headers: headers}
for _, n := range news {
if n.Subject == nil {
continue
@ -41,11 +42,10 @@ func NotificationsList(news []*gitea.NotificationThread, output string, showRepo
if showRepository {
item = append(item, n.Repository.FullName)
}
values = append(values, item)
t.addRowSlice(item)
}
if len(values) != 0 {
outputList(output, headers, values)
if t.Len() != 0 {
t.print(output)
}
return
}

View File

@ -17,28 +17,23 @@ func OrganizationsList(organizations []*gitea.Organization, output string) {
return
}
headers := []string{
t := tableWithHeader(
"Name",
"FullName",
"Website",
"Location",
"Description",
}
var values [][]string
)
for _, org := range organizations {
values = append(
values,
[]string{
org.UserName,
org.FullName,
org.Website,
org.Location,
org.Description,
},
t.addRow(
org.UserName,
org.FullName,
org.Website,
org.Location,
org.Description,
)
}
outputList(output, headers, values)
t.print(output)
}

View File

@ -60,20 +60,14 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview) {
// PullsList prints a listing of pulls
func PullsList(prs []*gitea.PullRequest, output string) {
var values [][]string
headers := []string{
t := tableWithHeader(
"Index",
"Title",
"State",
"Author",
"Milestone",
"Updated",
}
if len(prs) == 0 {
outputList(output, headers, values)
return
}
)
for _, pr := range prs {
if pr == nil {
@ -87,18 +81,15 @@ func PullsList(prs []*gitea.PullRequest, output string) {
if pr.Milestone != nil {
mile = pr.Milestone.Title
}
values = append(
values,
[]string{
strconv.FormatInt(pr.Index, 10),
pr.Title,
string(pr.State),
author,
mile,
FormatTime(*pr.Updated),
},
t.addRow(
strconv.FormatInt(pr.Index, 10),
pr.Title,
string(pr.State),
author,
mile,
FormatTime(*pr.Updated),
)
}
outputList(output, headers, values)
t.print(output)
}

View File

@ -10,19 +10,13 @@ import (
// ReleasesList prints a listing of releases
func ReleasesList(releases []*gitea.Release, output string) {
var values [][]string
headers := []string{
t := tableWithHeader(
"Tag-Name",
"Title",
"Published At",
"Status",
"Tar URL",
}
if len(releases) == 0 {
outputList(output, headers, values)
return
}
)
for _, release := range releases {
status := "released"
@ -31,17 +25,14 @@ func ReleasesList(releases []*gitea.Release, output string) {
} else if release.IsPrerelease {
status = "prerelease"
}
values = append(
values,
[]string{
release.TagName,
release.Title,
FormatTime(release.PublishedAt),
status,
release.TarURL,
},
t.addRow(
release.TagName,
release.Title,
FormatTime(release.PublishedAt),
status,
release.TarURL,
)
}
outputList(output, headers, values)
t.print(output)
}

View File

@ -90,7 +90,8 @@ func ReposList(repos []*gitea.Repository, output string, fields []string) {
}
}
outputList(output, fields, values)
t := table{headers: fields, values: values}
t.print(output)
}
// RepoDetails print an repo formatted to stdout

View File

@ -1,4 +1,4 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@ -7,19 +7,67 @@ package print
import (
"fmt"
"os"
"sort"
"strconv"
"strings"
"github.com/olekukonko/tablewriter"
)
var (
showLog bool
)
// table provides infrastructure to easily print (sorted) lists in different formats
type table struct {
headers []string
values [][]string
sortDesc bool // used internally by sortable interface
sortColumn uint // ↑
}
// errorf printf content as an error information
func errorf(format string, a ...interface{}) {
fmt.Printf(format, a...)
func tableWithHeader(header ...string) table {
return table{headers: header}
}
// it's the callers responsibility to ensure row length is equal to header length!
func (t *table) addRow(row ...string) {
t.addRowSlice(row)
}
// it's the callers responsibility to ensure row length is equal to header length!
func (t *table) addRowSlice(row []string) {
t.values = append(t.values, row)
}
func (t *table) sort(column uint, desc bool) {
t.sortColumn = column
t.sortDesc = desc
sort.Stable(t) // stable to allow multiple calls to sort
}
// sortable interface
func (t table) Len() int { return len(t.values) }
func (t table) Swap(i, j int) { t.values[i], t.values[j] = t.values[j], t.values[i] }
func (t table) Less(i, j int) bool {
const column = 0
if t.sortDesc {
i, j = j, i
}
return t.values[i][t.sortColumn] < t.values[j][t.sortColumn]
}
func (t *table) print(output string) {
switch {
case output == "" || output == "table":
outputtable(t.headers, t.values)
case output == "csv":
outputdsv(t.headers, t.values, ",")
case output == "simple":
outputsimple(t.headers, t.values)
case output == "tsv":
outputdsv(t.headers, t.values, "\t")
case output == "yaml":
outputyaml(t.headers, t.values)
default:
fmt.Printf("unknown output type '" + output + "', available types are:\n- csv: comma-separated values\n- simple: space-separated values\n- table: auto-aligned table format (default)\n- tsv: tab-separated values\n- yaml: YAML format\n")
}
}
// outputtable prints structured data as table
@ -71,22 +119,3 @@ func outputyaml(headers []string, values [][]string) {
}
}
}
// outputList provides general function to convert given list of items
// into several outputs (table, csv, simple, tsv, yaml)
func outputList(output string, headers []string, values [][]string) {
switch {
case output == "" || output == "table":
outputtable(headers, values)
case output == "csv":
outputdsv(headers, values, ",")
case output == "simple":
outputsimple(headers, values)
case output == "tsv":
outputdsv(headers, values, "\t")
case output == "yaml":
outputyaml(headers, values)
default:
errorf("unknown output type '" + output + "', available types are:\n- csv: comma-separated values\n- simple: space-separated values\n- table: auto-aligned table format (default)\n- tsv: tab-separated values\n- yaml: YAML format\n")
}
}

View File

@ -23,7 +23,12 @@ func formatDuration(seconds int64, outputType string) string {
// TrackedTimesList print list of tracked times to stdout
func TrackedTimesList(times []*gitea.TrackedTime, outputType string, from, until time.Time, printTotal bool) {
var outputValues [][]string
tab := tableWithHeader(
"Created",
"Issue",
"User",
"Duration",
)
var totalDuration int64
for _, t := range times {
@ -35,29 +40,16 @@ func TrackedTimesList(times []*gitea.TrackedTime, outputType string, from, until
}
totalDuration += t.Time
outputValues = append(
outputValues,
[]string{
FormatTime(t.Created),
"#" + strconv.FormatInt(t.Issue.Index, 10),
t.UserName,
formatDuration(t.Time, outputType),
},
tab.addRow(
FormatTime(t.Created),
"#"+strconv.FormatInt(t.Issue.Index, 10),
t.UserName,
formatDuration(t.Time, outputType),
)
}
if printTotal {
outputValues = append(outputValues, []string{
"TOTAL", "", "", formatDuration(totalDuration, outputType),
})
tab.addRow("TOTAL", "", "", formatDuration(totalDuration, outputType))
}
headers := []string{
"Created",
"Issue",
"User",
"Duration",
}
outputList(outputType, headers, outputValues)
tab.print(outputType)
}