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