...
1
2
3
4
5 package godoc
6
7 import (
8 "bytes"
9 "fmt"
10 "net/http"
11 "regexp"
12 "strings"
13 )
14
15 type SearchResult struct {
16 Query string
17 Alert string
18
19
20 Pak HitList
21 Hit *LookupResult
22 Alt *AltWords
23
24
25 Found int
26 Textual []FileLines
27 Complete bool
28 Idents map[SpotKind][]Ident
29 }
30
31 func (c *Corpus) Lookup(query string) SearchResult {
32 result := &SearchResult{Query: query}
33
34 index, timestamp := c.CurrentIndex()
35 if index != nil {
36
37 if r, err := index.Lookup(query); err == nil {
38 result = r
39 } else if err != nil && !c.IndexFullText {
40
41
42 result.Alert = "Error in query string: " + err.Error()
43 return *result
44 }
45
46
47 if c.IndexFullText && query != "" {
48 rx, err := regexp.Compile(query)
49 if err != nil {
50 result.Alert = "Error in query regular expression: " + err.Error()
51 return *result
52 }
53
54
55
56
57 result.Found, result.Textual = index.LookupRegexp(rx, c.MaxResults+1)
58 result.Complete = result.Found <= c.MaxResults
59 if !result.Complete {
60 result.Found--
61 }
62 }
63 }
64
65
66 if c.IndexEnabled {
67 if ts := c.FSModifiedTime(); timestamp.Before(ts) {
68
69 result.Alert = "Indexing in progress: result may be inaccurate"
70 }
71 } else {
72 result.Alert = "Search index disabled: no results available"
73 }
74
75 return *result
76 }
77
78
79
80 func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
81 return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
82 }
83
84
85
86 func (p *Presentation) SearchResultCode(result SearchResult) []byte {
87 return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
88 }
89
90
91
92 func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
93 return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
94 }
95
96
97
98 func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
99 query := strings.TrimSpace(r.FormValue("q"))
100 result := p.Corpus.Lookup(query)
101
102 var contents bytes.Buffer
103 for _, f := range p.SearchResults {
104 contents.Write(f(p, result))
105 }
106
107 var title string
108 if haveResults := contents.Len() > 0; haveResults {
109 title = fmt.Sprintf(`Results for query: %v`, query)
110 if !p.Corpus.IndexEnabled {
111 result.Alert = ""
112 }
113 } else {
114 title = fmt.Sprintf(`No results found for query %q`, query)
115 }
116
117 body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
118 body.Write(contents.Bytes())
119
120 p.ServePage(w, Page{
121 Title: title,
122 Tabtitle: query,
123 Query: query,
124 Body: body.Bytes(),
125 })
126 }
127
128 func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
129 w.Header().Set("Content-Type", "application/opensearchdescription+xml")
130 data := map[string]interface{}{
131 "BaseURL": fmt.Sprintf("http://%s", r.Host),
132 }
133 applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
134 }
135
136
137
138 func tocColCount(result SearchResult) int {
139 tocLen := tocLen(result)
140 colCount := 0
141
142 switch {
143 case tocLen <= 10:
144 colCount = 1
145 case tocLen <= 20:
146 colCount = 2
147 case tocLen <= 80:
148 colCount = 3
149 default:
150 colCount = 4
151 }
152 return colCount
153 }
154
155
156
157
158 func tocLen(result SearchResult) int {
159 tocLen := 0
160 for _, val := range result.Idents {
161 if len(val) != 0 {
162 tocLen++
163 }
164 }
165
166
167 if len(result.Idents) == 0 {
168 tocLen++
169 }
170 if result.Hit != nil {
171 if len(result.Hit.Decls) > 0 {
172 tocLen += len(result.Hit.Decls)
173
174 tocLen++
175 }
176 if len(result.Hit.Others) > 0 {
177 tocLen += len(result.Hit.Others)
178
179 tocLen++
180 }
181 }
182
183 tocLen++
184 return tocLen
185 }
186
View as plain text