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  }