aboutsummaryrefslogtreecommitdiffstats
path: root/commands
diff options
context:
space:
mode:
Diffstat (limited to 'commands')
-rw-r--r--commands/bug/bug.go107
-rw-r--r--commands/bug/bug_comment_test.go7
-rw-r--r--commands/bug/bug_test.go50
-rw-r--r--commands/execenv/env.go13
-rw-r--r--commands/execenv/env_testing.go12
5 files changed, 95 insertions, 94 deletions
diff --git a/commands/bug/bug.go b/commands/bug/bug.go
index 7004f5c1..4c83ffa5 100644
--- a/commands/bug/bug.go
+++ b/commands/bug/bug.go
@@ -15,6 +15,7 @@ import (
"github.com/MichaelMure/git-bug/commands/execenv"
"github.com/MichaelMure/git-bug/entities/bug"
"github.com/MichaelMure/git-bug/entities/common"
+ "github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/query"
"github.com/MichaelMure/git-bug/util/colors"
)
@@ -92,10 +93,10 @@ git bug status:open --by creation "foo bar" baz
flags.StringVarP(&options.sortDirection, "direction", "d", "asc",
"Select the sorting direction. Valid values are [asc,desc]")
cmd.RegisterFlagCompletionFunc("direction", completion.From([]string{"asc", "desc"}))
- flags.StringVarP(&options.outputFormat, "format", "f", "default",
- "Select the output formatting style. Valid values are [default,plain,compact,id,json,org-mode]")
+ flags.StringVarP(&options.outputFormat, "format", "f", "",
+ "Select the output formatting style. Valid values are [default,plain,id,json,org-mode]")
cmd.RegisterFlagCompletionFunc("format",
- completion.From([]string{"default", "plain", "compact", "id", "json", "org-mode"}))
+ completion.From([]string{"default", "plain", "id", "json", "org-mode"}))
const selectGroup = "select"
cmd.AddGroup(&cobra.Group{ID: selectGroup, Title: "Implicit selection"})
@@ -145,28 +146,32 @@ func runBug(env *execenv.Env, opts bugOptions, args []string) error {
return err
}
- bugExcerpt := make([]*cache.BugExcerpt, len(allIds))
+ excerpts := make([]*cache.BugExcerpt, len(allIds))
for i, id := range allIds {
b, err := env.Backend.Bugs().ResolveExcerpt(id)
if err != nil {
return err
}
- bugExcerpt[i] = b
+ excerpts[i] = b
}
switch opts.outputFormat {
- case "org-mode":
- return bugsOrgmodeFormatter(env, bugExcerpt)
+ case "":
+ if env.Out.IsTerminal() {
+ return bugsDefaultFormatter(env, excerpts)
+ } else {
+ return bugsPlainFormatter(env, excerpts)
+ }
+ case "default":
+ return bugsDefaultFormatter(env, excerpts)
+ case "id":
+ return bugsIDFormatter(env, excerpts)
case "plain":
- return bugsPlainFormatter(env, bugExcerpt)
+ return bugsPlainFormatter(env, excerpts)
case "json":
- return bugsJsonFormatter(env, bugExcerpt)
- case "compact":
- return bugsCompactFormatter(env, bugExcerpt)
- case "id":
- return bugsIDFormatter(env, bugExcerpt)
- case "default":
- return bugsDefaultFormatter(env, bugExcerpt)
+ return bugsJsonFormatter(env, excerpts)
+ case "org-mode":
+ return bugsOrgmodeFormatter(env, excerpts)
default:
return fmt.Errorf("unknown format %s", opts.outputFormat)
}
@@ -185,9 +190,9 @@ func repairQuery(args []string) string {
return strings.Join(args, " ")
}
-func bugsJsonFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error {
- jsonBugs := make([]cmdjson.BugExcerpt, len(bugExcerpts))
- for i, b := range bugExcerpts {
+func bugsJsonFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error {
+ jsonBugs := make([]cmdjson.BugExcerpt, len(excerpts))
+ for i, b := range excerpts {
jsonBug, err := cmdjson.NewBugExcerpt(env.Backend, b)
if err != nil {
return err
@@ -197,42 +202,34 @@ func bugsJsonFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error
return env.Out.PrintJSON(jsonBugs)
}
-func bugsCompactFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error {
- for _, b := range bugExcerpts {
- author, err := env.Backend.Identities().ResolveExcerpt(b.AuthorId)
- if err != nil {
- return err
- }
-
- var labelsTxt strings.Builder
- for _, l := range b.Labels {
- lc256 := l.Color().Term256()
- labelsTxt.WriteString(lc256.Escape())
- labelsTxt.WriteString("◼")
- labelsTxt.WriteString(lc256.Unescape())
- }
-
- env.Out.Printf("%s %s %s %s %s\n",
- colors.Cyan(b.Id().Human()),
- colors.Yellow(b.Status),
- text.LeftPadMaxLine(strings.TrimSpace(b.Title), 40, 0),
- text.LeftPadMaxLine(labelsTxt.String(), 5, 0),
- colors.Magenta(text.TruncateMax(author.DisplayName(), 15)),
- )
+func bugsIDFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error {
+ for _, b := range excerpts {
+ env.Out.Println(b.Id().String())
}
+
return nil
}
-func bugsIDFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error {
- for _, b := range bugExcerpts {
- env.Out.Println(b.Id().String())
+func bugsDefaultFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error {
+ width := env.Out.Width()
+ widthId := entity.HumanIdLength
+ widthStatus := len("closed")
+ widthComment := 6
+
+ widthRemaining := width -
+ widthId - 1 -
+ widthStatus - 1 -
+ widthComment - 1
+
+ widthTitle := int(float32(widthRemaining-3) * 0.7)
+ if widthTitle < 0 {
+ widthTitle = 0
}
- return nil
-}
+ widthRemaining = widthRemaining - widthTitle - 3 - 2
+ widthAuthor := widthRemaining
-func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error {
- for _, b := range bugExcerpts {
+ for _, b := range excerpts {
author, err := env.Backend.Identities().ResolveExcerpt(b.AuthorId)
if err != nil {
return err
@@ -248,8 +245,8 @@ func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err
// truncate + pad if needed
labelsFmt := text.TruncateMax(labelsTxt.String(), 10)
- titleFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Title), 50-text.Len(labelsFmt), 0)
- authorFmt := text.LeftPadMaxLine(author.DisplayName(), 15, 0)
+ titleFmt := text.LeftPadMaxLine(strings.TrimSpace(b.Title), widthTitle-text.Len(labelsFmt), 0)
+ authorFmt := text.LeftPadMaxLine(author.DisplayName(), widthAuthor, 0)
comments := fmt.Sprintf("%3d 💬", b.LenComments-1)
if b.LenComments-1 <= 0 {
@@ -259,7 +256,7 @@ func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err
comments = " ∞ 💬"
}
- env.Out.Printf("%s\t%s\t%s\t%s\t%s\n",
+ env.Out.Printf("%s\t%s\t%s %s %s\n",
colors.Cyan(b.Id().Human()),
colors.Yellow(b.Status),
titleFmt+labelsFmt,
@@ -270,14 +267,14 @@ func bugsDefaultFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err
return nil
}
-func bugsPlainFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error {
- for _, b := range bugExcerpts {
- env.Out.Printf("%s [%s] %s\n", b.Id().Human(), b.Status, strings.TrimSpace(b.Title))
+func bugsPlainFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error {
+ for _, b := range excerpts {
+ env.Out.Printf("%s\t%s\t%s\n", b.Id().Human(), b.Status, strings.TrimSpace(b.Title))
}
return nil
}
-func bugsOrgmodeFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) error {
+func bugsOrgmodeFormatter(env *execenv.Env, excerpts []*cache.BugExcerpt) error {
// see https://orgmode.org/manual/Tags.html
orgTagRe := regexp.MustCompile("[^[:alpha:]_@]")
formatTag := func(l bug.Label) string {
@@ -290,7 +287,7 @@ func bugsOrgmodeFormatter(env *execenv.Env, bugExcerpts []*cache.BugExcerpt) err
env.Out.Println("#+TODO: OPEN | CLOSED")
- for _, b := range bugExcerpts {
+ for _, b := range excerpts {
status := strings.ToUpper(b.Status.String())
var title string
diff --git a/commands/bug/bug_comment_test.go b/commands/bug/bug_comment_test.go
index c1dc9952..5625f1be 100644
--- a/commands/bug/bug_comment_test.go
+++ b/commands/bug/bug_comment_test.go
@@ -2,7 +2,7 @@ package bugcmd
import (
"fmt"
- "io/ioutil"
+ "os"
"strings"
"testing"
"time"
@@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/MichaelMure/git-bug/commands/bug/testenv"
- "github.com/MichaelMure/git-bug/commands/cmdtest"
"github.com/MichaelMure/git-bug/commands/execenv"
)
@@ -143,7 +142,7 @@ func requireCommentsEqual(t *testing.T, golden string, env *execenv.Env) {
t.Log("Got here")
for i, comment := range comments {
fileName := fmt.Sprintf(goldenFilePattern, golden, i)
- require.NoError(t, ioutil.WriteFile(fileName, []byte(comment.message), 0644))
+ require.NoError(t, os.WriteFile(fileName, []byte(comment.message), 0644))
}
}
@@ -157,7 +156,7 @@ func requireCommentsEqual(t *testing.T, golden string, env *execenv.Env) {
require.Equal(t, date.Add(time.Duration(i)*time.Minute), comment.date)
fileName := fmt.Sprintf(goldenFilePattern, golden, i)
- exp, err := ioutil.ReadFile(fileName)
+ exp, err := os.ReadFile(fileName)
require.NoError(t, err)
require.Equal(t, strings.ReplaceAll(string(exp), "\r", ""), strings.ReplaceAll(comment.message, "\r", ""))
}
diff --git a/commands/bug/bug_test.go b/commands/bug/bug_test.go
index cb6a4373..e6fadd6f 100644
--- a/commands/bug/bug_test.go
+++ b/commands/bug/bug_test.go
@@ -2,7 +2,6 @@ package bugcmd
import (
"encoding/json"
- "fmt"
"testing"
"github.com/stretchr/testify/require"
@@ -61,44 +60,33 @@ $`
format string
exp string
}{
- {"default", "^[0-9a-f]{7}\topen\tthis is a bug title \tJohn Doe \t\n$"},
- {"plain", "^[0-9a-f]{7} \\[open\\] this is a bug title\n$"},
- {"compact", "^[0-9a-f]{7} open this is a bug title John Doe\n$"},
+ {"default", "^[0-9a-f]{7}\topen\tthis is a bug title John Doe \n$"},
+ {"plain", "^[0-9a-f]{7}\topen\tthis is a bug title\n$"},
{"id", "^[0-9a-f]{64}\n$"},
{"org-mode", expOrgMode},
+ {"json", ".*"},
}
for _, testcase := range cases {
- opts := bugOptions{
- sortDirection: "asc",
- sortBy: "creation",
- outputFormat: testcase.format,
- }
-
- name := fmt.Sprintf("with %s format", testcase.format)
-
- t.Run(name, func(t *testing.T) {
+ t.Run(testcase.format, func(t *testing.T) {
env, _ := testenv.NewTestEnvAndBug(t)
+ opts := bugOptions{
+ sortDirection: "asc",
+ sortBy: "creation",
+ outputFormat: testcase.format,
+ }
+
require.NoError(t, runBug(env, opts, []string{}))
- require.Regexp(t, testcase.exp, env.Out.String())
+
+ switch testcase.format {
+ case "json":
+ var bugs []cmdjson.BugExcerpt
+ require.NoError(t, json.Unmarshal(env.Out.Bytes(), &bugs))
+ require.Len(t, bugs, 1)
+ default:
+ require.Regexp(t, testcase.exp, env.Out.String())
+ }
})
}
-
- t.Run("with JSON format", func(t *testing.T) {
- opts := bugOptions{
- sortDirection: "asc",
- sortBy: "creation",
- outputFormat: "json",
- }
-
- env, _ := testenv.NewTestEnvAndBug(t)
-
- require.NoError(t, runBug(env, opts, []string{}))
-
- var bugs []cmdjson.BugExcerpt
- require.NoError(t, json.Unmarshal(env.Out.Bytes(), &bugs))
-
- require.Len(t, bugs, 1)
- })
}
diff --git a/commands/execenv/env.go b/commands/execenv/env.go
index 46de8401..e693807e 100644
--- a/commands/execenv/env.go
+++ b/commands/execenv/env.go
@@ -7,6 +7,7 @@ import (
"os"
"github.com/mattn/go-isatty"
+ "golang.org/x/term"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/repository"
@@ -57,6 +58,8 @@ type Out interface {
// IsTerminal tells if the output is a user terminal (rather than a buffer,
// a pipe ...), which tells if we can use colors and other interactive features.
IsTerminal() bool
+ // Width return the width of the attached terminal, or a good enough value.
+ Width() int
// Raw return the underlying io.Writer, or itself if not.
// This is useful if something need to access the raw file descriptor.
@@ -123,6 +126,16 @@ func (o out) IsTerminal() bool {
return false
}
+func (o out) Width() int {
+ if f, ok := o.Raw().(*os.File); ok {
+ width, _, err := term.GetSize(int(f.Fd()))
+ if err == nil {
+ return width
+ }
+ }
+ return 80
+}
+
func (o out) Raw() io.Writer {
return o.Writer
}
diff --git a/commands/execenv/env_testing.go b/commands/execenv/env_testing.go
index 03fe0430..15d7b646 100644
--- a/commands/execenv/env_testing.go
+++ b/commands/execenv/env_testing.go
@@ -20,14 +20,14 @@ type TestIn struct {
forceIsTerminal bool
}
-func (t *TestIn) ForceIsTerminal(value bool) {
- t.forceIsTerminal = value
-}
-
func (t *TestIn) IsTerminal() bool {
return t.forceIsTerminal
}
+func (t *TestIn) ForceIsTerminal(value bool) {
+ t.forceIsTerminal = value
+}
+
var _ Out = &TestOut{}
type TestOut struct {
@@ -60,6 +60,10 @@ func (te *TestOut) IsTerminal() bool {
return te.forceIsTerminal
}
+func (te *TestOut) Width() int {
+ return 80
+}
+
func (te *TestOut) Raw() io.Writer {
return te.Buffer
}