github.com/mistwind/reviewdog@v0.0.0-20230322024206-9cfa11856d58/filter/filter.go (about) 1 package filter 2 3 import ( 4 "path/filepath" 5 6 "github.com/mistwind/reviewdog/diff" 7 "github.com/mistwind/reviewdog/proto/rdf" 8 ) 9 10 // FilteredDiagnostic represents Diagnostic with filtering info. 11 type FilteredDiagnostic struct { 12 Diagnostic *rdf.Diagnostic 13 ShouldReport bool 14 // false if the result is outside diff files. 15 InDiffFile bool 16 // true if the result is inside a diff hunk. 17 // If it's a multiline result, both start and end must be in the same diff 18 // hunk. 19 InDiffContext bool 20 21 // Similar to InDiffContext but for suggestion. True if first 22 // suggestion is in diff context. 23 FirstSuggestionInDiffContext bool 24 25 // Source lines text of the diagnostic message's line-range. Key is line 26 // number. If a suggestion range is broader than the diagnostic message's 27 // line-range, suggestions' line-range are included too. It contains a whole 28 // line even if the diagnostic range have column fields. 29 // Optional. Currently available only when it's in diff context. 30 SourceLines map[int]string 31 32 OldPath string 33 OldLine int 34 } 35 36 // FilterCheck filters check results by diff. It doesn't drop check which 37 // is not in diff but set FilteredDiagnostic.ShouldReport field false. 38 func FilterCheck(results []*rdf.Diagnostic, diff []*diff.FileDiff, strip int, 39 cwd string, mode Mode) []*FilteredDiagnostic { 40 checks := make([]*FilteredDiagnostic, 0, len(results)) 41 df := NewDiffFilter(diff, strip, cwd, mode) 42 for _, result := range results { 43 check := &FilteredDiagnostic{Diagnostic: result, SourceLines: make(map[int]string)} 44 loc := result.GetLocation() 45 loc.Path = NormalizePath(loc.GetPath(), cwd, "") 46 startLine := int(loc.GetRange().GetStart().GetLine()) 47 endLine := int(loc.GetRange().GetEnd().GetLine()) 48 if endLine == 0 { 49 endLine = startLine 50 } 51 check.InDiffContext = true 52 for l := startLine; l <= endLine; l++ { 53 shouldReport, difffile, diffline := df.ShouldReport(loc.GetPath(), l) 54 check.ShouldReport = check.ShouldReport || shouldReport 55 // all lines must be in diff. 56 check.InDiffContext = check.InDiffContext && diffline != nil 57 if diffline != nil { 58 check.SourceLines[l] = diffline.Content 59 } 60 if difffile != nil { 61 check.InDiffFile = true 62 if l == startLine { 63 // TODO(haya14busa): Support endline as well especially for GitLab. 64 check.OldPath, check.OldLine = getOldPosition(difffile, strip, loc.GetPath(), l) 65 } 66 } 67 } 68 // Add source lines for suggestions. 69 for i, s := range result.GetSuggestions() { 70 inDiffContext := true 71 start := int(s.GetRange().GetStart().GetLine()) 72 end := int(s.GetRange().GetEnd().GetLine()) 73 for l := start; l <= end; l++ { 74 if diffline := df.DiffLine(loc.GetPath(), l); diffline != nil { 75 check.SourceLines[l] = diffline.Content 76 } else { 77 inDiffContext = false 78 } 79 } 80 if i == 0 { 81 check.FirstSuggestionInDiffContext = inDiffContext 82 } 83 } 84 checks = append(checks, check) 85 } 86 return checks 87 } 88 89 // NormalizePath return normalized path with workdir and relative path to 90 // project. 91 func NormalizePath(path, workdir, projectRelPath string) string { 92 path = filepath.Clean(path) 93 if path == "." { 94 return "" 95 } 96 // Convert absolute path to relative path only if the path is in current 97 // directory. 98 if filepath.IsAbs(path) && workdir != "" && contains(path, workdir) { 99 relPath, err := filepath.Rel(workdir, path) 100 if err == nil { 101 path = relPath 102 } 103 } 104 if !filepath.IsAbs(path) && projectRelPath != "" { 105 path = filepath.Join(projectRelPath, path) 106 } 107 return filepath.ToSlash(path) 108 } 109 110 func getOldPosition(filediff *diff.FileDiff, strip int, newPath string, newLine int) (oldPath string, oldLine int) { 111 if filediff == nil { 112 return "", 0 113 } 114 if NormalizeDiffPath(filediff.PathNew, strip) != newPath { 115 return "", 0 116 } 117 oldPath = NormalizeDiffPath(filediff.PathOld, strip) 118 delta := 0 119 for _, hunk := range filediff.Hunks { 120 if newLine < hunk.StartLineNew { 121 break 122 } 123 delta += hunk.LineLengthOld - hunk.LineLengthNew 124 for _, line := range hunk.Lines { 125 if line.LnumNew == newLine { 126 return oldPath, line.LnumOld 127 } 128 } 129 } 130 return oldPath, newLine + delta 131 }