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