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 return fm[:docLimit], false 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 return fm, false 57 } 58 } 59 60 return fm, true 61 }, true 62} 63 64func limitMatches(files []FileMatch, limit int, chunkMatches bool) ([]FileMatch, int) { 65 var limiter func(file *FileMatch, limit int) int 66 if chunkMatches { 67 limiter = limitChunkMatches 68 } else { 69 limiter = limitLineMatches 70 } 71 for i := range files { 72 limit = limiter(&files[i], limit) 73 if limit <= 0 { 74 return files[:i+1], 0 75 } 76 } 77 return files, limit 78} 79 80// Limit the number of ChunkMatches in the given FileMatch, returning the 81// remaining limit, if any. 82func limitChunkMatches(file *FileMatch, limit int) int { 83 for i := range file.ChunkMatches { 84 cm := &file.ChunkMatches[i] 85 if len(cm.Ranges) > limit { 86 // We potentially need to effect the limit upon 3 different fields: 87 // Ranges, SymbolInfo, and Content. 88 89 // Content is the most complicated: we need to remove the last N 90 // lines from it, where N is the difference between the line number 91 // of the end of the old last Range and that of the new last Range. 92 // This calculation is correct in the presence of both context lines 93 // and multiline Ranges, taking into account that Content never has 94 // a trailing newline. 95 n := cm.Ranges[len(cm.Ranges)-1].End.LineNumber - cm.Ranges[limit-1].End.LineNumber 96 if n > 0 { 97 for b := len(cm.Content) - 1; b >= 0; b-- { 98 if cm.Content[b] == '\n' { 99 n -= 1 100 } 101 if n == 0 { 102 cm.Content = cm.Content[:b] 103 break 104 } 105 } 106 if n > 0 { 107 // Should be impossible. 108 log.Panicf("Failed to find enough newlines when truncating Content, %d left over, %d ranges", n, len(cm.Ranges)) 109 } 110 } 111 112 cm.Ranges = cm.Ranges[:limit] 113 if cm.SymbolInfo != nil { 114 // When non-nil, SymbolInfo is specified to have the same length 115 // as Ranges. 116 cm.SymbolInfo = cm.SymbolInfo[:limit] 117 } 118 } 119 if len(cm.Ranges) == limit { 120 file.ChunkMatches = file.ChunkMatches[:i+1] 121 limit = 0 122 break 123 } 124 limit -= len(cm.Ranges) 125 } 126 return limit 127} 128 129// Limit the number of LineMatches in the given FileMatch, returning the 130// remaining limit, if any. 131func limitLineMatches(file *FileMatch, limit int) int { 132 for i := range file.LineMatches { 133 lm := &file.LineMatches[i] 134 if len(lm.LineFragments) > limit { 135 lm.LineFragments = lm.LineFragments[:limit] 136 } 137 if len(lm.LineFragments) == limit { 138 file.LineMatches = file.LineMatches[:i+1] 139 limit = 0 140 break 141 } 142 limit -= len(lm.LineFragments) 143 } 144 return limit 145}