From f62d6074105d1096a2f03cfbc4b2e26cc2733c66 Mon Sep 17 00:00:00 2001 From: Denis Isaev Date: Sat, 2 Jun 2018 20:53:36 +0300 Subject: [PATCH] #37: add tab output format: --out-format=tab --- README.md | 4 +-- pkg/commands/run.go | 14 +++++++--- pkg/config/config.go | 5 ++-- pkg/printers/tab.go | 64 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 pkg/printers/tab.go diff --git a/README.md b/README.md index 1ef34947..e35d61a2 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ Usage: golangci-lint run [flags] Flags: - --out-format string Format of output: colored-line-number|line-number|json (default "colored-line-number") + --out-format string Format of output: colored-line-number|line-number|json|tab (default "colored-line-number") --print-issued-lines Print lines of code with issue (default true) --print-linter-name Print linter name in issue line (default true) --issues-exit-code int Exit code when issues were found (default 1) @@ -236,7 +236,7 @@ Flags: -e, --exclude strings Exclude issue by regexp --exclude-use-default Use or not use default excludes: # errcheck: Almost all programs ignore errors on these functions and in most cases it's ok - - Error return value of .((os\.)?std(out|err)\..*|.*Close|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked # golint: Annoying issue about not having a comment. The rare codebase has such comments - (should have comment|comment on exported method|should have a package comment) diff --git a/pkg/commands/run.go b/pkg/commands/run.go index a5ec9cb7..65762265 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -235,12 +235,19 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error { } var p printers.Printer - if e.cfg.Output.Format == config.OutFormatJSON { + format := e.cfg.Output.Format + switch format { + case config.OutFormatJSON: p = printers.NewJSON() - } else { + case config.OutFormatColoredLineNumber, config.OutFormatLineNumber: p = printers.NewText(e.cfg.Output.PrintIssuedLine, - e.cfg.Output.Format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName) + format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName) + case config.OutFormatTab: + p = printers.NewTab(e.cfg.Output.PrintLinterName) + default: + return fmt.Errorf("unknown output format %s", format) } + gotAnyIssues, err := p.Print(ctx, issues) if err != nil { return fmt.Errorf("can't print %d issues: %s", len(issues), err) @@ -296,6 +303,7 @@ func (e *Executor) parseConfig() { e.initFlagSet(fs) e.initRootFlagSet(fs) + fs.Usage = func() {} // otherwise help text will be printed twice if err := fs.Parse(os.Args); err != nil { if err == pflag.ErrHelp { return diff --git a/pkg/config/config.go b/pkg/config/config.go index 08654ecd..17915080 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -10,9 +10,10 @@ const ( OutFormatJSON = "json" OutFormatLineNumber = "line-number" OutFormatColoredLineNumber = "colored-line-number" + OutFormatTab = "tab" ) -var OutFormats = []string{OutFormatColoredLineNumber, OutFormatLineNumber, OutFormatJSON} +var OutFormats = []string{OutFormatColoredLineNumber, OutFormatLineNumber, OutFormatJSON, OutFormatTab} type ExcludePattern struct { Pattern string @@ -22,7 +23,7 @@ type ExcludePattern struct { var DefaultExcludePatterns = []ExcludePattern{ { - Pattern: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked", + Pattern: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked", Linter: "errcheck", Why: "Almost all programs ignore errors on these functions and in most cases it's ok", }, diff --git a/pkg/printers/tab.go b/pkg/printers/tab.go new file mode 100644 index 00000000..c677d480 --- /dev/null +++ b/pkg/printers/tab.go @@ -0,0 +1,64 @@ +package printers + +import ( + "context" + "fmt" + "io" + "text/tabwriter" + + "github.com/fatih/color" + "github.com/golangci/golangci-lint/pkg/result" + "github.com/sirupsen/logrus" +) + +type Tab struct { + printLinterName bool +} + +func NewTab(printLinterName bool) *Tab { + return &Tab{ + printLinterName: printLinterName, + } +} + +func (p Tab) SprintfColored(ca color.Attribute, format string, args ...interface{}) string { + c := color.New(ca) + return c.Sprintf(format, args...) +} + +func (p *Tab) Print(ctx context.Context, issues <-chan result.Issue) (bool, error) { + w := tabwriter.NewWriter(StdOut, 0, 0, 2, ' ', 0) + + issuesN := 0 + for i := range issues { + issuesN++ + p.printIssue(&i, w) + } + + if issuesN != 0 { + logrus.Infof("Found %d issues", issuesN) + } else if ctx.Err() == nil { // don't print "congrats" if timeouted + outStr := p.SprintfColored(color.FgGreen, "Congrats! No issues were found.") + fmt.Fprintln(StdOut, outStr) + } + + if err := w.Flush(); err != nil { + logrus.Warnf("Can't flush tab writer: %s", err) + } + + return issuesN != 0, nil +} + +func (p Tab) printIssue(i *result.Issue, w io.Writer) { + text := p.SprintfColored(color.FgRed, "%s", i.Text) + if p.printLinterName { + text = fmt.Sprintf("%s\t%s", i.FromLinter, text) + } + + pos := p.SprintfColored(color.Bold, "%s:%d", i.FilePath(), i.Line()) + if i.Pos.Column != 0 { + pos += fmt.Sprintf(":%d", i.Pos.Column) + } + + fmt.Fprintf(w, "%s\t%s\n", pos, text) +}