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

Configure Feed

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

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