...

Source file src/cloud.google.com/go/third_party/pkgsite/synopsis.go

Documentation: cloud.google.com/go/third_party/pkgsite

     1  // Copyright 2017 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  package pkgsite
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/format"
    12  	"go/token"
    13  	"strings"
    14  )
    15  
    16  const maxSynopsisNodeDepth = 10
    17  
    18  // Synopsis returns a one-line summary of the given input node.
    19  func Synopsis(fset *token.FileSet, n ast.Node, linkify func(string) string) string {
    20  	return oneLineNodeDepth(fset, n, 0, linkify)
    21  }
    22  
    23  // oneLineNodeDepth returns a one-line summary of the given input node.
    24  // The depth specifies the current depth when traversing the AST and the
    25  // function will stop traversing once depth reaches maxSynopsisNodeDepth.
    26  func oneLineNodeDepth(fset *token.FileSet, node ast.Node, depth int, linkify func(string) string) string {
    27  	const dotDotDot = "..."
    28  	if depth == maxSynopsisNodeDepth {
    29  		return dotDotDot
    30  	}
    31  	depth++
    32  
    33  	switch n := node.(type) {
    34  	case nil:
    35  		return ""
    36  
    37  	case *ast.GenDecl:
    38  		trailer := ""
    39  		if len(n.Specs) > 1 {
    40  			trailer = " " + dotDotDot
    41  		}
    42  
    43  		switch n.Tok {
    44  		case token.CONST, token.VAR:
    45  			typ := ""
    46  			for i, spec := range n.Specs {
    47  				valueSpec := spec.(*ast.ValueSpec) // must succeed; we can't mix types in one GenDecl.
    48  				if len(valueSpec.Names) > 1 || len(valueSpec.Values) > 1 {
    49  					trailer = " " + dotDotDot
    50  				}
    51  
    52  				// The type name may carry over from a previous specification in the
    53  				// case of constants and iota.
    54  				if valueSpec.Type != nil {
    55  					typ = fmt.Sprintf(" %s", oneLineNodeDepth(fset, valueSpec.Type, depth, linkify))
    56  				} else if len(valueSpec.Values) > 0 {
    57  					typ = ""
    58  				}
    59  
    60  				val := ""
    61  				if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
    62  					val = fmt.Sprintf(" = %s", oneLineNodeDepth(fset, valueSpec.Values[i], depth, linkify))
    63  				}
    64  				return fmt.Sprintf("%s %s%s%s%s", n.Tok, valueSpec.Names[0], typ, val, trailer)
    65  			}
    66  		case token.TYPE:
    67  			if len(n.Specs) > 0 {
    68  				return oneLineNodeDepth(fset, n.Specs[0], depth, linkify) + trailer
    69  			}
    70  		case token.IMPORT:
    71  			if len(n.Specs) > 0 {
    72  				pkg := n.Specs[0].(*ast.ImportSpec).Path.Value
    73  				return fmt.Sprintf("%s %s%s", n.Tok, pkg, trailer)
    74  			}
    75  		}
    76  		return fmt.Sprintf("%s ()", n.Tok)
    77  
    78  	case *ast.FuncDecl:
    79  		// Formats func declarations.
    80  		name := n.Name.Name
    81  		recv := oneLineNodeDepth(fset, n.Recv, depth, linkify)
    82  		if len(recv) > 0 {
    83  			recv = "(" + recv + ") "
    84  		}
    85  		fnc := oneLineNodeDepth(fset, n.Type, depth, linkify)
    86  		if strings.Index(fnc, "func") == 0 {
    87  			fnc = fnc[4:]
    88  		}
    89  		return fmt.Sprintf("func %s%s%s", recv, name, fnc)
    90  
    91  	case *ast.TypeSpec:
    92  		sep := " "
    93  		if n.Assign.IsValid() {
    94  			sep = " = "
    95  		}
    96  		return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, oneLineNodeDepth(fset, n.Type, depth, linkify))
    97  
    98  	case *ast.FuncType:
    99  		var params []string
   100  		if n.Params != nil {
   101  			for _, field := range n.Params.List {
   102  				params = append(params, oneLineField(fset, field, depth, linkify))
   103  			}
   104  		}
   105  		needParens := false
   106  		var results []string
   107  		if n.Results != nil {
   108  			needParens = needParens || len(n.Results.List) > 1
   109  			for _, field := range n.Results.List {
   110  				needParens = needParens || len(field.Names) > 0
   111  				results = append(results, oneLineField(fset, field, depth, linkify))
   112  			}
   113  		}
   114  
   115  		param := joinStrings(params)
   116  		if len(results) == 0 {
   117  			return fmt.Sprintf("func(%s)", param)
   118  		}
   119  		result := joinStrings(results)
   120  		if !needParens {
   121  			return fmt.Sprintf("func(%s) %s", param, result)
   122  		}
   123  		return fmt.Sprintf("func(%s) (%s)", param, result)
   124  
   125  	case *ast.StructType:
   126  		if n.Fields == nil || len(n.Fields.List) == 0 {
   127  			return "struct{}"
   128  		}
   129  		return "struct{ ... }"
   130  
   131  	case *ast.InterfaceType:
   132  		if n.Methods == nil || len(n.Methods.List) == 0 {
   133  			return "interface{}"
   134  		}
   135  		return "interface{ ... }"
   136  
   137  	case *ast.FieldList:
   138  		if n == nil || len(n.List) == 0 {
   139  			return ""
   140  		}
   141  		if len(n.List) == 1 {
   142  			return oneLineField(fset, n.List[0], depth, linkify)
   143  		}
   144  		return dotDotDot
   145  
   146  	case *ast.FuncLit:
   147  		return oneLineNodeDepth(fset, n.Type, depth, linkify) + " { ... }"
   148  
   149  	case *ast.CompositeLit:
   150  		typ := oneLineNodeDepth(fset, n.Type, depth, linkify)
   151  		if len(n.Elts) == 0 {
   152  			return fmt.Sprintf("%s{}", typ)
   153  		}
   154  		return fmt.Sprintf("%s{ %s }", typ, dotDotDot)
   155  
   156  	case *ast.ArrayType:
   157  		length := oneLineNodeDepth(fset, n.Len, depth, linkify)
   158  		element := oneLineNodeDepth(fset, n.Elt, depth, linkify)
   159  		return fmt.Sprintf("[%s]%s", length, element)
   160  
   161  	case *ast.MapType:
   162  		key := oneLineNodeDepth(fset, n.Key, depth, linkify)
   163  		value := oneLineNodeDepth(fset, n.Value, depth, linkify)
   164  		return fmt.Sprintf("map[%s]%s", key, value)
   165  
   166  	case *ast.CallExpr:
   167  		fnc := oneLineNodeDepth(fset, n.Fun, depth, linkify)
   168  		var args []string
   169  		for _, arg := range n.Args {
   170  			args = append(args, oneLineNodeDepth(fset, arg, depth, linkify))
   171  		}
   172  		return fmt.Sprintf("%s(%s)", fnc, joinStrings(args))
   173  
   174  	case *ast.UnaryExpr:
   175  		return fmt.Sprintf("%s%s", n.Op, oneLineNodeDepth(fset, n.X, depth, linkify))
   176  
   177  	case *ast.Ident:
   178  		return linkify(n.Name)
   179  
   180  	default:
   181  		// As a fallback, use default formatter for all unknown node types.
   182  		buf := new(bytes.Buffer)
   183  		format.Node(buf, fset, node)
   184  		s := buf.String()
   185  		if strings.Contains(s, "\n") {
   186  			return dotDotDot
   187  		}
   188  		return linkify(s)
   189  	}
   190  }
   191  
   192  // oneLineField returns a one-line summary of the field.
   193  func oneLineField(fset *token.FileSet, field *ast.Field, depth int, linkify func(string) string) string {
   194  	var names []string
   195  	for _, name := range field.Names {
   196  		names = append(names, name.Name)
   197  	}
   198  	t := oneLineNodeDepth(fset, field.Type, depth, linkify)
   199  	if len(names) == 0 {
   200  		return t
   201  	}
   202  	return joinStrings(names) + " " + t
   203  }
   204  
   205  // joinStrings formats the input as a comma-separated list.
   206  func joinStrings(ss []string) string {
   207  	return strings.Join(ss, ", ")
   208  }
   209  

View as plain text