You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.2 KiB

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