Skip to content

Commit

Permalink
feat: colorize tasks in prefixed output (#1572)
Browse files Browse the repository at this point in the history
* feat: Colorize tasks in prefixed output

* chore: comment and style changes

* fix code tag has spaces in api reference

* fix: migrate to use logger for colors

* fix: Add bright colors to the color sequence

* fix: make colorized prefix logger standard
  • Loading branch information
AlexanderArvidsson authored Jun 3, 2024
1 parent 0810ef0 commit 856ba3b
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 11 deletions.
24 changes: 24 additions & 0 deletions internal/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ func Red() PrintFunc {
return color.New(envColor("TASK_COLOR_RED", color.FgRed)...).FprintfFunc()
}

func BrightBlue() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_BLUE", color.FgHiBlue)...).FprintfFunc()
}

func BrightGreen() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_GREEN", color.FgHiGreen)...).FprintfFunc()
}

func BrightCyan() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_CYAN", color.FgHiCyan)...).FprintfFunc()
}

func BrightYellow() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_YELLOW", color.FgHiYellow)...).FprintfFunc()
}

func BrightMagenta() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_MAGENTA", color.FgHiMagenta)...).FprintfFunc()
}

func BrightRed() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_RED", color.FgHiRed)...).FprintfFunc()
}

func envColor(env string, defaultColor color.Attribute) []color.Attribute {
if os.Getenv("FORCE_COLOR") != "" {
color.NoColor = false
Expand Down
5 changes: 3 additions & 2 deletions internal/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"

"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile/ast"
)
Expand All @@ -15,7 +16,7 @@ type Output interface {
type CloseFunc func(err error) error

// Build the Output for the requested ast.Output.
func BuildFor(o *ast.Output) (Output, error) {
func BuildFor(o *ast.Output, logger *logger.Logger) (Output, error) {
switch o.Name {
case "interleaved", "":
if err := checkOutputGroupUnset(o); err != nil {
Expand All @@ -32,7 +33,7 @@ func BuildFor(o *ast.Output) (Output, error) {
if err := checkOutputGroupUnset(o); err != nil {
return nil, err
}
return Prefixed{}, nil
return NewPrefixed(logger), nil
default:
return nil, fmt.Errorf(`task: output style %q not recognized`, o.Name)
}
Expand Down
38 changes: 37 additions & 1 deletion internal/output/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"io"
"testing"

"github.com/fatih/color"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/omap"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/internal/templater"
Expand Down Expand Up @@ -107,7 +109,11 @@ func TestGroupErrorOnlyShowsOutputOnError(t *testing.T) {

func TestPrefixed(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Prefixed{}
l := &logger.Logger{
Color: false,
}

var o output.Output = output.NewPrefixed(l)
w, _, cleanup := o.WrapWriter(&b, io.Discard, "prefix", nil)

t.Run("simple use cases", func(t *testing.T) {
Expand All @@ -132,3 +138,33 @@ func TestPrefixed(t *testing.T) {
assert.Equal(t, "[prefix] Test!\n", b.String())
})
}

func TestPrefixedWithColor(t *testing.T) {
color.NoColor = false

var b bytes.Buffer
l := &logger.Logger{
Color: true,
}

var o output.Output = output.NewPrefixed(l)

writers := make([]io.Writer, 16)
for i := range writers {
writers[i], _, _ = o.WrapWriter(&b, io.Discard, fmt.Sprintf("prefix-%d", i), nil)
}

t.Run("colors should loop", func(t *testing.T) {
for i, w := range writers {
b.Reset()

color := output.PrefixColorSequence[i%len(output.PrefixColorSequence)]

var prefix bytes.Buffer
l.FOutf(&prefix, color, fmt.Sprintf("prefix-%d", i))

fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, fmt.Sprintf("[%s] foo\n[%s] bar\n", prefix.String(), prefix.String()), b.String())
}
})
}
56 changes: 49 additions & 7 deletions internal/output/prefixed.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,36 @@ import (
"io"
"strings"

"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
)

type Prefixed struct{}
type Prefixed struct {
logger *logger.Logger
seen map[string]uint
counter *uint
}

func NewPrefixed(logger *logger.Logger) Prefixed {
var counter uint

return Prefixed{
seen: make(map[string]uint),
counter: &counter,
logger: logger,
}
}

func (Prefixed) WrapWriter(stdOut, _ io.Writer, prefix string, _ *templater.Cache) (io.Writer, io.Writer, CloseFunc) {
pw := &prefixWriter{writer: stdOut, prefix: prefix}
func (p Prefixed) WrapWriter(stdOut, _ io.Writer, prefix string, _ *templater.Cache) (io.Writer, io.Writer, CloseFunc) {
pw := &prefixWriter{writer: stdOut, prefix: prefix, prefixed: &p}
return pw, pw, func(error) error { return pw.close() }
}

type prefixWriter struct {
writer io.Writer
prefix string
buff bytes.Buffer
writer io.Writer
prefixed *Prefixed
prefix string
buff bytes.Buffer
}

func (pw *prefixWriter) Write(p []byte) (int, error) {
Expand Down Expand Up @@ -56,13 +72,39 @@ func (pw *prefixWriter) writeOutputLines(force bool) error {
}
}

var PrefixColorSequence = []logger.Color{
logger.Yellow, logger.Blue, logger.Magenta, logger.Cyan, logger.Green, logger.Red,
logger.BrightYellow, logger.BrightBlue, logger.BrightMagenta, logger.BrightCyan, logger.BrightGreen, logger.BrightRed,
}

func (pw *prefixWriter) writeLine(line string) error {
if line == "" {
return nil
}
if !strings.HasSuffix(line, "\n") {
line += "\n"
}
_, err := fmt.Fprintf(pw.writer, "[%s] %s", pw.prefix, line)

idx, ok := pw.prefixed.seen[pw.prefix]

if !ok {
idx = *pw.prefixed.counter
pw.prefixed.seen[pw.prefix] = idx

*pw.prefixed.counter++
}

if _, err := fmt.Fprint(pw.writer, "["); err != nil {
return nil
}

color := PrefixColorSequence[idx%uint(len(PrefixColorSequence))]
pw.prefixed.logger.FOutf(pw.writer, color, pw.prefix)

if _, err := fmt.Fprint(pw.writer, "] "); err != nil {
return nil
}

_, err := fmt.Fprint(pw.writer, line)
return err
}
2 changes: 1 addition & 1 deletion setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (e *Executor) setupOutput() error {
}

var err error
e.Output, err = output.BuildFor(&e.OutputStyle)
e.Output, err = output.BuildFor(&e.OutputStyle, e.Logger)
return err
}

Expand Down

0 comments on commit 856ba3b

Please sign in to comment.