github.com/haya14busa/reviewdog@v0.0.0-20180723114510-ffb00ef78fd3/filter.go (about)

     1  package reviewdog
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  
     7  	"github.com/haya14busa/reviewdog/diff"
     8  )
     9  
    10  // FilteredCheck represents CheckResult with filtering info.
    11  type FilteredCheck struct {
    12  	*CheckResult
    13  	InDiff   bool
    14  	LnumDiff int
    15  }
    16  
    17  // FilterCheck filters check results by diff. It doesn't drop check which
    18  // is not in diff but set FilteredCheck.InDiff field false.
    19  func FilterCheck(results []*CheckResult, diff []*diff.FileDiff, strip int, wd string) []*FilteredCheck {
    20  	checks := make([]*FilteredCheck, 0, len(results))
    21  
    22  	addedlines := addedDiffLines(diff, strip)
    23  
    24  	for _, result := range results {
    25  		check := &FilteredCheck{CheckResult: result}
    26  
    27  		addedline := addedlines.Get(result.Path, result.Lnum)
    28  		result.Path = CleanPath(result.Path, wd)
    29  		if addedline != nil {
    30  			check.InDiff = true
    31  			check.LnumDiff = addedline.LnumDiff
    32  		}
    33  
    34  		checks = append(checks, check)
    35  	}
    36  
    37  	return checks
    38  }
    39  
    40  // CleanPath clean up given path. If workdir is not empty, it returns relative
    41  // path to the given workdir.
    42  func CleanPath(path, workdir string) string {
    43  	p := path
    44  	if filepath.IsAbs(path) && workdir != "" {
    45  		relPath, err := filepath.Rel(workdir, path)
    46  		if err == nil {
    47  			p = relPath
    48  		}
    49  	}
    50  	p = filepath.Clean(p)
    51  	if p == "." {
    52  		return ""
    53  	}
    54  	return p
    55  }
    56  
    57  // addedLine represents added line in diff.
    58  type addedLine struct {
    59  	Path     string // path to new file
    60  	Lnum     int    // the line number in the new file
    61  	LnumDiff int    // the line number of the diff (Same as Lnumdiff of diff.Line)
    62  	Content  string // line content
    63  }
    64  
    65  // posToAddedLine is a hash table of normalized path to line number to addedLine.
    66  type posToAddedLine map[string]map[int]*addedLine
    67  
    68  func (p posToAddedLine) Get(path string, lnum int) *addedLine {
    69  	npath, err := normalizePath(path)
    70  	if err != nil {
    71  		return nil
    72  	}
    73  	ltodiff, ok := p[npath]
    74  	if !ok {
    75  		return nil
    76  	}
    77  	diffline, ok := ltodiff[lnum]
    78  	if !ok {
    79  		return nil
    80  	}
    81  	return diffline
    82  }
    83  
    84  // addedDiffLines traverse []*diff.FileDiff and returns posToAddedLine.
    85  func addedDiffLines(filediffs []*diff.FileDiff, strip int) posToAddedLine {
    86  	r := make(posToAddedLine)
    87  	for _, filediff := range filediffs {
    88  		path := filediff.PathNew
    89  		ltodiff := make(map[int]*addedLine)
    90  		if strip > 0 {
    91  			ps := strings.Split(filepath.ToSlash(filediff.PathNew), "/")
    92  			if len(ps) > strip {
    93  				path = filepath.Join(ps[strip:]...)
    94  			}
    95  		}
    96  		np, err := normalizePath(path)
    97  		if err != nil {
    98  			// FIXME(haya14busa): log or return error?
    99  			continue
   100  		}
   101  		path = np
   102  
   103  		for _, hunk := range filediff.Hunks {
   104  			for _, line := range hunk.Lines {
   105  				if line.Type == diff.LineAdded {
   106  					ltodiff[line.LnumNew] = &addedLine{
   107  						Path:     path,
   108  						Lnum:     line.LnumNew,
   109  						LnumDiff: line.LnumDiff,
   110  						Content:  line.Content,
   111  					}
   112  				}
   113  			}
   114  		}
   115  		r[path] = ltodiff
   116  	}
   117  	return r
   118  }
   119  
   120  func normalizePath(p string) (string, error) {
   121  	if !filepath.IsAbs(p) {
   122  		path, err := filepath.Abs(p)
   123  		if err != nil {
   124  			return "", err
   125  		}
   126  		p = path
   127  	}
   128  	return filepath.ToSlash(p), nil
   129  }