GoPLS Viewer

Home|gopls/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
5package godoc
6
7import (
8    "bytes"
9    "fmt"
10    "net/http"
11    "regexp"
12    "strings"
13)
14
15type 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
31func (c *CorpusLookup(query stringSearchResult {
32    result := &SearchResult{Queryquery}
33
34    indextimestamp := c.CurrentIndex()
35    if index != nil {
36        // identifier search
37        if rerr := 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            rxerr := 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.Foundresult.Textual = index.LookupRegexp(rxc.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.
80func (p *PresentationSearchResultDoc(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.
86func (p *PresentationSearchResultCode(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.
92func (p *PresentationSearchResultTxt(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.
98func (p *PresentationHandleSearch(w http.ResponseWriterr *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(presult))
105    }
106
107    var title string
108    if haveResults := contents.Len() > 0haveResults {
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(wPage{
121        Title:    title,
122        Tabtitlequery,
123        Query:    query,
124        Body:     body.Bytes(),
125    })
126}
127
128func (p *PresentationserveSearchDesc(w http.ResponseWriterr *http.Request) {
129    w.Header().Set("Content-Type""application/opensearchdescription+xml")
130    data := map[string]interface{}{
131        "BaseURL"fmt.Sprintf("http://%s"r.Host),
132    }
133    applyTemplateToResponseWriter(wp.SearchDescXML, &data)
134}
135
136// tocColCount returns the no. of columns
137// to split the toc table to.
138func tocColCount(result SearchResultint {
139    tocLen := tocLen(result)
140    colCount := 0
141    // Simple heuristic based on visual aesthetic in manual testing.
142    switch {
143    case tocLen <= 10:
144        colCount = 1
145    case tocLen <= 20:
146        colCount = 2
147    case tocLen <= 80:
148        colCount = 3
149    default:
150        colCount = 4
151    }
152    return colCount
153}
154
155// tocLen calculates the no. of items in the toc table
156// by going through various fields in the SearchResult
157// that is rendered in the UI.
158func tocLen(result SearchResultint {
159    tocLen := 0
160    for _val := range result.Idents {
161        if len(val) != 0 {
162            tocLen++
163        }
164    }
165    // If no identifiers, then just one item for the header text "Package <result.Query>".
166    // See searchcode.html for further details.
167    if len(result.Idents) == 0 {
168        tocLen++
169    }
170    if result.Hit != nil {
171        if len(result.Hit.Decls) > 0 {
172            tocLen += len(result.Hit.Decls)
173            // We need one extra item for the header text "Package-level declarations".
174            tocLen++
175        }
176        if len(result.Hit.Others) > 0 {
177            tocLen += len(result.Hit.Others)
178            // We need one extra item for the header text "Local declarations and uses".
179            tocLen++
180        }
181    }
182    // For "textual occurrences".
183    tocLen++
184    return tocLen
185}
186
MembersX
Presentation.SearchResultTxt
Corpus.Lookup.BlockStmt.r
Presentation.serveSearchDesc.p
tocLen.RangeStmt_4770.val
Corpus.Lookup
Corpus.Lookup.BlockStmt.BlockStmt.rx
Presentation.HandleSearch.title
tocLen.result
SearchResult.Hit
Presentation.SearchResultCode.result
Corpus.Lookup.c
Presentation.SearchResultTxt.result
SearchResult.Complete
Corpus.Lookup.query
tocLen
SearchResult.Found
Corpus.Lookup.BlockStmt.BlockStmt.err
Presentation.serveSearchDesc.w
Corpus.Lookup.result
Presentation.HandleSearch.contents
tocColCount.result
tocColCount.colCount
tocLen.tocLen
Presentation.SearchResultDoc
Corpus.Lookup.BlockStmt.ts
Presentation.SearchResultDoc.p
tocColCount.tocLen
Corpus.Lookup.BlockStmt.err
tocColCount
Presentation.SearchResultDoc.result
Presentation.serveSearchDesc
Presentation.SearchResultCode.p
Presentation.SearchResultTxt.p
Presentation.HandleSearch.r
SearchResult.Query
Corpus.Lookup.timestamp
Presentation.HandleSearch
Presentation.HandleSearch.query
Presentation.HandleSearch.RangeStmt_3346.f
Presentation.serveSearchDesc.data
Corpus.Lookup.index
Presentation.HandleSearch.body
SearchResult
Presentation.HandleSearch.w
SearchResult.Pak
SearchResult.Textual
Presentation.SearchResultCode
Presentation.serveSearchDesc.r
SearchResult.Alert
SearchResult.Alt
Presentation.HandleSearch.p
Presentation.HandleSearch.result
SearchResult.Idents
Members
X