|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- // Copyright 2013 The Go Authors. All rights reserved.
- //
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file or at
- // https://developers.google.com/open-source/licenses/bsd.
-
- package doc
-
- import (
- "bytes"
- "fmt"
- "go/ast"
- "go/doc"
- "go/printer"
- "go/scanner"
- "go/token"
- "math"
- "strconv"
- )
-
- const (
- notPredeclared = iota
- predeclaredType
- predeclaredConstant
- predeclaredFunction
- )
-
- // predeclared represents the set of all predeclared identifiers.
- var predeclared = map[string]int{
- "bool": predeclaredType,
- "byte": predeclaredType,
- "complex128": predeclaredType,
- "complex64": predeclaredType,
- "error": predeclaredType,
- "float32": predeclaredType,
- "float64": predeclaredType,
- "int16": predeclaredType,
- "int32": predeclaredType,
- "int64": predeclaredType,
- "int8": predeclaredType,
- "int": predeclaredType,
- "rune": predeclaredType,
- "string": predeclaredType,
- "uint16": predeclaredType,
- "uint32": predeclaredType,
- "uint64": predeclaredType,
- "uint8": predeclaredType,
- "uint": predeclaredType,
- "uintptr": predeclaredType,
-
- "true": predeclaredConstant,
- "false": predeclaredConstant,
- "iota": predeclaredConstant,
- "nil": predeclaredConstant,
-
- "append": predeclaredFunction,
- "cap": predeclaredFunction,
- "close": predeclaredFunction,
- "complex": predeclaredFunction,
- "copy": predeclaredFunction,
- "delete": predeclaredFunction,
- "imag": predeclaredFunction,
- "len": predeclaredFunction,
- "make": predeclaredFunction,
- "new": predeclaredFunction,
- "panic": predeclaredFunction,
- "print": predeclaredFunction,
- "println": predeclaredFunction,
- "real": predeclaredFunction,
- "recover": predeclaredFunction,
- }
-
- type AnnotationKind int16
-
- const (
- // Link to export in package specified by Paths[PathIndex] with fragment
- // Text[strings.LastIndex(Text[Pos:End], ".")+1:End].
- LinkAnnotation AnnotationKind = iota
-
- // Anchor with name specified by Text[Pos:End] or typeName + "." +
- // Text[Pos:End] for type declarations.
- AnchorAnnotation
-
- // Comment.
- CommentAnnotation
-
- // Link to package specified by Paths[PathIndex].
- PackageLinkAnnotation
-
- // Link to builtin entity with name Text[Pos:End].
- BuiltinAnnotation
- )
-
- type Annotation struct {
- Pos, End int32
- Kind AnnotationKind
- PathIndex int16
- }
-
- type Code struct {
- Text string
- Annotations []Annotation
- Paths []string
- }
-
- // declVisitor modifies a declaration AST for printing and collects annotations.
- type declVisitor struct {
- annotations []Annotation
- paths []string
- pathIndex map[string]int
- comments []*ast.CommentGroup
- }
-
- func (v *declVisitor) add(kind AnnotationKind, importPath string) {
- pathIndex := -1
- if importPath != "" {
- var ok bool
- pathIndex, ok = v.pathIndex[importPath]
- if !ok {
- pathIndex = len(v.paths)
- v.paths = append(v.paths, importPath)
- v.pathIndex[importPath] = pathIndex
- }
- }
- v.annotations = append(v.annotations, Annotation{Kind: kind, PathIndex: int16(pathIndex)})
- }
-
- func (v *declVisitor) ignoreName() {
- v.add(-1, "")
- }
-
- func (v *declVisitor) Visit(n ast.Node) ast.Visitor {
- switch n := n.(type) {
- case *ast.TypeSpec:
- v.ignoreName()
- switch n := n.Type.(type) {
- case *ast.InterfaceType:
- for _, f := range n.Methods.List {
- for _ = range f.Names {
- v.add(AnchorAnnotation, "")
- }
- ast.Walk(v, f.Type)
- }
- case *ast.StructType:
- for _, f := range n.Fields.List {
- for _ = range f.Names {
- v.add(AnchorAnnotation, "")
- }
- ast.Walk(v, f.Type)
- }
- default:
- ast.Walk(v, n)
- }
- case *ast.FuncDecl:
- if n.Recv != nil {
- ast.Walk(v, n.Recv)
- }
- v.ignoreName()
- ast.Walk(v, n.Type)
- case *ast.Field:
- for _ = range n.Names {
- v.ignoreName()
- }
- ast.Walk(v, n.Type)
- case *ast.ValueSpec:
- for _ = range n.Names {
- v.add(AnchorAnnotation, "")
- }
- if n.Type != nil {
- ast.Walk(v, n.Type)
- }
- for _, x := range n.Values {
- ast.Walk(v, x)
- }
- case *ast.Ident:
- switch {
- case n.Obj == nil && predeclared[n.Name] != notPredeclared:
- v.add(BuiltinAnnotation, "")
- case n.Obj != nil && ast.IsExported(n.Name):
- v.add(LinkAnnotation, "")
- default:
- v.ignoreName()
- }
- case *ast.SelectorExpr:
- if x, _ := n.X.(*ast.Ident); x != nil {
- if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
- if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
- if path, err := strconv.Unquote(spec.Path.Value); err == nil {
- v.add(PackageLinkAnnotation, path)
- if path == "C" {
- v.ignoreName()
- } else {
- v.add(LinkAnnotation, path)
- }
- return nil
- }
- }
- }
- }
- ast.Walk(v, n.X)
- v.ignoreName()
- case *ast.BasicLit:
- if n.Kind == token.STRING && len(n.Value) > 128 {
- v.comments = append(v.comments,
- &ast.CommentGroup{List: []*ast.Comment{{
- Slash: n.Pos(),
- Text: fmt.Sprintf("/* %d byte string literal not displayed */", len(n.Value)),
- }}})
- n.Value = `""`
- } else {
- return v
- }
- case *ast.CompositeLit:
- if len(n.Elts) > 100 {
- if n.Type != nil {
- ast.Walk(v, n.Type)
- }
- v.comments = append(v.comments,
- &ast.CommentGroup{List: []*ast.Comment{{
- Slash: n.Lbrace,
- Text: fmt.Sprintf("/* %d elements not displayed */", len(n.Elts)),
- }}})
- n.Elts = n.Elts[:0]
- } else {
- return v
- }
- default:
- return v
- }
- return nil
- }
-
- func (b *builder) printDecl(decl ast.Decl) (d Code) {
- v := &declVisitor{pathIndex: make(map[string]int)}
- ast.Walk(v, decl)
- b.buf = b.buf[:0]
- err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(
- sliceWriter{&b.buf},
- b.fset,
- &printer.CommentedNode{Node: decl, Comments: v.comments})
- if err != nil {
- return Code{Text: err.Error()}
- }
-
- var annotations []Annotation
- var s scanner.Scanner
- fset := token.NewFileSet()
- file := fset.AddFile("", fset.Base(), len(b.buf))
- s.Init(file, b.buf, nil, scanner.ScanComments)
- prevTok := token.ILLEGAL
- loop:
- for {
- pos, tok, lit := s.Scan()
- switch tok {
- case token.EOF:
- break loop
- case token.COMMENT:
- p := file.Offset(pos)
- e := p + len(lit)
- if prevTok == token.COMMENT {
- annotations[len(annotations)-1].End = int32(e)
- } else {
- annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int32(p), End: int32(e)})
- }
- case token.IDENT:
- if len(v.annotations) == 0 {
- // Oops!
- break loop
- }
- annotation := v.annotations[0]
- v.annotations = v.annotations[1:]
- if annotation.Kind == -1 {
- continue
- }
- p := file.Offset(pos)
- e := p + len(lit)
- annotation.Pos = int32(p)
- annotation.End = int32(e)
- annotations = append(annotations, annotation)
- }
- prevTok = tok
- }
- return Code{Text: string(b.buf), Annotations: annotations, Paths: v.paths}
- }
-
- func (b *builder) position(n ast.Node) Pos {
- var position Pos
- pos := b.fset.Position(n.Pos())
- src := b.srcs[pos.Filename]
- if src != nil {
- position.File = int16(src.index)
- position.Line = int32(pos.Line)
- end := b.fset.Position(n.End())
- if src == b.srcs[end.Filename] {
- n := end.Line - pos.Line
- if n >= 0 && n <= math.MaxUint16 {
- position.N = uint16(n)
- }
- }
- }
- return position
- }
-
- func (b *builder) printExample(e *doc.Example) (code Code, output string) {
- output = e.Output
-
- b.buf = b.buf[:0]
- var n interface{}
- if _, ok := e.Code.(*ast.File); ok {
- n = e.Play
- } else {
- n = &printer.CommentedNode{Node: e.Code, Comments: e.Comments}
- }
- err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(sliceWriter{&b.buf}, b.fset, n)
- if err != nil {
- return Code{Text: err.Error()}, output
- }
-
- // additional formatting if this is a function body
- if i := len(b.buf); i >= 2 && b.buf[0] == '{' && b.buf[i-1] == '}' {
- // remove surrounding braces
- b.buf = b.buf[1 : i-1]
- // unindent
- b.buf = bytes.Replace(b.buf, []byte("\n "), []byte("\n"), -1)
- // remove output comment
- if j := exampleOutputRx.FindIndex(b.buf); j != nil {
- b.buf = bytes.TrimSpace(b.buf[:j[0]])
- }
- } else {
- // drop output, as the output comment will appear in the code
- output = ""
- }
-
- var annotations []Annotation
- var s scanner.Scanner
- fset := token.NewFileSet()
- file := fset.AddFile("", fset.Base(), len(b.buf))
- s.Init(file, b.buf, nil, scanner.ScanComments)
- prevTok := token.ILLEGAL
- scanLoop:
- for {
- pos, tok, lit := s.Scan()
- switch tok {
- case token.EOF:
- break scanLoop
- case token.COMMENT:
- p := file.Offset(pos)
- e := p + len(lit)
- if prevTok == token.COMMENT {
- annotations[len(annotations)-1].End = int32(e)
- } else {
- annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int32(p), End: int32(e)})
- }
- }
- prevTok = tok
- }
-
- return Code{Text: string(b.buf), Annotations: annotations}, output
- }
|