140 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2009 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package godoc
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"regexp"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| type SearchResult struct {
 | |
| 	Query string
 | |
| 	Alert string // error or warning message
 | |
| 
 | |
| 	// identifier matches
 | |
| 	Pak HitList       // packages matching Query
 | |
| 	Hit *LookupResult // identifier matches of Query
 | |
| 	Alt *AltWords     // alternative identifiers to look for
 | |
| 
 | |
| 	// textual matches
 | |
| 	Found    int         // number of textual occurrences found
 | |
| 	Textual  []FileLines // textual matches of Query
 | |
| 	Complete bool        // true if all textual occurrences of Query are reported
 | |
| 	Idents   map[SpotKind][]Ident
 | |
| }
 | |
| 
 | |
| func (c *Corpus) Lookup(query string) SearchResult {
 | |
| 	result := &SearchResult{Query: query}
 | |
| 
 | |
| 	index, timestamp := c.CurrentIndex()
 | |
| 	if index != nil {
 | |
| 		// identifier search
 | |
| 		if r, err := index.Lookup(query); err == nil {
 | |
| 			result = r
 | |
| 		} else if err != nil && !c.IndexFullText {
 | |
| 			// ignore the error if full text search is enabled
 | |
| 			// since the query may be a valid regular expression
 | |
| 			result.Alert = "Error in query string: " + err.Error()
 | |
| 			return *result
 | |
| 		}
 | |
| 
 | |
| 		// full text search
 | |
| 		if c.IndexFullText && query != "" {
 | |
| 			rx, err := regexp.Compile(query)
 | |
| 			if err != nil {
 | |
| 				result.Alert = "Error in query regular expression: " + err.Error()
 | |
| 				return *result
 | |
| 			}
 | |
| 			// If we get maxResults+1 results we know that there are more than
 | |
| 			// maxResults results and thus the result may be incomplete (to be
 | |
| 			// precise, we should remove one result from the result set, but
 | |
| 			// nobody is going to count the results on the result page).
 | |
| 			result.Found, result.Textual = index.LookupRegexp(rx, c.MaxResults+1)
 | |
| 			result.Complete = result.Found <= c.MaxResults
 | |
| 			if !result.Complete {
 | |
| 				result.Found-- // since we looked for maxResults+1
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// is the result accurate?
 | |
| 	if c.IndexEnabled {
 | |
| 		if ts := c.FSModifiedTime(); timestamp.Before(ts) {
 | |
| 			// The index is older than the latest file system change under godoc's observation.
 | |
| 			result.Alert = "Indexing in progress: result may be inaccurate"
 | |
| 		}
 | |
| 	} else {
 | |
| 		result.Alert = "Search index disabled: no results available"
 | |
| 	}
 | |
| 
 | |
| 	return *result
 | |
| }
 | |
| 
 | |
| // SearchResultDoc optionally specifies a function returning an HTML body
 | |
| // displaying search results matching godoc documentation.
 | |
| func (p *Presentation) SearchResultDoc(result SearchResult) []byte {
 | |
| 	return applyTemplate(p.SearchDocHTML, "searchDocHTML", result)
 | |
| }
 | |
| 
 | |
| // SearchResultCode optionally specifies a function returning an HTML body
 | |
| // displaying search results matching source code.
 | |
| func (p *Presentation) SearchResultCode(result SearchResult) []byte {
 | |
| 	return applyTemplate(p.SearchCodeHTML, "searchCodeHTML", result)
 | |
| }
 | |
| 
 | |
| // SearchResultTxt optionally specifies a function returning an HTML body
 | |
| // displaying search results of textual matches.
 | |
| func (p *Presentation) SearchResultTxt(result SearchResult) []byte {
 | |
| 	return applyTemplate(p.SearchTxtHTML, "searchTxtHTML", result)
 | |
| }
 | |
| 
 | |
| // HandleSearch obtains results for the requested search and returns a page
 | |
| // to display them.
 | |
| func (p *Presentation) HandleSearch(w http.ResponseWriter, r *http.Request) {
 | |
| 	query := strings.TrimSpace(r.FormValue("q"))
 | |
| 	result := p.Corpus.Lookup(query)
 | |
| 
 | |
| 	if p.GetPageInfoMode(r)&NoHTML != 0 {
 | |
| 		p.ServeText(w, applyTemplate(p.SearchText, "searchText", result))
 | |
| 		return
 | |
| 	}
 | |
| 	contents := bytes.Buffer{}
 | |
| 	for _, f := range p.SearchResults {
 | |
| 		contents.Write(f(p, result))
 | |
| 	}
 | |
| 
 | |
| 	var title string
 | |
| 	if haveResults := contents.Len() > 0; haveResults {
 | |
| 		title = fmt.Sprintf(`Results for query %q`, query)
 | |
| 		if !p.Corpus.IndexEnabled {
 | |
| 			result.Alert = ""
 | |
| 		}
 | |
| 	} else {
 | |
| 		title = fmt.Sprintf(`No results found for query %q`, query)
 | |
| 	}
 | |
| 
 | |
| 	body := bytes.NewBuffer(applyTemplate(p.SearchHTML, "searchHTML", result))
 | |
| 	body.Write(contents.Bytes())
 | |
| 
 | |
| 	p.ServePage(w, Page{
 | |
| 		Title:    title,
 | |
| 		Tabtitle: query,
 | |
| 		Query:    query,
 | |
| 		Body:     body.Bytes(),
 | |
| 		Share:    allowShare(r),
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (p *Presentation) serveSearchDesc(w http.ResponseWriter, r *http.Request) {
 | |
| 	w.Header().Set("Content-Type", "application/opensearchdescription+xml")
 | |
| 	data := map[string]interface{}{
 | |
| 		"BaseURL": fmt.Sprintf("http://%s", r.Host),
 | |
| 	}
 | |
| 	applyTemplateToResponseWriter(w, p.SearchDescXML, &data)
 | |
| }
 |