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  }