Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / godoc / search.go
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.
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 // error or warning message
18
19         // identifier matches
20         Pak HitList       // packages matching Query
21         Hit *LookupResult // identifier matches of Query
22         Alt *AltWords     // alternative identifiers to look for
23
24         // textual matches
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
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                 // identifier search
37                 if r, err := index.Lookup(query); err == nil {
38                         result = r
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()
43                         return *result
44                 }
45
46                 // full text search
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                         // 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
59                         if !result.Complete {
60                                 result.Found-- // since we looked for maxResults+1
61                         }
62                 }
63         }
64
65         // is the result accurate?
66         if c.IndexEnabled {
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"
70                 }
71         } else {
72                 result.Alert = "Search index disabled: no results available"
73         }
74
75         return *result
76 }
77
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)
82 }
83
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)
88 }
89
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)
94 }
95
96 // HandleSearch obtains results for the requested search and returns a page
97 // to display them.
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                 GoogleCN: googleCN(r),
126         })
127 }
128
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),
133         }
134         applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
135 }
136
137 // tocColCount returns the no. of columns
138 // to split the toc table to.
139 func tocColCount(result SearchResult) int {
140         tocLen := tocLen(result)
141         colCount := 0
142         // Simple heuristic based on visual aesthetic in manual testing.
143         switch {
144         case tocLen <= 10:
145                 colCount = 1
146         case tocLen <= 20:
147                 colCount = 2
148         case tocLen <= 80:
149                 colCount = 3
150         default:
151                 colCount = 4
152         }
153         return colCount
154 }
155
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 {
160         tocLen := 0
161         for _, val := range result.Idents {
162                 if len(val) != 0 {
163                         tocLen++
164                 }
165         }
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 {
169                 tocLen++
170         }
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".
175                         tocLen++
176                 }
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".
180                         tocLen++
181                 }
182         }
183         // For "textual occurrences".
184         tocLen++
185         return tocLen
186 }