gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/copycheck/output/html.go (about) 1 package output 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "log" 8 "regexp" 9 "sort" 10 11 "github.com/360EntSecGroup-Skylar/goreporter/linters/copycheck/syntax" 12 ) 13 14 type HtmlPrinter struct { 15 iota int 16 *TextPrinter 17 } 18 19 func NewHtmlPrinter(w io.Writer, fr FileReader) *HtmlPrinter { 20 fmt.Fprint(w, `<!DOCTYPE html> 21 <meta charset="utf-8"/> 22 <title>Duplicates</title> 23 <style> 24 pre { 25 background-color: #FFD; 26 border: 1px solid #E2E2E2; 27 padding: 1ex; 28 } 29 </style> 30 `) 31 return &HtmlPrinter{ 32 TextPrinter: NewTextPrinter(w, fr), 33 } 34 } 35 36 func (p *HtmlPrinter) Print(dups [][]*syntax.Node) { 37 p.iota++ 38 fmt.Fprintf(p.writer, "<h1>#%d found %d clones</h1>\n", p.iota, len(dups)) 39 40 clones := make([]clone, len(dups)) 41 for i, dup := range dups { 42 cnt := len(dup) 43 if cnt == 0 { 44 log.Fatal("zero length dup") 45 } 46 nstart := dup[0] 47 nend := dup[cnt-1] 48 49 file, err := p.freader.ReadFile(nstart) 50 if err != nil { 51 log.Fatal(err) 52 } 53 54 lineStart, _ := blockLines(file, nstart.Pos, nend.End) 55 cl := clone{filename: nstart.Filename, lineStart: lineStart} 56 start := findLineBeg(file, nstart.Pos) 57 content := append(toWhitespace(file[start:nstart.Pos]), file[nstart.Pos:nend.End]...) 58 cl.fragment = deindent(content) 59 clones[i] = cl 60 } 61 62 sort.Sort(byNameAndLine(clones)) 63 for _, cl := range clones { 64 fmt.Fprintf(p.writer, "<h2>%s:%d</h2>\n<pre>%s</pre>\n", cl.filename, cl.lineStart, cl.fragment) 65 } 66 } 67 68 func (*HtmlPrinter) Finish() {} 69 70 func findLineBeg(file []byte, index int) int { 71 for i := index; i >= 0; i-- { 72 if file[i] == '\n' { 73 return i + 1 74 } 75 } 76 return 0 77 } 78 79 func toWhitespace(str []byte) []byte { 80 var out []byte 81 for _, c := range bytes.Runes(str) { 82 if c == '\t' { 83 out = append(out, '\t') 84 } else { 85 out = append(out, ' ') 86 } 87 } 88 return out 89 } 90 91 func deindent(block []byte) []byte { 92 const maxVal = 99 93 min := maxVal 94 re := regexp.MustCompile(`(^|\n)(\t*)\S`) 95 for _, line := range re.FindAllSubmatch(block, -1) { 96 indent := line[2] 97 if len(indent) < min { 98 min = len(indent) 99 } 100 } 101 if min == 0 || min == maxVal { 102 return block 103 } 104 block = block[min:] 105 Loop: 106 for i := 0; i < len(block); i++ { 107 if block[i] == '\n' && i != len(block)-1 { 108 for j := 0; j < min; j++ { 109 if block[i+j+1] != '\t' { 110 continue Loop 111 } 112 } 113 block = append(block[:i+1], block[i+1+min:]...) 114 } 115 } 116 return block 117 }