Skip to content

Commit 00d17cc

Browse files
authored
output: add colored-tab (#3729)
1 parent 9c46d7d commit 00d17cc

File tree

13 files changed

+154
-36
lines changed

13 files changed

+154
-36
lines changed

.golangci.reference.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ run:
7676

7777
# output configuration options
7878
output:
79-
# Format: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions|teamcity
79+
# Format: colored-line-number|line-number|json|colored-tab|tab|checkstyle|code-climate|junit-xml|github-actions|teamcity
8080
#
8181
# Multiple can be specified by separating them by comma, output can be provided
8282
# for each of them by separating format name and path by colon symbol.

.golangci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ linters-settings:
1010
dupl:
1111
threshold: 100
1212
funlen:
13-
lines: 100
13+
lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner.
1414
statements: 50
1515
goconst:
1616
min-len: 2

pkg/commands/run.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,10 @@ func (e *Executor) createPrinter(format string, w io.Writer) (printers.Printer,
475475
p = printers.NewText(e.cfg.Output.PrintIssuedLine,
476476
format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName,
477477
e.log.Child(logutils.DebugKeyTextPrinter), w)
478-
case config.OutFormatTab:
479-
p = printers.NewTab(e.cfg.Output.PrintLinterName, e.log.Child(logutils.DebugKeyTabPrinter), w)
478+
case config.OutFormatTab, config.OutFormatColoredTab:
479+
p = printers.NewTab(e.cfg.Output.PrintLinterName,
480+
format == config.OutFormatColoredTab,
481+
e.log.Child(logutils.DebugKeyTabPrinter), w)
480482
case config.OutFormatCheckstyle:
481483
p = printers.NewCheckstyle(w)
482484
case config.OutFormatCodeClimate:

pkg/config/output.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const (
55
OutFormatLineNumber = "line-number"
66
OutFormatColoredLineNumber = "colored-line-number"
77
OutFormatTab = "tab"
8+
OutFormatColoredTab = "colored-tab"
89
OutFormatCheckstyle = "checkstyle"
910
OutFormatCodeClimate = "code-climate"
1011
OutFormatHTML = "html"
@@ -28,11 +29,13 @@ var OutFormats = []string{
2829

2930
type Output struct {
3031
Format string
31-
Color string
3232
PrintIssuedLine bool `mapstructure:"print-issued-lines"`
3333
PrintLinterName bool `mapstructure:"print-linter-name"`
3434
UniqByLine bool `mapstructure:"uniq-by-line"`
3535
SortResults bool `mapstructure:"sort-results"`
3636
PrintWelcomeMessage bool `mapstructure:"print-welcome"`
3737
PathPrefix string `mapstructure:"path-prefix"`
38+
39+
// only work with CLI flags because the setup of logs is done before the config file parsing.
40+
Color string
3841
}

pkg/golinters/nolintlint/nolintlint_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/golangci/golangci-lint/pkg/result"
1212
)
1313

14-
//nolint:funlen
1514
func TestLinter_Run(t *testing.T) {
1615
type issueWithReplacement struct {
1716
issue string

pkg/lint/lintersdb/enabled_set_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/golangci/golangci-lint/pkg/lint/linter"
1111
)
1212

13-
//nolint:funlen
1413
func TestGetEnabledLintersSet(t *testing.T) {
1514
type cs struct {
1615
cfg config.Linters

pkg/printers/tab.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,28 @@ import (
1414

1515
type Tab struct {
1616
printLinterName bool
17-
log logutils.Log
18-
w io.Writer
17+
useColors bool
18+
19+
log logutils.Log
20+
w io.Writer
1921
}
2022

21-
func NewTab(printLinterName bool, log logutils.Log, w io.Writer) *Tab {
23+
func NewTab(printLinterName, useColors bool, log logutils.Log, w io.Writer) *Tab {
2224
return &Tab{
2325
printLinterName: printLinterName,
26+
useColors: useColors,
2427
log: log,
2528
w: w,
2629
}
2730
}
2831

2932
func (p *Tab) SprintfColored(ca color.Attribute, format string, args ...any) string {
3033
c := color.New(ca)
34+
35+
if !p.useColors {
36+
c.DisableColor()
37+
}
38+
3139
return c.Sprintf(format, args...)
3240
}
3341

pkg/printers/tab_test.go

+50-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"go/token"
77
"testing"
88

9+
"github.com/fatih/color"
910
"github.com/stretchr/testify/assert"
1011
"github.com/stretchr/testify/require"
1112

@@ -14,6 +15,13 @@ import (
1415
)
1516

1617
func TestTab_Print(t *testing.T) {
18+
// force color globally
19+
backup := color.NoColor
20+
t.Cleanup(func() {
21+
color.NoColor = backup
22+
})
23+
color.NoColor = false
24+
1725
issues := []result.Issue{
1826
{
1927
FromLinter: "linter-a",
@@ -44,16 +52,50 @@ func TestTab_Print(t *testing.T) {
4452
},
4553
}
4654

47-
buf := new(bytes.Buffer)
55+
testCases := []struct {
56+
desc string
57+
printLinterName bool
58+
useColors bool
59+
expected string
60+
}{
61+
{
62+
desc: "with linter name",
63+
printLinterName: true,
64+
useColors: false,
65+
expected: `path/to/filea.go:10:4 linter-a some issue
66+
path/to/fileb.go:300:9 linter-b another issue
67+
`,
68+
},
69+
{
70+
desc: "disable all options",
71+
printLinterName: false,
72+
useColors: false,
73+
expected: `path/to/filea.go:10:4 some issue
74+
path/to/fileb.go:300:9 another issue
75+
`,
76+
},
77+
{
78+
desc: "enable all options",
79+
printLinterName: true,
80+
useColors: true,
81+
//nolint:lll // color characters must be in a simple string.
82+
expected: "\x1b[1mpath/to/filea.go:10\x1b[0m:4 linter-a \x1b[31msome issue\x1b[0m\n\x1b[1mpath/to/fileb.go:300\x1b[0m:9 linter-b \x1b[31manother issue\x1b[0m\n",
83+
},
84+
}
85+
86+
for _, test := range testCases {
87+
test := test
88+
t.Run(test.desc, func(t *testing.T) {
89+
t.Parallel()
4890

49-
printer := NewTab(true, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf)
91+
buf := new(bytes.Buffer)
5092

51-
err := printer.Print(context.Background(), issues)
52-
require.NoError(t, err)
93+
printer := NewTab(test.printLinterName, test.useColors, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf)
5394

54-
expected := `path/to/filea.go:10:4 linter-a some issue
55-
path/to/fileb.go:300:9 linter-b another issue
56-
`
95+
err := printer.Print(context.Background(), issues)
96+
require.NoError(t, err)
5797

58-
assert.Equal(t, expected, buf.String())
98+
assert.Equal(t, test.expected, buf.String())
99+
})
100+
}
59101
}

pkg/printers/text.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import (
1414

1515
type Text struct {
1616
printIssuedLine bool
17-
useColors bool
1817
printLinterName bool
18+
useColors bool
1919

2020
log logutils.Log
2121
w io.Writer
@@ -24,19 +24,20 @@ type Text struct {
2424
func NewText(printIssuedLine, useColors, printLinterName bool, log logutils.Log, w io.Writer) *Text {
2525
return &Text{
2626
printIssuedLine: printIssuedLine,
27-
useColors: useColors,
2827
printLinterName: printLinterName,
28+
useColors: useColors,
2929
log: log,
3030
w: w,
3131
}
3232
}
3333

3434
func (p *Text) SprintfColored(ca color.Attribute, format string, args ...any) string {
35+
c := color.New(ca)
36+
3537
if !p.useColors {
36-
return fmt.Sprintf(format, args...)
38+
c.DisableColor()
3739
}
3840

39-
c := color.New(ca)
4041
return c.Sprintf(format, args...)
4142
}
4243

@@ -73,7 +74,7 @@ func (p *Text) printSourceCode(i *result.Issue) {
7374
}
7475
}
7576

76-
func (p Text) printUnderLinePointer(i *result.Issue) {
77+
func (p *Text) printUnderLinePointer(i *result.Issue) {
7778
// if column == 0 it means column is unknown (e.g. for gosec)
7879
if len(i.SourceLines) != 1 || i.Pos.Column == 0 {
7980
return

pkg/printers/text_test.go

+77-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"go/token"
77
"testing"
88

9+
"github.com/fatih/color"
910
"github.com/stretchr/testify/assert"
1011
"github.com/stretchr/testify/require"
1112

@@ -14,6 +15,13 @@ import (
1415
)
1516

1617
func TestText_Print(t *testing.T) {
18+
// force color globally
19+
backup := color.NoColor
20+
t.Cleanup(func() {
21+
color.NoColor = backup
22+
})
23+
color.NoColor = false
24+
1725
issues := []result.Issue{
1826
{
1927
FromLinter: "linter-a",
@@ -44,19 +52,78 @@ func TestText_Print(t *testing.T) {
4452
},
4553
}
4654

47-
buf := new(bytes.Buffer)
48-
49-
printer := NewText(true, false, true, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf)
50-
51-
err := printer.Print(context.Background(), issues)
52-
require.NoError(t, err)
53-
54-
expected := `path/to/filea.go:10:4: some issue (linter-a)
55+
testCases := []struct {
56+
desc string
57+
printIssuedLine bool
58+
printLinterName bool
59+
useColors bool
60+
expected string
61+
}{
62+
{
63+
desc: "printIssuedLine and printLinterName",
64+
printIssuedLine: true,
65+
printLinterName: true,
66+
useColors: false,
67+
expected: `path/to/filea.go:10:4: some issue (linter-a)
68+
path/to/fileb.go:300:9: another issue (linter-b)
69+
func foo() {
70+
fmt.Println("bar")
71+
}
72+
`,
73+
},
74+
{
75+
desc: "printLinterName only",
76+
printIssuedLine: false,
77+
printLinterName: true,
78+
useColors: false,
79+
expected: `path/to/filea.go:10:4: some issue (linter-a)
5580
path/to/fileb.go:300:9: another issue (linter-b)
81+
`,
82+
},
83+
{
84+
desc: "printIssuedLine only",
85+
printIssuedLine: true,
86+
printLinterName: false,
87+
useColors: false,
88+
expected: `path/to/filea.go:10:4: some issue
89+
path/to/fileb.go:300:9: another issue
5690
func foo() {
5791
fmt.Println("bar")
5892
}
59-
`
93+
`,
94+
},
95+
{
96+
desc: "enable all options",
97+
printIssuedLine: true,
98+
printLinterName: true,
99+
useColors: true,
100+
//nolint:lll // color characters must be in a simple string.
101+
expected: "\x1b[1mpath/to/filea.go:10\x1b[0m:4: \x1b[31msome issue\x1b[0m (linter-a)\n\x1b[1mpath/to/fileb.go:300\x1b[0m:9: \x1b[31manother issue\x1b[0m (linter-b)\nfunc foo() {\n\tfmt.Println(\"bar\")\n}\n",
102+
},
103+
{
104+
desc: "disable all options",
105+
printIssuedLine: false,
106+
printLinterName: false,
107+
useColors: false,
108+
expected: `path/to/filea.go:10:4: some issue
109+
path/to/fileb.go:300:9: another issue
110+
`,
111+
},
112+
}
113+
114+
for _, test := range testCases {
115+
test := test
116+
t.Run(test.desc, func(t *testing.T) {
117+
t.Parallel()
118+
119+
buf := new(bytes.Buffer)
60120

61-
assert.Equal(t, expected, buf.String())
121+
printer := NewText(test.printIssuedLine, test.useColors, test.printLinterName, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf)
122+
123+
err := printer.Print(context.Background(), issues)
124+
require.NoError(t, err)
125+
126+
assert.Equal(t, test.expected, buf.String())
127+
})
128+
}
62129
}

test/enabled_linters_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/golangci/golangci-lint/test/testshared"
1111
)
1212

13-
//nolint:funlen
1413
func TestEnabledLinters(t *testing.T) {
1514
// require to display the message "Active x linters: [x,y]"
1615
t.Setenv(lintersdb.EnvTestRun, "1")

test/testshared/analysis_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/stretchr/testify/require"
99
)
1010

11-
//nolint:funlen
1211
func Test_parseComments(t *testing.T) {
1312
testCases := []struct {
1413
filename string

test/testshared/runner_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/golangci/golangci-lint/pkg/exitcodes"
1010
)
1111

12-
//nolint:funlen
1312
func TestRunnerBuilder_Runner(t *testing.T) {
1413
testCases := []struct {
1514
desc string

0 commit comments

Comments
 (0)