1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
15 type SearchResult struct {
17 Alert string // error or warning message
20 Pak HitList // packages matching Query
21 Hit *LookupResult // identifier matches of Query
22 Alt *AltWords // alternative identifiers to look for
25 Found int // number of textual occurrences found
26 Textual []FileLines // textual matches of Query
27 Complete bool // true if all textual occurrences of Query are reported
28 Idents map[SpotKind][]Ident
31 func (c *Corpus) Lookup(query string) SearchResult {
32 result := &SearchResult{Query: query}
34 index, timestamp := c.CurrentIndex()
37 if r, err := index.Lookup(query); err == nil {
39 } else if err != nil && !c.IndexFullText {
40 // ignore the error if full text search is enabled
41 // since the query may be a valid regular expression
42 result.Alert = "Error in query string: " + err.Error()
47 if c.IndexFullText && query != "" {
48 rx, err := regexp.Compile(query)
50 result.Alert = "Error in query regular expression: " + err.Error()
53 // If we get maxResults+1 results we know that there are more than
54 // maxResults results and thus the result may be incomplete (to be
55 // precise, we should remove one result from the result set, but
56 // nobody is going to count the results on the result page).
57 result.Found, result.Textual = index.LookupRegexp(rx, c.MaxResults+1)
58 result.Complete = result.Found <= c.MaxResults
60 result.Found-- // since we looked for maxResults+1
65 // is the result accurate?
67 if ts := c.FSModifiedTime(); timestamp.Before(ts) {
68 // The index is older than the latest file system change under godoc's observation.
69 result.Alert = "Indexing in progress: result may be inaccurate"
72 result.Alert = "Search index disabled: no results available"
78 // SearchResultDoc optionally specifies a function returning an HTML body
79 // displaying search results matching godoc documentation.
80 func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
81 return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
84 // SearchResultCode optionally specifies a function returning an HTML body
85 // displaying search results matching source code.
86 func (p *Presentation) SearchResultCode(result SearchResult) []byte {
87 return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
90 // SearchResultTxt optionally specifies a function returning an HTML body
91 // displaying search results of textual matches.
92 func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
93 return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
96 // HandleSearch obtains results for the requested search and returns a page
98 func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
99 query := strings.TrimSpace(r.FormValue("q"))
100 result := p.Corpus.Lookup(query)
102 var contents bytes.Buffer
103 for _, f := range p.SearchResults {
104 contents.Write(f(p, result))
108 if haveResults := contents.Len() > 0; haveResults {
109 title = fmt.Sprintf(`Results for query: %v`, query)
110 if !p.Corpus.IndexEnabled {
114 title = fmt.Sprintf(`No results found for query %q`, query)
117 body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
118 body.Write(contents.Bytes())
125 GoogleCN: googleCN(r),
129 func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
130 w.Header().Set("Content-Type", "application/opensearchdescription+xml")
131 data := map[string]interface{}{
132 "BaseURL": fmt.Sprintf("http://%s", r.Host),
134 applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
137 // tocColCount returns the no. of columns
138 // to split the toc table to.
139 func tocColCount(result SearchResult) int {
140 tocLen := tocLen(result)
142 // Simple heuristic based on visual aesthetic in manual testing.
156 // tocLen calculates the no. of items in the toc table
157 // by going through various fields in the SearchResult
158 // that is rendered in the UI.
159 func tocLen(result SearchResult) int {
161 for _, val := range result.Idents {
166 // If no identifiers, then just one item for the header text "Package <result.Query>".
167 // See searchcode.html for further details.
168 if len(result.Idents) == 0 {
171 if result.Hit != nil {
172 if len(result.Hit.Decls) > 0 {
173 tocLen += len(result.Hit.Decls)
174 // We need one extra item for the header text "Package-level declarations".
177 if len(result.Hit.Others) > 0 {
178 tocLen += len(result.Hit.Others)
179 // We need one extra item for the header text "Local declarations and uses".
183 // For "textual occurrences".