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

Configure Feed

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

feat(web): Add context lines to search results (#931)

This adds the before and after Lines in the search results. The default is
still 0 so the user has to increase "context lines" to see more.

author
Marc W.
committer
GitHub
date (Apr 15, 2025, 1:08 PM -0700) commit 0978c13f parent 75879a72
+151 -2
+39
web/server.go
··· 47 47 "More": func(orig int) int { 48 48 return orig * 3 49 49 }, 50 + "AddLineNumbers": func(content string, lineNum int, isBefore bool) []lineMatch { 51 + return AddLineNumbers(content, lineNum, isBefore) 52 + }, 50 53 "HumanUnit": func(orig int64) string { 51 54 b := orig 52 55 suffix := "" ··· 78 81 "TrimTrailingNewline": func(s string) string { 79 82 return strings.TrimSuffix(s, "\n") 80 83 }, 84 + } 85 + 86 + // lineMatch represents a line of content with its associated line number 87 + type lineMatch struct { 88 + LineNum int 89 + Content string 90 + } 91 + 92 + // AddLineNumbers adds line numbers to the beginning of each line in the given content string. 93 + // The line numbers are relative to the current line number (lineNum). 94 + // For 'before' content, numbers will count backwards from lineNum-1. 95 + // For 'after' content, numbers will count forwards from lineNum+1. 96 + func AddLineNumbers(content string, lineNum int, isBefore bool) []lineMatch { 97 + if content == "" { 98 + return nil 99 + } 100 + 101 + lines := strings.Split(content, "\n") 102 + var result []lineMatch 103 + 104 + for i, line := range lines { 105 + if i == len(lines)-1 && line == "" { 106 + continue 107 + } 108 + 109 + var num int 110 + if isBefore { 111 + num = lineNum - len(lines) + i 112 + } else { 113 + num = lineNum + i + 1 114 + } 115 + 116 + // Add the line number and content to the result 117 + result = append(result, lineMatch{LineNum: num, Content: line}) 118 + } 119 + return result 81 120 } 82 121 83 122 const defaultNumResults = 50
+96
web/server_test.go
··· 1 + package web 2 + 3 + import ( 4 + "testing" 5 + 6 + "github.com/google/go-cmp/cmp" 7 + ) 8 + 9 + func TestAddLineNumbers(t *testing.T) { 10 + tests := []struct { 11 + name string 12 + content string 13 + lineNum int 14 + isBefore bool 15 + want []lineMatch 16 + }{ 17 + { 18 + name: "empty content", 19 + content: "", 20 + lineNum: 10, 21 + isBefore: true, 22 + want: nil, 23 + }, 24 + { 25 + name: "single line before", 26 + content: "hello world", 27 + lineNum: 10, 28 + isBefore: true, 29 + want: []lineMatch{ 30 + {LineNum: 9, Content: "hello world"}, 31 + }, 32 + }, 33 + { 34 + name: "single line after", 35 + content: "hello world", 36 + lineNum: 10, 37 + isBefore: false, 38 + want: []lineMatch{ 39 + {LineNum: 11, Content: "hello world"}, 40 + }, 41 + }, 42 + { 43 + name: "multiple lines before", 44 + content: "first line\nsecond line\nthird line", 45 + lineNum: 10, 46 + isBefore: true, 47 + want: []lineMatch{ 48 + {LineNum: 7, Content: "first line"}, 49 + {LineNum: 8, Content: "second line"}, 50 + {LineNum: 9, Content: "third line"}, 51 + }, 52 + }, 53 + { 54 + name: "multiple lines after", 55 + content: "first line\nsecond line\nthird line", 56 + lineNum: 10, 57 + isBefore: false, 58 + want: []lineMatch{ 59 + {LineNum: 11, Content: "first line"}, 60 + {LineNum: 12, Content: "second line"}, 61 + {LineNum: 13, Content: "third line"}, 62 + }, 63 + }, 64 + { 65 + name: "content with empty lines before", 66 + content: "first line\n\nthird line", 67 + lineNum: 10, 68 + isBefore: true, 69 + want: []lineMatch{ 70 + {LineNum: 7, Content: "first line"}, 71 + {LineNum: 8, Content: ""}, 72 + {LineNum: 9, Content: "third line"}, 73 + }, 74 + }, 75 + { 76 + name: "content with empty lines after", 77 + content: "first line\n\nthird line", 78 + lineNum: 10, 79 + isBefore: false, 80 + want: []lineMatch{ 81 + {LineNum: 11, Content: "first line"}, 82 + {LineNum: 12, Content: ""}, 83 + {LineNum: 13, Content: "third line"}, 84 + }, 85 + }, 86 + } 87 + 88 + for _, tt := range tests { 89 + t.Run(tt.name, func(t *testing.T) { 90 + got := AddLineNumbers(tt.content, tt.lineNum, tt.isBefore) 91 + if diff := cmp.Diff(tt.want, got); diff != "" { 92 + t.Errorf("AddLineNumbers() mismatch (-want +got):\n%s", diff) 93 + } 94 + }) 95 + } 96 + }
+16 -2
web/templates.go
··· 34 34 <style> 35 35 #navsearchbox { width: 350px !important; } 36 36 #maxhits { width: 100px !important; } 37 + #context { width: 70px !important; } 37 38 .label-dup { 38 39 border-width: 1px !important; 39 40 border-style: solid !important; ··· 41 42 color: black; 42 43 } 43 44 .noselect { 45 + color: #999; 44 46 user-select: none; 45 47 } 46 48 a.label-dup:hover { ··· 116 118 <div class="input-group"> 117 119 <div class="input-group-addon">Max Results</div> 118 120 <input class="form-control" type="number" id="maxhits" name="num" value="{{.Num}}"> 121 + </div> 122 + <div class="input-group"> 123 + <div class="input-group-addon">Context Lines</div> 124 + <input class="form-control" id="context" name="ctx" type="number" value="{{.Ctx}}"> 119 125 </div> 120 126 <button class="btn btn-primary">Search</button> 121 127 <!--Hack: we use a hidden form field to keep track of the debug flag across searches--> ··· 226 232 <table class="table table-hover table-condensed"> 227 233 <thead> 228 234 <tr> 229 - <th> 235 + <th colspan="2"> 230 236 {{if .URL}}<a name="{{.ResultID}}" class="result"></a><a href="{{.URL}}" >{{else}}<a name="{{.ResultID}}">{{end}} 231 237 <small> 232 238 {{.Repo}}:{{.FileName}} {{if .ScoreDebug}}<i>({{.ScoreDebug}})</i>{{end}}</a>: ··· 244 250 {{range .Matches}} 245 251 {{if gt .LineNum 0}} 246 252 <tr> 253 + <td style="width: 1%; white-space: nowrap; background-color: rgba(238, 238, 255, 0.6);"> 254 + <pre class="inline-pre"><p style="margin: 0px;">{{$beforeLines := AddLineNumbers .Before .LineNum true}}{{range $line := $beforeLines}}<span class="noselect"><u>{{$line.LineNum}}</u>:</span> 255 + {{end}}<span class="noselect">{{if .URL}}<a href="{{.URL}}">{{end}}<u>{{.LineNum}}</u>{{if .URL}}</a>{{end}}:</span> 256 + {{$afterLines := AddLineNumbers .After .LineNum false}}{{range $line := $afterLines}}<span class="noselect"><u>{{$line.LineNum}}</u>:</span> 257 + {{end}}</p></pre> 258 + </td> 247 259 <td style="background-color: rgba(238, 238, 255, 0.6);"> 248 - <pre class="inline-pre"><span class="noselect">{{if .URL}}<a href="{{.URL}}">{{end}}<u>{{.LineNum}}</u>{{if .URL}}</a>{{end}}: </span>{{range .Fragments}}{{LimitPre 100 .Pre}}<b>{{.Match}}</b>{{LimitPost 100 (TrimTrailingNewline .Post)}}{{end}} {{if .ScoreDebug}}<i>({{.ScoreDebug}})</i>{{end}}</pre> 260 + <pre class="inline-pre"><p style="margin: 0px;">{{range $line := $beforeLines}} {{$line.Content}} 261 + {{end}}</p> {{range .Fragments}}{{LimitPre 100 .Pre}}<b>{{.Match}}</b>{{LimitPost 100 (TrimTrailingNewline .Post)}}{{end}}<p style="margin: 0px;">{{range $line := $afterLines}} {{$line.Content}} 262 + {{end}}</p>{{if .ScoreDebug}}<i>({{.ScoreDebug}})</i>{{end}}</pre> 249 263 </td> 250 264 </tr> 251 265 {{end}}