diff --git a/.golangci.reference.yml b/.golangci.reference.yml index f7e67fbb2d98..467381bccbf6 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -76,7 +76,7 @@ run: # output configuration options output: - # Format: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions|teamcity + # Format: colored-line-number|line-number|json|colored-tab|tab|checkstyle|code-climate|junit-xml|github-actions|teamcity # # Multiple can be specified by separating them by comma, output can be provided # for each of them by separating format name and path by colon symbol. diff --git a/.golangci.yml b/.golangci.yml index 41d6c55dc349..cd1fbe1940bb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,7 +10,7 @@ linters-settings: dupl: threshold: 100 funlen: - lines: 100 + lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner. statements: 50 goconst: min-len: 2 diff --git a/pkg/commands/run.go b/pkg/commands/run.go index 20106b44f624..453b3a666d61 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -475,8 +475,10 @@ func (e *Executor) createPrinter(format string, w io.Writer) (printers.Printer, p = printers.NewText(e.cfg.Output.PrintIssuedLine, format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName, e.log.Child(logutils.DebugKeyTextPrinter), w) - case config.OutFormatTab: - p = printers.NewTab(e.cfg.Output.PrintLinterName, e.log.Child(logutils.DebugKeyTabPrinter), w) + case config.OutFormatTab, config.OutFormatColoredTab: + p = printers.NewTab(e.cfg.Output.PrintLinterName, + format == config.OutFormatColoredTab, + e.log.Child(logutils.DebugKeyTabPrinter), w) case config.OutFormatCheckstyle: p = printers.NewCheckstyle(w) case config.OutFormatCodeClimate: diff --git a/pkg/config/output.go b/pkg/config/output.go index 2c49ea7f4e66..e8726392055d 100644 --- a/pkg/config/output.go +++ b/pkg/config/output.go @@ -5,6 +5,7 @@ const ( OutFormatLineNumber = "line-number" OutFormatColoredLineNumber = "colored-line-number" OutFormatTab = "tab" + OutFormatColoredTab = "colored-tab" OutFormatCheckstyle = "checkstyle" OutFormatCodeClimate = "code-climate" OutFormatHTML = "html" @@ -28,11 +29,13 @@ var OutFormats = []string{ type Output struct { Format string - Color string PrintIssuedLine bool `mapstructure:"print-issued-lines"` PrintLinterName bool `mapstructure:"print-linter-name"` UniqByLine bool `mapstructure:"uniq-by-line"` SortResults bool `mapstructure:"sort-results"` PrintWelcomeMessage bool `mapstructure:"print-welcome"` PathPrefix string `mapstructure:"path-prefix"` + + // only work with CLI flags because the setup of logs is done before the config file parsing. + Color string } diff --git a/pkg/golinters/nolintlint/nolintlint_test.go b/pkg/golinters/nolintlint/nolintlint_test.go index 3dba4d6578b3..0400e8dfe623 100644 --- a/pkg/golinters/nolintlint/nolintlint_test.go +++ b/pkg/golinters/nolintlint/nolintlint_test.go @@ -11,7 +11,6 @@ import ( "github.com/golangci/golangci-lint/pkg/result" ) -//nolint:funlen func TestLinter_Run(t *testing.T) { type issueWithReplacement struct { issue string diff --git a/pkg/lint/lintersdb/enabled_set_test.go b/pkg/lint/lintersdb/enabled_set_test.go index f9b6393f71e3..a5586b586cb5 100644 --- a/pkg/lint/lintersdb/enabled_set_test.go +++ b/pkg/lint/lintersdb/enabled_set_test.go @@ -10,7 +10,6 @@ import ( "github.com/golangci/golangci-lint/pkg/lint/linter" ) -//nolint:funlen func TestGetEnabledLintersSet(t *testing.T) { type cs struct { cfg config.Linters diff --git a/pkg/printers/tab.go b/pkg/printers/tab.go index e3f6e266f5ce..f58451e4dc88 100644 --- a/pkg/printers/tab.go +++ b/pkg/printers/tab.go @@ -14,13 +14,16 @@ import ( type Tab struct { printLinterName bool - log logutils.Log - w io.Writer + useColors bool + + log logutils.Log + w io.Writer } -func NewTab(printLinterName bool, log logutils.Log, w io.Writer) *Tab { +func NewTab(printLinterName, useColors bool, log logutils.Log, w io.Writer) *Tab { return &Tab{ printLinterName: printLinterName, + useColors: useColors, log: log, w: w, } @@ -28,6 +31,11 @@ func NewTab(printLinterName bool, log logutils.Log, w io.Writer) *Tab { func (p *Tab) SprintfColored(ca color.Attribute, format string, args ...any) string { c := color.New(ca) + + if !p.useColors { + c.DisableColor() + } + return c.Sprintf(format, args...) } diff --git a/pkg/printers/tab_test.go b/pkg/printers/tab_test.go index 882699ef853c..c887d24280c3 100644 --- a/pkg/printers/tab_test.go +++ b/pkg/printers/tab_test.go @@ -6,6 +6,7 @@ import ( "go/token" "testing" + "github.com/fatih/color" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,6 +15,13 @@ import ( ) func TestTab_Print(t *testing.T) { + // force color globally + backup := color.NoColor + t.Cleanup(func() { + color.NoColor = backup + }) + color.NoColor = false + issues := []result.Issue{ { FromLinter: "linter-a", @@ -44,16 +52,50 @@ func TestTab_Print(t *testing.T) { }, } - buf := new(bytes.Buffer) + testCases := []struct { + desc string + printLinterName bool + useColors bool + expected string + }{ + { + desc: "with linter name", + printLinterName: true, + useColors: false, + expected: `path/to/filea.go:10:4 linter-a some issue +path/to/fileb.go:300:9 linter-b another issue +`, + }, + { + desc: "disable all options", + printLinterName: false, + useColors: false, + expected: `path/to/filea.go:10:4 some issue +path/to/fileb.go:300:9 another issue +`, + }, + { + desc: "enable all options", + printLinterName: true, + useColors: true, + //nolint:lll // color characters must be in a simple string. + 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", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() - printer := NewTab(true, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf) + buf := new(bytes.Buffer) - err := printer.Print(context.Background(), issues) - require.NoError(t, err) + printer := NewTab(test.printLinterName, test.useColors, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf) - expected := `path/to/filea.go:10:4 linter-a some issue -path/to/fileb.go:300:9 linter-b another issue -` + err := printer.Print(context.Background(), issues) + require.NoError(t, err) - assert.Equal(t, expected, buf.String()) + assert.Equal(t, test.expected, buf.String()) + }) + } } diff --git a/pkg/printers/text.go b/pkg/printers/text.go index 3b715acd0629..e7be17bbf046 100644 --- a/pkg/printers/text.go +++ b/pkg/printers/text.go @@ -14,8 +14,8 @@ import ( type Text struct { printIssuedLine bool - useColors bool printLinterName bool + useColors bool log logutils.Log w io.Writer @@ -24,19 +24,20 @@ type Text struct { func NewText(printIssuedLine, useColors, printLinterName bool, log logutils.Log, w io.Writer) *Text { return &Text{ printIssuedLine: printIssuedLine, - useColors: useColors, printLinterName: printLinterName, + useColors: useColors, log: log, w: w, } } func (p *Text) SprintfColored(ca color.Attribute, format string, args ...any) string { + c := color.New(ca) + if !p.useColors { - return fmt.Sprintf(format, args...) + c.DisableColor() } - c := color.New(ca) return c.Sprintf(format, args...) } @@ -73,7 +74,7 @@ func (p *Text) printSourceCode(i *result.Issue) { } } -func (p Text) printUnderLinePointer(i *result.Issue) { +func (p *Text) printUnderLinePointer(i *result.Issue) { // if column == 0 it means column is unknown (e.g. for gosec) if len(i.SourceLines) != 1 || i.Pos.Column == 0 { return diff --git a/pkg/printers/text_test.go b/pkg/printers/text_test.go index 18f7c272f872..0d07c1cb6727 100644 --- a/pkg/printers/text_test.go +++ b/pkg/printers/text_test.go @@ -6,6 +6,7 @@ import ( "go/token" "testing" + "github.com/fatih/color" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,6 +15,13 @@ import ( ) func TestText_Print(t *testing.T) { + // force color globally + backup := color.NoColor + t.Cleanup(func() { + color.NoColor = backup + }) + color.NoColor = false + issues := []result.Issue{ { FromLinter: "linter-a", @@ -44,19 +52,78 @@ func TestText_Print(t *testing.T) { }, } - buf := new(bytes.Buffer) - - printer := NewText(true, false, true, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf) - - err := printer.Print(context.Background(), issues) - require.NoError(t, err) - - expected := `path/to/filea.go:10:4: some issue (linter-a) + testCases := []struct { + desc string + printIssuedLine bool + printLinterName bool + useColors bool + expected string + }{ + { + desc: "printIssuedLine and printLinterName", + printIssuedLine: true, + printLinterName: true, + useColors: false, + expected: `path/to/filea.go:10:4: some issue (linter-a) +path/to/fileb.go:300:9: another issue (linter-b) +func foo() { + fmt.Println("bar") +} +`, + }, + { + desc: "printLinterName only", + printIssuedLine: false, + printLinterName: true, + useColors: false, + expected: `path/to/filea.go:10:4: some issue (linter-a) path/to/fileb.go:300:9: another issue (linter-b) +`, + }, + { + desc: "printIssuedLine only", + printIssuedLine: true, + printLinterName: false, + useColors: false, + expected: `path/to/filea.go:10:4: some issue +path/to/fileb.go:300:9: another issue func foo() { fmt.Println("bar") } -` +`, + }, + { + desc: "enable all options", + printIssuedLine: true, + printLinterName: true, + useColors: true, + //nolint:lll // color characters must be in a simple string. + 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", + }, + { + desc: "disable all options", + printIssuedLine: false, + printLinterName: false, + useColors: false, + expected: `path/to/filea.go:10:4: some issue +path/to/fileb.go:300:9: another issue +`, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + buf := new(bytes.Buffer) - assert.Equal(t, expected, buf.String()) + printer := NewText(test.printIssuedLine, test.useColors, test.printLinterName, logutils.NewStderrLog(logutils.DebugKeyEmpty), buf) + + err := printer.Print(context.Background(), issues) + require.NoError(t, err) + + assert.Equal(t, test.expected, buf.String()) + }) + } } diff --git a/test/enabled_linters_test.go b/test/enabled_linters_test.go index 15a7d76825f6..7a2aa702ccd5 100644 --- a/test/enabled_linters_test.go +++ b/test/enabled_linters_test.go @@ -10,7 +10,6 @@ import ( "github.com/golangci/golangci-lint/test/testshared" ) -//nolint:funlen func TestEnabledLinters(t *testing.T) { // require to display the message "Active x linters: [x,y]" t.Setenv(lintersdb.EnvTestRun, "1") diff --git a/test/testshared/analysis_test.go b/test/testshared/analysis_test.go index 2c7f69666d06..7446b041c2f7 100644 --- a/test/testshared/analysis_test.go +++ b/test/testshared/analysis_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" ) -//nolint:funlen func Test_parseComments(t *testing.T) { testCases := []struct { filename string diff --git a/test/testshared/runner_test.go b/test/testshared/runner_test.go index fd8817cc35a2..d7e0d32f0baa 100644 --- a/test/testshared/runner_test.go +++ b/test/testshared/runner_test.go @@ -9,7 +9,6 @@ import ( "github.com/golangci/golangci-lint/pkg/exitcodes" ) -//nolint:funlen func TestRunnerBuilder_Runner(t *testing.T) { testCases := []struct { desc string