github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/formatter/friendly.go (about) 1 package formatter 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 8 "github.com/fatih/color" 9 "github.com/mgechev/revive/lint" 10 "github.com/olekukonko/tablewriter" 11 ) 12 13 var newLines = map[rune]bool{ 14 0x000A: true, 15 0x000B: true, 16 0x000C: true, 17 0x000D: true, 18 0x0085: true, 19 0x2028: true, 20 0x2029: true, 21 } 22 23 func getErrorEmoji() string { 24 return color.RedString("✘") 25 } 26 27 func getWarningEmoji() string { 28 return color.YellowString("⚠") 29 } 30 31 // Friendly is an implementation of the Formatter interface 32 // which formats the errors to JSON. 33 type Friendly struct { 34 Metadata lint.FormatterMetadata 35 } 36 37 // Name returns the name of the formatter 38 func (f *Friendly) Name() string { 39 return "friendly" 40 } 41 42 // Format formats the failures gotten from the lint. 43 func (f *Friendly) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { 44 errorMap := map[string]int{} 45 warningMap := map[string]int{} 46 totalErrors := 0 47 totalWarnings := 0 48 for failure := range failures { 49 sev := severity(config, failure) 50 f.printFriendlyFailure(failure, sev) 51 if sev == lint.SeverityWarning { 52 warningMap[failure.RuleName] = warningMap[failure.RuleName] + 1 53 totalWarnings++ 54 } 55 if sev == lint.SeverityError { 56 errorMap[failure.RuleName] = errorMap[failure.RuleName] + 1 57 totalErrors++ 58 } 59 } 60 f.printSummary(totalErrors, totalWarnings) 61 f.printStatistics(color.RedString("Errors:"), errorMap) 62 f.printStatistics(color.YellowString("Warnings:"), warningMap) 63 return "", nil 64 } 65 66 func (f *Friendly) printFriendlyFailure(failure lint.Failure, severity lint.Severity) { 67 f.printHeaderRow(failure, severity) 68 f.printFilePosition(failure) 69 fmt.Println() 70 fmt.Println() 71 } 72 73 func (f *Friendly) printHeaderRow(failure lint.Failure, severity lint.Severity) { 74 emoji := getWarningEmoji() 75 if severity == lint.SeverityError { 76 emoji = getErrorEmoji() 77 } 78 fmt.Print(f.table([][]string{{emoji, "https://revive.run/r#" + failure.RuleName, color.GreenString(failure.Failure)}})) 79 } 80 81 func (f *Friendly) printFilePosition(failure lint.Failure) { 82 fmt.Printf(" %s:%d:%d", failure.GetFilename(), failure.Position.Start.Line, failure.Position.Start.Column) 83 } 84 85 type statEntry struct { 86 name string 87 failures int 88 } 89 90 func (f *Friendly) printSummary(errors, warnings int) { 91 emoji := getWarningEmoji() 92 if errors > 0 { 93 emoji = getErrorEmoji() 94 } 95 problemsLabel := "problems" 96 if errors+warnings == 1 { 97 problemsLabel = "problem" 98 } 99 warningsLabel := "warnings" 100 if warnings == 1 { 101 warningsLabel = "warning" 102 } 103 errorsLabel := "errors" 104 if errors == 1 { 105 errorsLabel = "error" 106 } 107 str := fmt.Sprintf("%d %s (%d %s, %d %s)", errors+warnings, problemsLabel, errors, errorsLabel, warnings, warningsLabel) 108 if errors > 0 { 109 fmt.Printf("%s %s\n", emoji, color.RedString(str)) 110 fmt.Println() 111 return 112 } 113 if warnings > 0 { 114 fmt.Printf("%s %s\n", emoji, color.YellowString(str)) 115 fmt.Println() 116 return 117 } 118 } 119 120 func (f *Friendly) printStatistics(header string, stats map[string]int) { 121 if len(stats) == 0 { 122 return 123 } 124 var data []statEntry 125 for name, total := range stats { 126 data = append(data, statEntry{name, total}) 127 } 128 sort.Slice(data, func(i, j int) bool { 129 return data[i].failures > data[j].failures 130 }) 131 formatted := [][]string{} 132 for _, entry := range data { 133 formatted = append(formatted, []string{color.GreenString(fmt.Sprintf("%d", entry.failures)), entry.name}) 134 } 135 fmt.Println(header) 136 fmt.Println(f.table(formatted)) 137 } 138 139 func (f *Friendly) table(rows [][]string) string { 140 buf := new(bytes.Buffer) 141 table := tablewriter.NewWriter(buf) 142 table.SetBorder(false) 143 table.SetColumnSeparator("") 144 table.SetRowSeparator("") 145 table.SetAutoWrapText(false) 146 table.AppendBulk(rows) 147 table.Render() 148 return buf.String() 149 }