Source file
src/cmd/doc/main.go
Documentation: cmd/doc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package main
39
40 import (
41 "bytes"
42 "flag"
43 "fmt"
44 "go/build"
45 "go/token"
46 "io"
47 "log"
48 "os"
49 "path"
50 "path/filepath"
51 "strings"
52 )
53
54 var (
55 unexported bool
56 matchCase bool
57 showAll bool
58 showCmd bool
59 showSrc bool
60 short bool
61 )
62
63
64 func usage() {
65 fmt.Fprintf(os.Stderr, "Usage of [go] doc:\n")
66 fmt.Fprintf(os.Stderr, "\tgo doc\n")
67 fmt.Fprintf(os.Stderr, "\tgo doc <pkg>\n")
68 fmt.Fprintf(os.Stderr, "\tgo doc <sym>[.<methodOrField>]\n")
69 fmt.Fprintf(os.Stderr, "\tgo doc [<pkg>.]<sym>[.<methodOrField>]\n")
70 fmt.Fprintf(os.Stderr, "\tgo doc [<pkg>.][<sym>.]<methodOrField>\n")
71 fmt.Fprintf(os.Stderr, "\tgo doc <pkg> <sym>[.<methodOrField>]\n")
72 fmt.Fprintf(os.Stderr, "For more information run\n")
73 fmt.Fprintf(os.Stderr, "\tgo help doc\n\n")
74 fmt.Fprintf(os.Stderr, "Flags:\n")
75 flag.PrintDefaults()
76 os.Exit(2)
77 }
78
79 func main() {
80 log.SetFlags(0)
81 log.SetPrefix("doc: ")
82 dirsInit()
83 err := do(os.Stdout, flag.CommandLine, os.Args[1:])
84 if err != nil {
85 log.Fatal(err)
86 }
87 }
88
89
90 func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
91 flagSet.Usage = usage
92 unexported = false
93 matchCase = false
94 flagSet.BoolVar(&unexported, "u", false, "show unexported symbols as well as exported")
95 flagSet.BoolVar(&matchCase, "c", false, "symbol matching honors case (paths not affected)")
96 flagSet.BoolVar(&showAll, "all", false, "show all documentation for package")
97 flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command")
98 flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
99 flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol")
100 flagSet.Parse(args)
101 var paths []string
102 var symbol, method string
103
104 dirs.Reset()
105 for i := 0; ; i++ {
106 buildPackage, userPath, sym, more := parseArgs(flagSet.Args())
107 if i > 0 && !more {
108 return failMessage(paths, symbol, method)
109 }
110 if buildPackage == nil {
111 return fmt.Errorf("no such package: %s", userPath)
112 }
113 symbol, method = parseSymbol(sym)
114 pkg := parsePackage(writer, buildPackage, userPath)
115 paths = append(paths, pkg.prettyPath())
116
117 defer func() {
118 pkg.flush()
119 e := recover()
120 if e == nil {
121 return
122 }
123 pkgError, ok := e.(PackageError)
124 if ok {
125 err = pkgError
126 return
127 }
128 panic(e)
129 }()
130
131
132
133 if pkg.build.ImportPath == "builtin" {
134 unexported = true
135 }
136
137
138 if showAll && symbol == "" {
139 pkg.allDoc()
140 return
141 }
142
143 switch {
144 case symbol == "":
145 pkg.packageDoc()
146 return
147 case method == "":
148 if pkg.symbolDoc(symbol) {
149 return
150 }
151 default:
152 if pkg.methodDoc(symbol, method) {
153 return
154 }
155 if pkg.fieldDoc(symbol, method) {
156 return
157 }
158 }
159 }
160 }
161
162
163 func failMessage(paths []string, symbol, method string) error {
164 var b bytes.Buffer
165 if len(paths) > 1 {
166 b.WriteString("s")
167 }
168 b.WriteString(" ")
169 for i, path := range paths {
170 if i > 0 {
171 b.WriteString(", ")
172 }
173 b.WriteString(path)
174 }
175 if method == "" {
176 return fmt.Errorf("no symbol %s in package%s", symbol, &b)
177 }
178 return fmt.Errorf("no method or field %s.%s in package%s", symbol, method, &b)
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192 func parseArgs(args []string) (pkg *build.Package, path, symbol string, more bool) {
193 wd, err := os.Getwd()
194 if err != nil {
195 log.Fatal(err)
196 }
197 if len(args) == 0 {
198
199 return importDir(wd), "", "", false
200 }
201 arg := args[0]
202
203
204
205 if isDotSlash(arg) {
206 arg = filepath.Join(wd, arg)
207 }
208 switch len(args) {
209 default:
210 usage()
211 case 1:
212
213 case 2:
214
215 pkg, err := build.Import(args[0], wd, build.ImportComment)
216 if err == nil {
217 return pkg, args[0], args[1], false
218 }
219 for {
220 packagePath, ok := findNextPackage(arg)
221 if !ok {
222 break
223 }
224 if pkg, err := build.ImportDir(packagePath, build.ImportComment); err == nil {
225 return pkg, arg, args[1], true
226 }
227 }
228 return nil, args[0], args[1], false
229 }
230
231
232
233
234
235
236 var importErr error
237 if filepath.IsAbs(arg) {
238 pkg, importErr = build.ImportDir(arg, build.ImportComment)
239 if importErr == nil {
240 return pkg, arg, "", false
241 }
242 } else {
243 pkg, importErr = build.Import(arg, wd, build.ImportComment)
244 if importErr == nil {
245 return pkg, arg, "", false
246 }
247 }
248
249
250
251
252 if !strings.ContainsAny(arg, `/\`) && token.IsExported(arg) {
253 pkg, err := build.ImportDir(".", build.ImportComment)
254 if err == nil {
255 return pkg, "", arg, false
256 }
257 }
258
259
260 slash := strings.LastIndex(arg, "/")
261
262
263
264
265
266 var period int
267
268
269 for start := slash + 1; start < len(arg); start = period + 1 {
270 period = strings.Index(arg[start:], ".")
271 symbol := ""
272 if period < 0 {
273 period = len(arg)
274 } else {
275 period += start
276 symbol = arg[period+1:]
277 }
278
279 pkg, err := build.Import(arg[0:period], wd, build.ImportComment)
280 if err == nil {
281 return pkg, arg[0:period], symbol, false
282 }
283
284
285 pkgName := arg[:period]
286 for {
287 path, ok := findNextPackage(pkgName)
288 if !ok {
289 break
290 }
291 if pkg, err = build.ImportDir(path, build.ImportComment); err == nil {
292 return pkg, arg[0:period], symbol, true
293 }
294 }
295 dirs.Reset()
296 }
297
298 if slash >= 0 {
299
300
301
302
303
304
305 importErrStr := importErr.Error()
306 if strings.Contains(importErrStr, arg[:period]) {
307 log.Fatal(importErrStr)
308 } else {
309 log.Fatalf("no such package %s: %s", arg[:period], importErrStr)
310 }
311 }
312
313 return importDir(wd), "", arg, false
314 }
315
316
317
318
319
320 var dotPaths = []string{
321 `./`,
322 `../`,
323 `.\`,
324 `..\`,
325 }
326
327
328
329 func isDotSlash(arg string) bool {
330 if arg == "." || arg == ".." {
331 return true
332 }
333 for _, dotPath := range dotPaths {
334 if strings.HasPrefix(arg, dotPath) {
335 return true
336 }
337 }
338 return false
339 }
340
341
342 func importDir(dir string) *build.Package {
343 pkg, err := build.ImportDir(dir, build.ImportComment)
344 if err != nil {
345 log.Fatal(err)
346 }
347 return pkg
348 }
349
350
351
352
353 func parseSymbol(str string) (symbol, method string) {
354 if str == "" {
355 return
356 }
357 elem := strings.Split(str, ".")
358 switch len(elem) {
359 case 1:
360 case 2:
361 method = elem[1]
362 default:
363 log.Printf("too many periods in symbol specification")
364 usage()
365 }
366 symbol = elem[0]
367 return
368 }
369
370
371
372
373 func isExported(name string) bool {
374 return unexported || token.IsExported(name)
375 }
376
377
378
379 func findNextPackage(pkg string) (string, bool) {
380 if filepath.IsAbs(pkg) {
381 if dirs.offset == 0 {
382 dirs.offset = -1
383 return pkg, true
384 }
385 return "", false
386 }
387 if pkg == "" || token.IsExported(pkg) {
388 return "", false
389 }
390 pkg = path.Clean(pkg)
391 pkgSuffix := "/" + pkg
392 for {
393 d, ok := dirs.Next()
394 if !ok {
395 return "", false
396 }
397 if d.importPath == pkg || strings.HasSuffix(d.importPath, pkgSuffix) {
398 return d.dir, true
399 }
400 }
401 }
402
403 var buildCtx = build.Default
404
405
406 func splitGopath() []string {
407 return filepath.SplitList(buildCtx.GOPATH)
408 }
409
View as plain text