diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 8d72539be..61f15b84c 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -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 diff --git a/internal/output/output.go b/internal/output/output.go index c3c1346c3..9940f29fa 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -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" ) @@ -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 { @@ -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) } diff --git a/internal/output/output_test.go b/internal/output/output_test.go index 41b35552a..9a0c22335 100644 --- a/internal/output/output_test.go +++ b/internal/output/output_test.go @@ -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" @@ -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) { @@ -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()) + } + }) +} diff --git a/internal/output/prefixed.go b/internal/output/prefixed.go index cea2c7d35..8898f249b 100644 --- a/internal/output/prefixed.go +++ b/internal/output/prefixed.go @@ -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) { @@ -56,6 +72,11 @@ 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 @@ -63,6 +84,27 @@ func (pw *prefixWriter) writeLine(line string) error { 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 } diff --git a/setup.go b/setup.go index 1387f5290..a877a5971 100644 --- a/setup.go +++ b/setup.go @@ -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 }