github.com/v2fly/tools@v0.100.0/godoc/snippet.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 // 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 10 package godoc 11 12 import ( 13 "bytes" 14 "fmt" 15 "go/ast" 16 "go/token" 17 ) 18 19 type Snippet struct { 20 Line int 21 Text string // HTML-escaped 22 } 23 24 func (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 36 func 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 58 func (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 77 func (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. 96 func 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. 107 func (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 }