Source file
src/cmd/doc/main.go
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
114
115
116 if buildPackage.ImportPath == "builtin" {
117 unexported = true
118 }
119
120 symbol, method = parseSymbol(sym)
121 pkg := parsePackage(writer, buildPackage, userPath)
122 paths = append(paths, pkg.prettyPath())
123
124 defer func() {
125 pkg.flush()
126 e := recover()
127 if e == nil {
128 return
129 }
130 pkgError, ok := e.(PackageError)
131 if ok {
132 err = pkgError
133 return
134 }
135 panic(e)
136 }()
137
138
139 if showAll && symbol == "" {
140 pkg.allDoc()
141 return
142 }
143
144 switch {
145 case symbol == "":
146 pkg.packageDoc()
147 return
148 case method == "":
149 if pkg.symbolDoc(symbol) {
150 return
151 }
152 default:
153 if pkg.methodDoc(symbol, method) {
154 return
155 }
156 if pkg.fieldDoc(symbol, method) {
157 return
158 }
159 }
160 }
161 }
162
163
164 func failMessage(paths []string, symbol, method string) error {
165 var b bytes.Buffer
166 if len(paths) > 1 {
167 b.WriteString("s")
168 }
169 b.WriteString(" ")
170 for i, path := range paths {
171 if i > 0 {
172 b.WriteString(", ")
173 }
174 b.WriteString(path)
175 }
176 if method == "" {
177 return fmt.Errorf("no symbol %s in package%s", symbol, &b)
178 }
179 return fmt.Errorf("no method or field %s.%s in package%s", symbol, method, &b)
180 }
181
182
183
184
185
186
187
188
189
190
191
192
193 func parseArgs(args []string) (pkg *build.Package, path, symbol string, more bool) {
194 wd, err := os.Getwd()
195 if err != nil {
196 log.Fatal(err)
197 }
198 if len(args) == 0 {
199
200 return importDir(wd), "", "", false
201 }
202 arg := args[0]
203
204
205
206 if isDotSlash(arg) {
207 arg = filepath.Join(wd, arg)
208 }
209 switch len(args) {
210 default:
211 usage()
212 case 1:
213
214 case 2:
215
216 pkg, err := build.Import(args[0], wd, build.ImportComment)
217 if err == nil {
218 return pkg, args[0], args[1], false
219 }
220 for {
221 packagePath, ok := findNextPackage(arg)
222 if !ok {
223 break
224 }
225 if pkg, err := build.ImportDir(packagePath, build.ImportComment); err == nil {
226 return pkg, arg, args[1], true
227 }
228 }
229 return nil, args[0], args[1], false
230 }
231
232
233
234
235
236
237 var importErr error
238 if filepath.IsAbs(arg) {
239 pkg, importErr = build.ImportDir(arg, build.ImportComment)
240 if importErr == nil {
241 return pkg, arg, "", false
242 }
243 } else {
244 pkg, importErr = build.Import(arg, wd, build.ImportComment)
245 if importErr == nil {
246 return pkg, arg, "", false
247 }
248 }
249
250
251
252
253 if !strings.ContainsAny(arg, `/\`) && token.IsExported(arg) {
254 pkg, err := build.ImportDir(".", build.ImportComment)
255 if err == nil {
256 return pkg, "", arg, false
257 }
258 }
259
260
261 slash := strings.LastIndex(arg, "/")
262
263
264
265
266
267 var period int
268
269
270 for start := slash + 1; start < len(arg); start = period + 1 {
271 period = strings.Index(arg[start:], ".")
272 symbol := ""
273 if period < 0 {
274 period = len(arg)
275 } else {
276 period += start
277 symbol = arg[period+1:]
278 }
279
280 pkg, err := build.Import(arg[0:period], wd, build.ImportComment)
281 if err == nil {
282 return pkg, arg[0:period], symbol, false
283 }
284
285
286 pkgName := arg[:period]
287 for {
288 path, ok := findNextPackage(pkgName)
289 if !ok {
290 break
291 }
292 if pkg, err = build.ImportDir(path, build.ImportComment); err == nil {
293 return pkg, arg[0:period], symbol, true
294 }
295 }
296 dirs.Reset()
297 }
298
299 if slash >= 0 {
300
301
302
303
304
305
306 importErrStr := importErr.Error()
307 if strings.Contains(importErrStr, arg[:period]) {
308 log.Fatal(importErrStr)
309 } else {
310 log.Fatalf("no such package %s: %s", arg[:period], importErrStr)
311 }
312 }
313
314 return importDir(wd), "", arg, false
315 }
316
317
318
319
320
321 var dotPaths = []string{
322 `./`,
323 `../`,
324 `.\`,
325 `..\`,
326 }
327
328
329
330 func isDotSlash(arg string) bool {
331 if arg == "." || arg == ".." {
332 return true
333 }
334 for _, dotPath := range dotPaths {
335 if strings.HasPrefix(arg, dotPath) {
336 return true
337 }
338 }
339 return false
340 }
341
342
343 func importDir(dir string) *build.Package {
344 pkg, err := build.ImportDir(dir, build.ImportComment)
345 if err != nil {
346 log.Fatal(err)
347 }
348 return pkg
349 }
350
351
352
353
354 func parseSymbol(str string) (symbol, method string) {
355 if str == "" {
356 return
357 }
358 elem := strings.Split(str, ".")
359 switch len(elem) {
360 case 1:
361 case 2:
362 method = elem[1]
363 default:
364 log.Printf("too many periods in symbol specification")
365 usage()
366 }
367 symbol = elem[0]
368 return
369 }
370
371
372
373
374 func isExported(name string) bool {
375 return unexported || token.IsExported(name)
376 }
377
378
379
380 func findNextPackage(pkg string) (string, bool) {
381 if filepath.IsAbs(pkg) {
382 if dirs.offset == 0 {
383 dirs.offset = -1
384 return pkg, true
385 }
386 return "", false
387 }
388 if pkg == "" || token.IsExported(pkg) {
389 return "", false
390 }
391 pkg = path.Clean(pkg)
392 pkgSuffix := "/" + pkg
393 for {
394 d, ok := dirs.Next()
395 if !ok {
396 return "", false
397 }
398 if d.importPath == pkg || strings.HasSuffix(d.importPath, pkgSuffix) {
399 return d.dir, true
400 }
401 }
402 }
403
404 var buildCtx = build.Default
405
406
407 func splitGopath() []string {
408 return filepath.SplitList(buildCtx.GOPATH)
409 }
410
View as plain text