fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 4.2 kB View raw
1package index 2 3import ( 4 "log" 5 6 "github.com/sourcegraph/zoekt" 7) 8 9// SortAndTruncateFiles is a convenience around SortFiles and 10// DisplayTruncator. Given an aggregated files it will sort and then truncate 11// based on the search options. 12func SortAndTruncateFiles(files []zoekt.FileMatch, opts *zoekt.SearchOptions) []zoekt.FileMatch { 13 SortFiles(files) 14 truncator, _ := NewDisplayTruncator(opts) 15 files, _ = truncator(files) 16 return files 17} 18 19// DisplayTruncator is a stateful function which enforces Document and Match 20// display limits by truncating and mutating before. hasMore is true until the 21// limits are exhausted. Once hasMore is false each subsequent call will 22// return an empty after and hasMore false. 23type DisplayTruncator func(before []zoekt.FileMatch) (after []zoekt.FileMatch, hasMore bool) 24 25// NewDisplayTruncator will return a DisplayTruncator which enforces the limits in 26// opts. If there are no limits to enforce, hasLimits is false and there is no 27// need to call DisplayTruncator. 28func NewDisplayTruncator(opts *zoekt.SearchOptions) (_ DisplayTruncator, hasLimits bool) { 29 docLimit := opts.MaxDocDisplayCount 30 docLimited := docLimit > 0 31 32 matchLimit := opts.MaxMatchDisplayCount 33 matchLimited := matchLimit > 0 34 35 done := false 36 37 if !docLimited && !matchLimited { 38 return func(fm []zoekt.FileMatch) ([]zoekt.FileMatch, bool) { 39 return fm, true 40 }, false 41 } 42 43 return func(fm []zoekt.FileMatch) ([]zoekt.FileMatch, bool) { 44 if done { 45 return nil, false 46 } 47 48 if docLimited { 49 if len(fm) >= docLimit { 50 done = true 51 fm = fm[:docLimit] 52 } 53 docLimit -= len(fm) 54 } 55 56 if matchLimited { 57 fm, matchLimit = limitMatches(fm, matchLimit, opts.ChunkMatches) 58 if matchLimit <= 0 { 59 done = true 60 } 61 } 62 63 return fm, !done 64 }, true 65} 66 67func limitMatches(files []zoekt.FileMatch, limit int, chunkMatches bool) ([]zoekt.FileMatch, int) { 68 var limiter func(file *zoekt.FileMatch, limit int) int 69 if chunkMatches { 70 limiter = limitChunkMatches 71 } else { 72 limiter = limitLineMatches 73 } 74 for i := range files { 75 limit = limiter(&files[i], limit) 76 if limit <= 0 { 77 return files[:i+1], 0 78 } 79 } 80 return files, limit 81} 82 83// Limit the number of ChunkMatches in the given FileMatch, returning the 84// remaining limit, if any. 85func limitChunkMatches(file *zoekt.FileMatch, limit int) int { 86 for i := range file.ChunkMatches { 87 cm := &file.ChunkMatches[i] 88 if len(cm.Ranges) > limit { 89 // We potentially need to effect the limit upon 3 different fields: 90 // Ranges, SymbolInfo, and Content. 91 92 // Content is the most complicated: we need to remove the last N 93 // lines from it, where N is the difference between the line number 94 // of the end of the old last Range and that of the new last Range. 95 // This calculation is correct in the presence of both context lines 96 // and multiline Ranges, taking into account that Content never has 97 // a trailing newline. 98 n := cm.Ranges[len(cm.Ranges)-1].End.LineNumber - cm.Ranges[limit-1].End.LineNumber 99 if n > 0 { 100 for b := len(cm.Content) - 1; b >= 0; b-- { 101 if cm.Content[b] == '\n' { 102 n -= 1 103 } 104 if n == 0 { 105 cm.Content = cm.Content[:b] 106 break 107 } 108 } 109 if n > 0 { 110 // Should be impossible. 111 log.Panicf("Failed to find enough newlines when truncating Content, %d left over, %d ranges", n, len(cm.Ranges)) 112 } 113 } 114 115 cm.Ranges = cm.Ranges[:limit] 116 if cm.SymbolInfo != nil { 117 // When non-nil, SymbolInfo is specified to have the same length 118 // as Ranges. 119 cm.SymbolInfo = cm.SymbolInfo[:limit] 120 } 121 } 122 if len(cm.Ranges) == limit { 123 file.ChunkMatches = file.ChunkMatches[:i+1] 124 limit = 0 125 break 126 } 127 limit -= len(cm.Ranges) 128 } 129 return limit 130} 131 132// Limit the number of LineMatches in the given FileMatch, returning the 133// remaining limit, if any. 134func limitLineMatches(file *zoekt.FileMatch, limit int) int { 135 for i := range file.LineMatches { 136 lm := &file.LineMatches[i] 137 if len(lm.LineFragments) > limit { 138 lm.LineFragments = lm.LineFragments[:limit] 139 } 140 if len(lm.LineFragments) == limit { 141 file.LineMatches = file.LineMatches[:i+1] 142 limit = 0 143 break 144 } 145 limit -= len(lm.LineFragments) 146 } 147 return limit 148}