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  }