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// This file contains the infrastructure to create a code 6// snippet for search results. 7// 8// Note: At the moment, this only creates HTML snippets. 9 10package godoc 11 12import ( 13 "bytes" 14 "fmt" 15 "go/ast" 16 "go/token" 17) 18 19type Snippet struct { 20 Line int 21 Text string // HTML-escaped 22} 23 24func (p *Presentation) newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { 25 // TODO instead of pretty-printing the node, should use the original source instead 26 var buf1 bytes.Buffer 27 p.writeNode(&buf1, nil, fset, decl) 28 // wrap text with <pre> tag 29 var buf2 bytes.Buffer 30 buf2.WriteString("<pre>") 31 FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil) 32 buf2.WriteString("</pre>") 33 return &Snippet{fset.Position(id.Pos()).Line, buf2.String()} 34} 35 36func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec { 37 for _, spec := range list { 38 switch s := spec.(type) { 39 case *ast.ImportSpec: 40 if s.Name == id { 41 return s 42 } 43 case *ast.ValueSpec: 44 for _, n := range s.Names { 45 if n == id { 46 return s 47 } 48 } 49 case *ast.TypeSpec: 50 if s.Name == id { 51 return s 52 } 53 } 54 } 55 return nil 56} 57 58func (p *Presentation) genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet { 59 s := findSpec(d.Specs, id) 60 if s == nil { 61 return nil // declaration doesn't contain id - exit gracefully 62 } 63 64 // only use the spec containing the id for the snippet 65 dd := &ast.GenDecl{ 66 Doc: d.Doc, 67 TokPos: d.Pos(), 68 Tok: d.Tok, 69 Lparen: d.Lparen, 70 Specs: []ast.Spec{s}, 71 Rparen: d.Rparen, 72 } 73 74 return p.newSnippet(fset, dd, id) 75} 76 77func (p *Presentation) funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet { 78 if d.Name != id { 79 return nil // declaration doesn't contain id - exit gracefully 80 } 81 82 // only use the function signature for the snippet 83 dd := &ast.FuncDecl{ 84 Doc: d.Doc, 85 Recv: d.Recv, 86 Name: d.Name, 87 Type: d.Type, 88 } 89 90 return p.newSnippet(fset, dd, id) 91} 92 93// NewSnippet creates a text snippet from a declaration decl containing an 94// identifier id. Parts of the declaration not containing the identifier 95// may be removed for a more compact snippet. 96func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { 97 // TODO(bradfitz, adg): remove this function. But it's used by indexer, which 98 // doesn't have a *Presentation, and NewSnippet needs a TabWidth. 99 var p Presentation 100 p.TabWidth = 4 101 return p.NewSnippet(fset, decl, id) 102} 103 104// NewSnippet creates a text snippet from a declaration decl containing an 105// identifier id. Parts of the declaration not containing the identifier 106// may be removed for a more compact snippet. 107func (p *Presentation) NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { 108 var s *Snippet 109 switch d := decl.(type) { 110 case *ast.GenDecl: 111 s = p.genSnippet(fset, d, id) 112 case *ast.FuncDecl: 113 s = p.funcSnippet(fset, d, id) 114 } 115 116 // handle failure gracefully 117 if s == nil { 118 var buf bytes.Buffer 119 fmt.Fprintf(&buf, `<span class="alert">could not generate a snippet for <span class="highlight">%s</span></span>`, id.Name) 120 s = &Snippet{fset.Position(id.Pos()).Line, buf.String()} 121 } 122 return s 123} 124