github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/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 main
    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 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  	writeNode(&buf1, 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 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 newSnippet(fset, dd, id)
    75  }
    76  
    77  func 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 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  //
    97  func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) {
    98  	switch d := decl.(type) {
    99  	case *ast.GenDecl:
   100  		s = genSnippet(fset, d, id)
   101  	case *ast.FuncDecl:
   102  		s = funcSnippet(fset, d, id)
   103  	}
   104  
   105  	// handle failure gracefully
   106  	if s == nil {
   107  		var buf bytes.Buffer
   108  		fmt.Fprintf(&buf, `<span class="alert">could not generate a snippet for <span class="highlight">%s</span></span>`, id.Name)
   109  		s = &Snippet{fset.Position(id.Pos()).Line, buf.String()}
   110  	}
   111  	return
   112  }