github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/godoc/search.go (about) 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 if p.GetPageInfoMode(r)&NoHTML != 0 { 103 p.ServeText(w, applyTemplate(p.SearchText, "searchText", result)) 104 return 105 } 106 contents := bytes.Buffer{} 107 for _, f := range p.SearchResults { 108 contents.Write(f(p, result)) 109 } 110 111 var title string 112 if haveResults := contents.Len() > 0; haveResults { 113 title = fmt.Sprintf(`Results for query %q`, query) 114 if !p.Corpus.IndexEnabled { 115 result.Alert = "" 116 } 117 } else { 118 title = fmt.Sprintf(`No results found for query %q`, query) 119 } 120 121 body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result)) 122 body.Write(contents.Bytes()) 123 124 p.ServePage(w, Page{ 125 Title: title, 126 Tabtitle: query, 127 Query: query, 128 Body: body.Bytes(), 129 Share: allowShare(r), 130 }) 131 } 132 133 func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) { 134 w.Header().Set("Content-Type", "application/opensearchdescription+xml") 135 data := map[string]interface{}{ 136 "BaseURL": fmt.Sprintf("http://%s", r.Host), 137 } 138 applyTemplateToResponseWriter(w, p.SearchDescXML, &data) 139 }