Source file src/cmd/doc/pkg.go
1
2
3
4
5 package main
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "go/ast"
12 "go/build"
13 "go/doc"
14 "go/format"
15 "go/parser"
16 "go/printer"
17 "go/token"
18 "io"
19 "log"
20 "os"
21 "path/filepath"
22 "strings"
23 "unicode"
24 "unicode/utf8"
25 )
26
27 const (
28 punchedCardWidth = 80
29 indentedWidth = punchedCardWidth - len(indent)
30 indent = " "
31 )
32
33 type Package struct {
34 writer io.Writer
35 name string
36 userPath string
37 pkg *ast.Package
38 file *ast.File
39 doc *doc.Package
40 build *build.Package
41 typedValue map[*doc.Value]bool
42 constructor map[*doc.Func]bool
43 fs *token.FileSet
44 buf pkgBuffer
45 }
46
47
48
49 type pkgBuffer struct {
50 pkg *Package
51 printed bool
52 bytes.Buffer
53 }
54
55 func (pb *pkgBuffer) Write(p []byte) (int, error) {
56 if !pb.printed && len(p) > 0 {
57 pb.printed = true
58
59 if pb.pkg.pkg.Name != "main" || showCmd {
60 pb.pkg.packageClause()
61 }
62 }
63 return pb.Buffer.Write(p)
64 }
65
66 type PackageError string
67
68 func (p PackageError) Error() string {
69 return string(p)
70 }
71
72
73
74
75
76 func (pkg *Package) prettyPath() string {
77 path := pkg.build.ImportComment
78 if path == "" {
79 path = pkg.build.ImportPath
80 }
81 if path != "." && path != "" {
82 return path
83 }
84
85
86 path = filepath.Clean(filepath.ToSlash(pkg.build.Dir))
87
88 goroot := filepath.Join(buildCtx.GOROOT, "src")
89 if p, ok := trim(path, filepath.ToSlash(goroot)); ok {
90 return p
91 }
92 for _, gopath := range splitGopath() {
93 if p, ok := trim(path, filepath.ToSlash(gopath)); ok {
94 return p
95 }
96 }
97 return path
98 }
99
100
101
102
103
104 func trim(path, prefix string) (string, bool) {
105 if !strings.HasPrefix(path, prefix) {
106 return path, false
107 }
108 if path == prefix {
109 return path, true
110 }
111 if path[len(prefix)] == '/' {
112 return path[len(prefix)+1:], true
113 }
114 return path, false
115 }
116
117
118
119
120
121 func (pkg *Package) Fatalf(format string, args ...interface{}) {
122 panic(PackageError(fmt.Sprintf(format, args...)))
123 }
124
125
126
127 func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Package {
128 fs := token.NewFileSet()
129
130
131
132 include := func(info os.FileInfo) bool {
133 for _, name := range pkg.GoFiles {
134 if name == info.Name() {
135 return true
136 }
137 }
138 for _, name := range pkg.CgoFiles {
139 if name == info.Name() {
140 return true
141 }
142 }
143 return false
144 }
145 pkgs, err := parser.ParseDir(fs, pkg.Dir, include, parser.ParseComments)
146 if err != nil {
147 log.Fatal(err)
148 }
149
150 if len(pkgs) == 0 {
151 log.Fatalf("no source-code package in directory %s", pkg.Dir)
152 }
153 if len(pkgs) > 1 {
154 log.Fatalf("multiple packages in directory %s", pkg.Dir)
155 }
156 astPkg := pkgs[pkg.Name]
157
158
159
160
161
162
163
164
165
166 mode := doc.AllDecls
167 if showSrc {
168 mode |= doc.PreserveAST
169 }
170 docPkg := doc.New(astPkg, pkg.ImportPath, mode)
171 typedValue := make(map[*doc.Value]bool)
172 constructor := make(map[*doc.Func]bool)
173 for _, typ := range docPkg.Types {
174 docPkg.Consts = append(docPkg.Consts, typ.Consts...)
175 for _, value := range typ.Consts {
176 typedValue[value] = true
177 }
178 docPkg.Vars = append(docPkg.Vars, typ.Vars...)
179 for _, value := range typ.Vars {
180 typedValue[value] = true
181 }
182 docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
183 for _, fun := range typ.Funcs {
184
185
186 if isExported(typ.Name) {
187 constructor[fun] = true
188 }
189 }
190 }
191
192 p := &Package{
193 writer: writer,
194 name: pkg.Name,
195 userPath: userPath,
196 pkg: astPkg,
197 file: ast.MergePackageFiles(astPkg, 0),
198 doc: docPkg,
199 typedValue: typedValue,
200 constructor: constructor,
201 build: pkg,
202 fs: fs,
203 }
204 p.buf.pkg = p
205 return p
206 }
207
208 func (pkg *Package) Printf(format string, args ...interface{}) {
209 fmt.Fprintf(&pkg.buf, format, args...)
210 }
211
212 func (pkg *Package) flush() {
213 _, err := pkg.writer.Write(pkg.buf.Bytes())
214 if err != nil {
215 log.Fatal(err)
216 }
217 pkg.buf.Reset()
218 }
219
220 var newlineBytes = []byte("\n\n")
221
222
223 func (pkg *Package) newlines(n int) {
224 for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) {
225 pkg.buf.WriteRune('\n')
226 }
227 }
228
229
230
231
232 func (pkg *Package) emit(comment string, node ast.Node) {
233 if node != nil {
234 var arg interface{} = node
235 if showSrc {
236
237 arg = &printer.CommentedNode{
238 Node: node,
239 Comments: pkg.file.Comments,
240 }
241 }
242 err := format.Node(&pkg.buf, pkg.fs, arg)
243 if err != nil {
244 log.Fatal(err)
245 }
246 if comment != "" && !showSrc {
247 pkg.newlines(1)
248 doc.ToText(&pkg.buf, comment, indent, indent+indent, indentedWidth)
249 pkg.newlines(2)
250 } else {
251 pkg.newlines(1)
252 }
253 }
254 }
255
256
257 func (pkg *Package) oneLineNode(node ast.Node) string {
258 const maxDepth = 10
259 return pkg.oneLineNodeDepth(node, maxDepth)
260 }
261
262
263
264 func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
265 const dotDotDot = "..."
266 if depth == 0 {
267 return dotDotDot
268 }
269 depth--
270
271 switch n := node.(type) {
272 case nil:
273 return ""
274
275 case *ast.GenDecl:
276
277 trailer := ""
278 if len(n.Specs) > 1 {
279 trailer = " " + dotDotDot
280 }
281
282
283 typ := ""
284 for i, spec := range n.Specs {
285 valueSpec := spec.(*ast.ValueSpec)
286
287
288
289 if valueSpec.Type != nil {
290 typ = fmt.Sprintf(" %s", pkg.oneLineNodeDepth(valueSpec.Type, depth))
291 } else if len(valueSpec.Values) > 0 {
292 typ = ""
293 }
294
295 if !isExported(valueSpec.Names[0].Name) {
296 continue
297 }
298 val := ""
299 if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
300 val = fmt.Sprintf(" = %s", pkg.oneLineNodeDepth(valueSpec.Values[i], depth))
301 }
302 return fmt.Sprintf("%s %s%s%s%s", n.Tok, valueSpec.Names[0], typ, val, trailer)
303 }
304 return ""
305
306 case *ast.FuncDecl:
307
308 name := n.Name.Name
309 recv := pkg.oneLineNodeDepth(n.Recv, depth)
310 if len(recv) > 0 {
311 recv = "(" + recv + ") "
312 }
313 fnc := pkg.oneLineNodeDepth(n.Type, depth)
314 if strings.Index(fnc, "func") == 0 {
315 fnc = fnc[4:]
316 }
317 return fmt.Sprintf("func %s%s%s", recv, name, fnc)
318
319 case *ast.TypeSpec:
320 sep := " "
321 if n.Assign.IsValid() {
322 sep = " = "
323 }
324 return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth))
325
326 case *ast.FuncType:
327 var params []string
328 if n.Params != nil {
329 for _, field := range n.Params.List {
330 params = append(params, pkg.oneLineField(field, depth))
331 }
332 }
333 needParens := false
334 var results []string
335 if n.Results != nil {
336 needParens = needParens || len(n.Results.List) > 1
337 for _, field := range n.Results.List {
338 needParens = needParens || len(field.Names) > 0
339 results = append(results, pkg.oneLineField(field, depth))
340 }
341 }
342
343 param := joinStrings(params)
344 if len(results) == 0 {
345 return fmt.Sprintf("func(%s)", param)
346 }
347 result := joinStrings(results)
348 if !needParens {
349 return fmt.Sprintf("func(%s) %s", param, result)
350 }
351 return fmt.Sprintf("func(%s) (%s)", param, result)
352
353 case *ast.StructType:
354 if n.Fields == nil || len(n.Fields.List) == 0 {
355 return "struct{}"
356 }
357 return "struct{ ... }"
358
359 case *ast.InterfaceType:
360 if n.Methods == nil || len(n.Methods.List) == 0 {
361 return "interface{}"
362 }
363 return "interface{ ... }"
364
365 case *ast.FieldList:
366 if n == nil || len(n.List) == 0 {
367 return ""
368 }
369 if len(n.List) == 1 {
370 return pkg.oneLineField(n.List[0], depth)
371 }
372 return dotDotDot
373
374 case *ast.FuncLit:
375 return pkg.oneLineNodeDepth(n.Type, depth) + " { ... }"
376
377 case *ast.CompositeLit:
378 typ := pkg.oneLineNodeDepth(n.Type, depth)
379 if len(n.Elts) == 0 {
380 return fmt.Sprintf("%s{}", typ)
381 }
382 return fmt.Sprintf("%s{ %s }", typ, dotDotDot)
383
384 case *ast.ArrayType:
385 length := pkg.oneLineNodeDepth(n.Len, depth)
386 element := pkg.oneLineNodeDepth(n.Elt, depth)
387 return fmt.Sprintf("[%s]%s", length, element)
388
389 case *ast.MapType:
390 key := pkg.oneLineNodeDepth(n.Key, depth)
391 value := pkg.oneLineNodeDepth(n.Value, depth)
392 return fmt.Sprintf("map[%s]%s", key, value)
393
394 case *ast.CallExpr:
395 fnc := pkg.oneLineNodeDepth(n.Fun, depth)
396 var args []string
397 for _, arg := range n.Args {
398 args = append(args, pkg.oneLineNodeDepth(arg, depth))
399 }
400 return fmt.Sprintf("%s(%s)", fnc, joinStrings(args))
401
402 case *ast.UnaryExpr:
403 return fmt.Sprintf("%s%s", n.Op, pkg.oneLineNodeDepth(n.X, depth))
404
405 case *ast.Ident:
406 return n.Name
407
408 default:
409
410 buf := new(bytes.Buffer)
411 format.Node(buf, pkg.fs, node)
412 s := buf.String()
413 if strings.Contains(s, "\n") {
414 return dotDotDot
415 }
416 return s
417 }
418 }
419
420
421 func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
422 var names []string
423 for _, name := range field.Names {
424 names = append(names, name.Name)
425 }
426 if len(names) == 0 {
427 return pkg.oneLineNodeDepth(field.Type, depth)
428 }
429 return joinStrings(names) + " " + pkg.oneLineNodeDepth(field.Type, depth)
430 }
431
432
433
434 func joinStrings(ss []string) string {
435 var n int
436 for i, s := range ss {
437 n += len(s) + len(", ")
438 if n > punchedCardWidth {
439 ss = append(ss[:i:i], "...")
440 break
441 }
442 }
443 return strings.Join(ss, ", ")
444 }
445
446
447 func (pkg *Package) allDoc() {
448 defer pkg.flush()
449
450 doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
451 pkg.newlines(1)
452
453 printed := make(map[*ast.GenDecl]bool)
454
455 hdr := ""
456 printHdr := func(s string) {
457 if hdr != s {
458 pkg.Printf("\n%s\n\n", s)
459 hdr = s
460 }
461 }
462
463
464 for _, value := range pkg.doc.Consts {
465
466
467 for _, name := range value.Names {
468 if isExported(name) && !pkg.typedValue[value] {
469 printHdr("CONSTANTS")
470 pkg.valueDoc(value, printed)
471 break
472 }
473 }
474 }
475
476
477 for _, value := range pkg.doc.Vars {
478
479
480 for _, name := range value.Names {
481 if isExported(name) && !pkg.typedValue[value] {
482 printHdr("VARIABLES")
483 pkg.valueDoc(value, printed)
484 break
485 }
486 }
487 }
488
489
490 for _, fun := range pkg.doc.Funcs {
491 if isExported(fun.Name) && !pkg.constructor[fun] {
492 printHdr("FUNCTIONS")
493 pkg.emit(fun.Doc, fun.Decl)
494 }
495 }
496
497
498 for _, typ := range pkg.doc.Types {
499 if isExported(typ.Name) {
500 printHdr("TYPES")
501 pkg.typeDoc(typ)
502 }
503 }
504 }
505
506
507 func (pkg *Package) packageDoc() {
508 defer pkg.flush()
509
510 doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
511 pkg.newlines(1)
512
513 if pkg.pkg.Name == "main" && !showCmd {
514
515 return
516 }
517
518 pkg.newlines(2)
519 pkg.valueSummary(pkg.doc.Consts, false)
520 pkg.valueSummary(pkg.doc.Vars, false)
521 pkg.funcSummary(pkg.doc.Funcs, false)
522 pkg.typeSummary()
523 pkg.bugs()
524 }
525
526
527 func (pkg *Package) packageClause() {
528 importPath := pkg.build.ImportComment
529 if importPath == "" {
530 importPath = pkg.build.ImportPath
531 }
532
533
534
535
536
537 if usingModules {
538 for _, root := range codeRoots() {
539 if pkg.build.Dir == root.dir {
540 importPath = root.importPath
541 break
542 }
543 if strings.HasPrefix(pkg.build.Dir, root.dir+string(filepath.Separator)) {
544 suffix := filepath.ToSlash(pkg.build.Dir[len(root.dir)+1:])
545 if root.importPath == "" {
546 importPath = suffix
547 } else {
548 importPath = root.importPath + "/" + suffix
549 }
550 break
551 }
552 }
553 }
554
555 pkg.Printf("package %s // import %q\n\n", pkg.name, importPath)
556 if !usingModules && importPath != pkg.build.ImportPath {
557 pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
558 }
559 }
560
561
562
563
564 func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) {
565 var isGrouped map[*doc.Value]bool
566 if !showGrouped {
567 isGrouped = make(map[*doc.Value]bool)
568 for _, typ := range pkg.doc.Types {
569 if !isExported(typ.Name) {
570 continue
571 }
572 for _, c := range typ.Consts {
573 isGrouped[c] = true
574 }
575 for _, v := range typ.Vars {
576 isGrouped[v] = true
577 }
578 }
579 }
580
581 for _, value := range values {
582 if !isGrouped[value] {
583 if decl := pkg.oneLineNode(value.Decl); decl != "" {
584 pkg.Printf("%s\n", decl)
585 }
586 }
587 }
588 }
589
590
591
592 func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
593 for _, fun := range funcs {
594
595 if isExported(fun.Name) {
596 if showConstructors || !pkg.constructor[fun] {
597 pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl))
598 }
599 }
600 }
601 }
602
603
604 func (pkg *Package) typeSummary() {
605 for _, typ := range pkg.doc.Types {
606 for _, spec := range typ.Decl.Specs {
607 typeSpec := spec.(*ast.TypeSpec)
608 if isExported(typeSpec.Name.Name) {
609 pkg.Printf("%s\n", pkg.oneLineNode(typeSpec))
610
611 for _, c := range typ.Consts {
612 if decl := pkg.oneLineNode(c.Decl); decl != "" {
613 pkg.Printf(indent+"%s\n", decl)
614 }
615 }
616 for _, v := range typ.Vars {
617 if decl := pkg.oneLineNode(v.Decl); decl != "" {
618 pkg.Printf(indent+"%s\n", decl)
619 }
620 }
621 for _, constructor := range typ.Funcs {
622 if isExported(constructor.Name) {
623 pkg.Printf(indent+"%s\n", pkg.oneLineNode(constructor.Decl))
624 }
625 }
626 }
627 }
628 }
629 }
630
631
632
633 func (pkg *Package) bugs() {
634 if pkg.doc.Notes["BUG"] == nil {
635 return
636 }
637 pkg.Printf("\n")
638 for _, note := range pkg.doc.Notes["BUG"] {
639 pkg.Printf("%s: %v\n", "BUG", note.Body)
640 }
641 }
642
643
644 func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) {
645 for _, value := range docValues {
646 for _, name := range value.Names {
647 if match(symbol, name) {
648 values = append(values, value)
649 }
650 }
651 }
652 return
653 }
654
655
656 func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) {
657 for _, fun := range pkg.doc.Funcs {
658 if match(symbol, fun.Name) {
659 funcs = append(funcs, fun)
660 }
661 }
662 return
663 }
664
665
666
667 func (pkg *Package) findTypes(symbol string) (types []*doc.Type) {
668 for _, typ := range pkg.doc.Types {
669 if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) {
670 types = append(types, typ)
671 }
672 }
673 return
674 }
675
676
677
678 func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec {
679 for _, spec := range decl.Specs {
680 typeSpec := spec.(*ast.TypeSpec)
681 if symbol == typeSpec.Name.Name {
682 return typeSpec
683 }
684 }
685 return nil
686 }
687
688
689
690
691 func (pkg *Package) symbolDoc(symbol string) bool {
692 defer pkg.flush()
693 found := false
694
695 for _, fun := range pkg.findFuncs(symbol) {
696
697 decl := fun.Decl
698 pkg.emit(fun.Doc, decl)
699 found = true
700 }
701
702 values := pkg.findValues(symbol, pkg.doc.Consts)
703 values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
704
705
706
707
708 printed := make(map[*ast.GenDecl]bool)
709 for _, value := range values {
710 pkg.valueDoc(value, printed)
711 found = true
712 }
713
714 for _, typ := range pkg.findTypes(symbol) {
715 pkg.typeDoc(typ)
716 found = true
717 }
718 if !found {
719
720 if !pkg.printMethodDoc("", symbol) {
721 return false
722 }
723 }
724 return true
725 }
726
727
728 func (pkg *Package) valueDoc(value *doc.Value, printed map[*ast.GenDecl]bool) {
729 if printed[value.Decl] {
730 return
731 }
732
733
734
735
736
737 specs := make([]ast.Spec, 0, len(value.Decl.Specs))
738 var typ ast.Expr
739 for _, spec := range value.Decl.Specs {
740 vspec := spec.(*ast.ValueSpec)
741
742
743
744 if vspec.Type != nil {
745 typ = vspec.Type
746 }
747
748 for _, ident := range vspec.Names {
749 if showSrc || isExported(ident.Name) {
750 if vspec.Type == nil && vspec.Values == nil && typ != nil {
751
752
753 vspec.Type = &ast.Ident{
754 Name: pkg.oneLineNode(typ),
755 NamePos: vspec.End() - 1,
756 }
757 }
758
759 specs = append(specs, vspec)
760 typ = nil
761 break
762 }
763 }
764 }
765 if len(specs) == 0 {
766 return
767 }
768 value.Decl.Specs = specs
769 pkg.emit(value.Doc, value.Decl)
770 printed[value.Decl] = true
771 }
772
773
774
775 func (pkg *Package) typeDoc(typ *doc.Type) {
776 decl := typ.Decl
777 spec := pkg.findTypeSpec(decl, typ.Name)
778 trimUnexportedElems(spec)
779
780 if len(decl.Specs) > 1 {
781 decl.Specs = []ast.Spec{spec}
782 }
783 pkg.emit(typ.Doc, decl)
784 pkg.newlines(2)
785
786 if showAll {
787 printed := make(map[*ast.GenDecl]bool)
788
789 values := typ.Consts
790 values = append(values, typ.Vars...)
791 for _, value := range values {
792 for _, name := range value.Names {
793 if isExported(name) {
794 pkg.valueDoc(value, printed)
795 break
796 }
797 }
798 }
799 funcs := typ.Funcs
800 funcs = append(funcs, typ.Methods...)
801 for _, fun := range funcs {
802 if isExported(fun.Name) {
803 pkg.emit(fun.Doc, fun.Decl)
804 if fun.Doc == "" {
805 pkg.newlines(2)
806 }
807 }
808 }
809 } else {
810 pkg.valueSummary(typ.Consts, true)
811 pkg.valueSummary(typ.Vars, true)
812 pkg.funcSummary(typ.Funcs, true)
813 pkg.funcSummary(typ.Methods, true)
814 }
815 }
816
817
818
819
820 func trimUnexportedElems(spec *ast.TypeSpec) {
821 if unexported || showSrc {
822 return
823 }
824 switch typ := spec.Type.(type) {
825 case *ast.StructType:
826 typ.Fields = trimUnexportedFields(typ.Fields, false)
827 case *ast.InterfaceType:
828 typ.Methods = trimUnexportedFields(typ.Methods, true)
829 }
830 }
831
832
833 func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList {
834 what := "methods"
835 if !isInterface {
836 what = "fields"
837 }
838
839 trimmed := false
840 list := make([]*ast.Field, 0, len(fields.List))
841 for _, field := range fields.List {
842 names := field.Names
843 if len(names) == 0 {
844
845
846
847 ty := field.Type
848 if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok {
849
850
851 ty = se.X
852 }
853 switch ident := ty.(type) {
854 case *ast.Ident:
855 if isInterface && ident.Name == "error" && ident.Obj == nil {
856
857
858
859 list = append(list, field)
860 continue
861 }
862 names = []*ast.Ident{ident}
863 case *ast.SelectorExpr:
864
865 names = []*ast.Ident{ident.Sel}
866 }
867 if names == nil {
868
869 log.Print("invalid program: unexpected type for embedded field")
870 }
871 }
872
873 ok := true
874 for _, name := range names {
875 if !isExported(name.Name) {
876 trimmed = true
877 ok = false
878 break
879 }
880 }
881 if ok {
882 list = append(list, field)
883 }
884 }
885 if !trimmed {
886 return fields
887 }
888 unexportedField := &ast.Field{
889 Type: &ast.Ident{
890
891
892
893
894 Name: "",
895 NamePos: fields.Closing - 1,
896 },
897 Comment: &ast.CommentGroup{
898 List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}},
899 },
900 }
901 return &ast.FieldList{
902 Opening: fields.Opening,
903 List: append(list, unexportedField),
904 Closing: fields.Closing,
905 }
906 }
907
908
909
910
911 func (pkg *Package) printMethodDoc(symbol, method string) bool {
912 defer pkg.flush()
913 types := pkg.findTypes(symbol)
914 if types == nil {
915 if symbol == "" {
916 return false
917 }
918 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
919 }
920 found := false
921 for _, typ := range types {
922 if len(typ.Methods) > 0 {
923 for _, meth := range typ.Methods {
924 if match(method, meth.Name) {
925 decl := meth.Decl
926 pkg.emit(meth.Doc, decl)
927 found = true
928 }
929 }
930 continue
931 }
932 if symbol == "" {
933 continue
934 }
935
936
937 spec := pkg.findTypeSpec(typ.Decl, typ.Name)
938 inter, ok := spec.Type.(*ast.InterfaceType)
939 if !ok {
940
941 continue
942 }
943 for _, iMethod := range inter.Methods.List {
944
945
946 if len(iMethod.Names) == 0 {
947 continue
948 }
949 name := iMethod.Names[0].Name
950 if match(method, name) {
951 if iMethod.Doc != nil {
952 for _, comment := range iMethod.Doc.List {
953 doc.ToText(&pkg.buf, comment.Text, "", indent, indentedWidth)
954 }
955 }
956 s := pkg.oneLineNode(iMethod.Type)
957
958
959 lineComment := ""
960 if iMethod.Comment != nil {
961 lineComment = fmt.Sprintf(" %s", iMethod.Comment.List[0].Text)
962 }
963 pkg.Printf("func %s%s%s\n", name, s[4:], lineComment)
964 found = true
965 }
966 }
967 }
968 return found
969 }
970
971
972
973
974 func (pkg *Package) printFieldDoc(symbol, fieldName string) bool {
975 if symbol == "" || fieldName == "" {
976 return false
977 }
978 defer pkg.flush()
979 types := pkg.findTypes(symbol)
980 if types == nil {
981 pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
982 }
983 found := false
984 numUnmatched := 0
985 for _, typ := range types {
986
987 spec := pkg.findTypeSpec(typ.Decl, typ.Name)
988 structType, ok := spec.Type.(*ast.StructType)
989 if !ok {
990
991 continue
992 }
993 for _, field := range structType.Fields.List {
994
995 for _, name := range field.Names {
996 if !match(fieldName, name.Name) {
997 numUnmatched++
998 continue
999 }
1000 if !found {
1001 pkg.Printf("type %s struct {\n", typ.Name)
1002 }
1003 if field.Doc != nil {
1004
1005
1006 docBuf := bytes.Buffer{}
1007 doc.ToText(&docBuf, field.Doc.Text(), "", indent, indentedWidth)
1008 scanner := bufio.NewScanner(&docBuf)
1009 for scanner.Scan() {
1010 fmt.Fprintf(&pkg.buf, "%s// %s\n", indent, scanner.Bytes())
1011 }
1012 }
1013 s := pkg.oneLineNode(field.Type)
1014 lineComment := ""
1015 if field.Comment != nil {
1016 lineComment = fmt.Sprintf(" %s", field.Comment.List[0].Text)
1017 }
1018 pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment)
1019 found = true
1020 }
1021 }
1022 }
1023 if found {
1024 if numUnmatched > 0 {
1025 pkg.Printf("\n // ... other fields elided ...\n")
1026 }
1027 pkg.Printf("}\n")
1028 }
1029 return found
1030 }
1031
1032
1033 func (pkg *Package) methodDoc(symbol, method string) bool {
1034 defer pkg.flush()
1035 return pkg.printMethodDoc(symbol, method)
1036 }
1037
1038
1039 func (pkg *Package) fieldDoc(symbol, field string) bool {
1040 defer pkg.flush()
1041 return pkg.printFieldDoc(symbol, field)
1042 }
1043
1044
1045
1046
1047 func match(user, program string) bool {
1048 if !isExported(program) {
1049 return false
1050 }
1051 if matchCase {
1052 return user == program
1053 }
1054 for _, u := range user {
1055 p, w := utf8.DecodeRuneInString(program)
1056 program = program[w:]
1057 if u == p {
1058 continue
1059 }
1060 if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
1061 continue
1062 }
1063 return false
1064 }
1065 return program == ""
1066 }
1067
1068
1069
1070 func simpleFold(r rune) rune {
1071 for {
1072 r1 := unicode.SimpleFold(r)
1073 if r1 <= r {
1074 return r1
1075 }
1076 r = r1
1077 }
1078 }
1079
View as plain text