fork of https://github.com/sourcegraph/zoekt
1// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package web
16
17import (
18 "bytes"
19 "log"
20 "net/url"
21 "strconv"
22 "strings"
23 "text/template"
24
25 "github.com/sourcegraph/zoekt"
26)
27
28func (s *Server) formatResults(result *zoekt.SearchResult, query string, localPrint bool) ([]*FileMatch, error) {
29 var fmatches []*FileMatch
30
31 templateMap := map[string]*template.Template{}
32 fragmentMap := map[string]*template.Template{}
33 if !localPrint {
34 for repo, str := range result.RepoURLs {
35 if str != "" {
36 templateMap[repo] = s.getTextTemplate(str)
37 }
38 }
39 for repo, str := range result.LineFragments {
40 if str != "" {
41 fragmentMap[repo] = s.getTextTemplate(str)
42 }
43 }
44 }
45 getFragment := func(repo string, linenum int) string {
46 tpl := fragmentMap[repo]
47
48 if tpl == nil || localPrint {
49 return "#l" + strconv.Itoa(linenum)
50 }
51
52 var buf bytes.Buffer
53 if err := tpl.Execute(&buf, map[string]string{
54 "LineNumber": strconv.Itoa(linenum),
55 }); err != nil {
56 log.Printf("fragment template: %v", err)
57 return ""
58 }
59 return buf.String()
60 }
61 getURL := func(repo, filename string, branches []string, version string) string {
62 tpl := templateMap[repo]
63 if localPrint || tpl == nil {
64 v := make(url.Values)
65 v.Add("r", repo)
66 v.Add("f", filename)
67 v.Add("q", query)
68 if len(branches) > 0 {
69 v.Add("b", branches[0])
70 }
71 return "print?" + v.Encode()
72 }
73
74 var buf bytes.Buffer
75 b := ""
76 if len(branches) > 0 {
77 b = branches[0]
78 }
79 err := tpl.Execute(&buf, map[string]string{
80 "Branch": b,
81 "Version": version,
82 "Path": filename,
83 })
84 if err != nil {
85 log.Printf("url template: %v", err)
86 return ""
87 }
88 return buf.String()
89 }
90
91 // hash => result-id
92 seenFiles := map[string]string{}
93 for _, f := range result.Files {
94 fMatch := FileMatch{
95 FileName: f.FileName,
96 Repo: f.Repository,
97 ResultID: f.Repository + ":" + f.FileName,
98 Branches: f.Branches,
99 Language: f.Language,
100 Score: f.Score,
101 ScoreDebug: f.Debug,
102 }
103
104 if dup, ok := seenFiles[string(f.Checksum)]; ok {
105 fMatch.DuplicateID = dup
106 } else {
107 seenFiles[string(f.Checksum)] = fMatch.ResultID
108 }
109
110 if f.SubRepositoryName != "" {
111 fn := strings.TrimPrefix(fMatch.FileName[len(f.SubRepositoryPath):], "/")
112 fMatch.URL = getURL(f.SubRepositoryName, fn, f.Branches, f.Version)
113 } else {
114 fMatch.URL = getURL(f.Repository, f.FileName, f.Branches, f.Version)
115 }
116
117 for _, m := range f.LineMatches {
118 fragment := getFragment(f.Repository, m.LineNumber)
119 if !strings.HasPrefix(fragment, "#") && !strings.HasPrefix(fragment, ";") {
120 // TODO - remove this is backward compatibility glue.
121 fragment = "#" + fragment
122 }
123 md := Match{
124 FileName: f.FileName,
125 LineNum: m.LineNumber,
126 URL: fMatch.URL + fragment,
127
128 Score: m.Score,
129 ScoreDebug: m.DebugScore,
130 }
131
132 md.Before = string(m.Before)
133 md.After = string(m.After)
134 lastEnd := 0
135 line := m.Line
136 for i, f := range m.LineFragments {
137 l := f.LineOffset
138 e := l + f.MatchLength
139
140 frag := Fragment{
141 Pre: string(line[lastEnd:l]),
142 Match: string(line[l:e]),
143 }
144 if i == len(m.LineFragments)-1 {
145 frag.Post = string(m.Line[e:])
146 }
147
148 md.Fragments = append(md.Fragments, frag)
149 lastEnd = e
150 }
151 fMatch.Matches = append(fMatch.Matches, md)
152 }
153 fmatches = append(fmatches, &fMatch)
154 }
155 return fmatches, nil
156}