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