You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
4.2 KiB

  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. package godoc
  5. import (
  6. "bytes"
  7. "fmt"
  8. "net/http"
  9. "regexp"
  10. "strings"
  11. )
  12. type SearchResult struct {
  13. Query string
  14. Alert string // error or warning message
  15. // identifier matches
  16. Pak HitList // packages matching Query
  17. Hit *LookupResult // identifier matches of Query
  18. Alt *AltWords // alternative identifiers to look for
  19. // textual matches
  20. Found int // number of textual occurrences found
  21. Textual []FileLines // textual matches of Query
  22. Complete bool // true if all textual occurrences of Query are reported
  23. Idents map[SpotKind][]Ident
  24. }
  25. func (c *Corpus) Lookup(query string) SearchResult {
  26. result := &SearchResult{Query: query}
  27. index, timestamp := c.CurrentIndex()
  28. if index != nil {
  29. // identifier search
  30. if r, err := index.Lookup(query); err == nil {
  31. result = r
  32. } else if err != nil && !c.IndexFullText {
  33. // ignore the error if full text search is enabled
  34. // since the query may be a valid regular expression
  35. result.Alert = "Error in query string: " + err.Error()
  36. return *result
  37. }
  38. // full text search
  39. if c.IndexFullText && query != "" {
  40. rx, err := regexp.Compile(query)
  41. if err != nil {
  42. result.Alert = "Error in query regular expression: " + err.Error()
  43. return *result
  44. }
  45. // If we get maxResults+1 results we know that there are more than
  46. // maxResults results and thus the result may be incomplete (to be
  47. // precise, we should remove one result from the result set, but
  48. // nobody is going to count the results on the result page).
  49. result.Found, result.Textual = index.LookupRegexp(rx, c.MaxResults+1)
  50. result.Complete = result.Found <= c.MaxResults
  51. if !result.Complete {
  52. result.Found-- // since we looked for maxResults+1
  53. }
  54. }
  55. }
  56. // is the result accurate?
  57. if c.IndexEnabled {
  58. if ts := c.FSModifiedTime(); timestamp.Before(ts) {
  59. // The index is older than the latest file system change under godoc's observation.
  60. result.Alert = "Indexing in progress: result may be inaccurate"
  61. }
  62. } else {
  63. result.Alert = "Search index disabled: no results available"
  64. }
  65. return *result
  66. }
  67. // SearchResultDoc optionally specifies a function returning an HTML body
  68. // displaying search results matching godoc documentation.
  69. func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
  70. return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
  71. }
  72. // SearchResultCode optionally specifies a function returning an HTML body
  73. // displaying search results matching source code.
  74. func (p *Presentation) SearchResultCode(result SearchResult) []byte {
  75. return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
  76. }
  77. // SearchResultTxt optionally specifies a function returning an HTML body
  78. // displaying search results of textual matches.
  79. func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
  80. return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
  81. }
  82. // HandleSearch obtains results for the requested search and returns a page
  83. // to display them.
  84. func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
  85. query := strings.TrimSpace(r.FormValue("q"))
  86. result := p.Corpus.Lookup(query)
  87. if p.GetPageInfoMode(r)&NoHTML != 0 {
  88. p.ServeText(w, applyTemplate(p.SearchText, "searchText", result))
  89. return
  90. }
  91. contents := bytes.Buffer{}
  92. for _, f := range p.SearchResults {
  93. contents.Write(f(p, result))
  94. }
  95. var title string
  96. if haveResults := contents.Len() > 0; haveResults {
  97. title = fmt.Sprintf(`Results for query: %v`, query)
  98. if !p.Corpus.IndexEnabled {
  99. result.Alert = ""
  100. }
  101. } else {
  102. title = fmt.Sprintf(`No results found for query %q`, query)
  103. }
  104. body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
  105. body.Write(contents.Bytes())
  106. p.ServePage(w, Page{
  107. Title: title,
  108. Tabtitle: query,
  109. Query: query,
  110. Body: body.Bytes(),
  111. GoogleCN: googleCN(r),
  112. })
  113. }
  114. func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
  115. w.Header().Set("Content-Type", "application/opensearchdescription+xml")
  116. data := map[string]interface{}{
  117. "BaseURL": fmt.Sprintf("http://%s", r.Host),
  118. }
  119. applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
  120. }