github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/pkg/printers/text.go (about)

     1  package printers
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strings"
     7  
     8  	"github.com/fatih/color"
     9  
    10  	"github.com/vanstinator/golangci-lint/pkg/logutils"
    11  	"github.com/vanstinator/golangci-lint/pkg/result"
    12  )
    13  
    14  type Text struct {
    15  	printIssuedLine bool
    16  	printLinterName bool
    17  	useColors       bool
    18  
    19  	log logutils.Log
    20  	w   io.Writer
    21  }
    22  
    23  func NewText(printIssuedLine, useColors, printLinterName bool, log logutils.Log, w io.Writer) *Text {
    24  	return &Text{
    25  		printIssuedLine: printIssuedLine,
    26  		printLinterName: printLinterName,
    27  		useColors:       useColors,
    28  		log:             log,
    29  		w:               w,
    30  	}
    31  }
    32  
    33  func (p *Text) SprintfColored(ca color.Attribute, format string, args ...any) string {
    34  	c := color.New(ca)
    35  
    36  	if !p.useColors {
    37  		c.DisableColor()
    38  	}
    39  
    40  	return c.Sprintf(format, args...)
    41  }
    42  
    43  func (p *Text) Print(issues []result.Issue) error {
    44  	for i := range issues {
    45  		p.printIssue(&issues[i])
    46  
    47  		if !p.printIssuedLine {
    48  			continue
    49  		}
    50  
    51  		p.printSourceCode(&issues[i])
    52  		p.printUnderLinePointer(&issues[i])
    53  	}
    54  
    55  	return nil
    56  }
    57  
    58  func (p *Text) printIssue(i *result.Issue) {
    59  	text := p.SprintfColored(color.FgRed, "%s", strings.TrimSpace(i.Text))
    60  	if p.printLinterName {
    61  		text += fmt.Sprintf(" (%s)", i.FromLinter)
    62  	}
    63  	pos := p.SprintfColored(color.Bold, "%s:%d", i.FilePath(), i.Line())
    64  	if i.Pos.Column != 0 {
    65  		pos += fmt.Sprintf(":%d", i.Pos.Column)
    66  	}
    67  	fmt.Fprintf(p.w, "%s: %s\n", pos, text)
    68  }
    69  
    70  func (p *Text) printSourceCode(i *result.Issue) {
    71  	for _, line := range i.SourceLines {
    72  		fmt.Fprintln(p.w, line)
    73  	}
    74  }
    75  
    76  func (p *Text) printUnderLinePointer(i *result.Issue) {
    77  	// if column == 0 it means column is unknown (e.g. for gosec)
    78  	if len(i.SourceLines) != 1 || i.Pos.Column == 0 {
    79  		return
    80  	}
    81  
    82  	col0 := i.Pos.Column - 1
    83  	line := i.SourceLines[0]
    84  	prefixRunes := make([]rune, 0, len(line))
    85  	for j := 0; j < len(line) && j < col0; j++ {
    86  		if line[j] == '\t' {
    87  			prefixRunes = append(prefixRunes, '\t')
    88  		} else {
    89  			prefixRunes = append(prefixRunes, ' ')
    90  		}
    91  	}
    92  
    93  	fmt.Fprintf(p.w, "%s%s\n", string(prefixRunes), p.SprintfColored(color.FgYellow, "^"))
    94  }