1
2
3
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
19 func Synopsis(fset *token.FileSet, n ast.Node, linkify func(string) string) string {
20 return oneLineNodeDepth(fset, n, 0, linkify)
21 }
22
23
24
25
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)
48 if len(valueSpec.Names) > 1 || len(valueSpec.Values) > 1 {
49 trailer = " " + dotDotDot
50 }
51
52
53
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
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
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
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
206 func joinStrings(ss []string) string {
207 return strings.Join(ss, ", ")
208 }
209
View as plain text