Source file src/pkg/go/printer/printer.go
1
2
3
4
5
6 package printer
7
8 import (
9 "fmt"
10 "go/ast"
11 "go/token"
12 "io"
13 "os"
14 "strconv"
15 "strings"
16 "text/tabwriter"
17 "unicode"
18 )
19
20 const (
21 maxNewlines = 2
22 debug = false
23 infinity = 1 << 30
24 )
25
26 type whiteSpace byte
27
28 const (
29 ignore = whiteSpace(0)
30 blank = whiteSpace(' ')
31 vtab = whiteSpace('\v')
32 newline = whiteSpace('\n')
33 formfeed = whiteSpace('\f')
34 indent = whiteSpace('>')
35 unindent = whiteSpace('<')
36 )
37
38
39 type pmode int
40
41 const (
42 noExtraLinebreak pmode = 1 << iota
43 )
44
45 type printer struct {
46
47 Config
48 fset *token.FileSet
49
50
51 output []byte
52 indent int
53 mode pmode
54 impliedSemi bool
55 lastTok token.Token
56 wsbuf []whiteSpace
57
58
59
60
61
62
63
64 pos token.Position
65 out token.Position
66 last token.Position
67
68
69 comments []*ast.CommentGroup
70 cindex int
71 useNodeComments bool
72
73
74 comment *ast.CommentGroup
75 commentOffset int
76 commentNewline bool
77
78
79 nodeSizes map[ast.Node]int
80
81
82 cachedPos token.Pos
83 cachedLine int
84 }
85
86 func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
87 p.Config = *cfg
88 p.fset = fset
89 p.pos = token.Position{Line: 1, Column: 1}
90 p.out = token.Position{Line: 1, Column: 1}
91 p.wsbuf = make([]whiteSpace, 0, 16)
92 p.nodeSizes = nodeSizes
93 p.cachedPos = -1
94 }
95
96
97
98
99 func (p *printer) commentsHaveNewline(list []*ast.Comment) bool {
100
101 line := p.lineFor(list[0].Pos())
102 for i, c := range list {
103 if i > 0 && p.lineFor(list[i].Pos()) != line {
104
105 return true
106 }
107 if t := c.Text; len(t) >= 2 && (t[1] == '/' || strings.Contains(t, "\n")) {
108 return true
109 }
110 }
111 _ = line
112 return false
113 }
114
115 func (p *printer) nextComment() {
116 for p.cindex < len(p.comments) {
117 c := p.comments[p.cindex]
118 p.cindex++
119 if list := c.List; len(list) > 0 {
120 p.comment = c
121 p.commentOffset = p.posFor(list[0].Pos()).Offset
122 p.commentNewline = p.commentsHaveNewline(list)
123 return
124 }
125
126
127 }
128
129 p.commentOffset = infinity
130 }
131
132 func (p *printer) internalError(msg ...interface{}) {
133 if debug {
134 fmt.Print(p.pos.String() + ": ")
135 fmt.Println(msg...)
136 panic("go/printer")
137 }
138 }
139
140 func (p *printer) posFor(pos token.Pos) token.Position {
141
142 return p.fset.Position(pos)
143 }
144
145 func (p *printer) lineFor(pos token.Pos) int {
146 if pos != p.cachedPos {
147 p.cachedPos = pos
148 p.cachedLine = p.fset.Position(pos).Line
149 }
150 return p.cachedLine
151 }
152
153
154 func (p *printer) atLineBegin(pos token.Position) {
155
156 if p.Config.Mode&SourcePos != 0 && pos.IsValid() && (p.out.Line != pos.Line || p.out.Filename != pos.Filename) {
157 p.output = append(p.output, tabwriter.Escape)
158 p.output = append(p.output, fmt.Sprintf("//line %s:%d\n", pos.Filename, pos.Line)...)
159 p.output = append(p.output, tabwriter.Escape)
160
161 p.out.Filename = pos.Filename
162 p.out.Line = pos.Line
163 }
164
165
166
167
168 n := p.Config.Indent + p.indent
169 for i := 0; i < n; i++ {
170 p.output = append(p.output, '\t')
171 }
172
173
174 p.pos.Offset += n
175 p.pos.Column += n
176 p.out.Column += n
177 }
178
179
180 func (p *printer) writeByte(ch byte, n int) {
181 if p.out.Column == 1 {
182 p.atLineBegin(p.pos)
183 }
184
185 for i := 0; i < n; i++ {
186 p.output = append(p.output, ch)
187 }
188
189
190 p.pos.Offset += n
191 if ch == '\n' || ch == '\f' {
192 p.pos.Line += n
193 p.out.Line += n
194 p.pos.Column = 1
195 p.out.Column = 1
196 return
197 }
198 p.pos.Column += n
199 p.out.Column += n
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213 func (p *printer) writeString(pos token.Position, s string, isLit bool) {
214 if p.out.Column == 1 {
215 p.atLineBegin(pos)
216 }
217
218 if pos.IsValid() {
219
220
221
222
223 p.pos = pos
224 }
225
226 if isLit {
227
228
229
230
231 p.output = append(p.output, tabwriter.Escape)
232 }
233
234 if debug {
235 p.output = append(p.output, fmt.Sprintf("/*%s*/", pos)...)
236 }
237 p.output = append(p.output, s...)
238
239
240 nlines := 0
241 var li int
242 for i := 0; i < len(s); i++ {
243
244 if s[i] == '\n' {
245 nlines++
246 li = i
247 }
248 }
249 p.pos.Offset += len(s)
250 if nlines > 0 {
251 p.pos.Line += nlines
252 p.out.Line += nlines
253 c := len(s) - li
254 p.pos.Column = c
255 p.out.Column = c
256 } else {
257 p.pos.Column += len(s)
258 p.out.Column += len(s)
259 }
260
261 if isLit {
262 p.output = append(p.output, tabwriter.Escape)
263 }
264
265 p.last = p.pos
266 }
267
268
269
270
271
272
273
274
275 func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, tok token.Token) {
276 if len(p.output) == 0 {
277
278 return
279 }
280
281 if pos.IsValid() && pos.Filename != p.last.Filename {
282
283 p.writeByte('\f', maxNewlines)
284 return
285 }
286
287 if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') {
288
289
290 hasSep := false
291 if prev == nil {
292
293 j := 0
294 for i, ch := range p.wsbuf {
295 switch ch {
296 case blank:
297
298 p.wsbuf[i] = ignore
299 continue
300 case vtab:
301
302
303 hasSep = true
304 continue
305 case indent:
306
307 continue
308 }
309 j = i
310 break
311 }
312 p.writeWhitespace(j)
313 }
314
315 if !hasSep {
316 sep := byte('\t')
317 if pos.Line == next.Line {
318
319
320
321 sep = ' '
322 }
323 p.writeByte(sep, 1)
324 }
325
326 } else {
327
328
329 droppedLinebreak := false
330 j := 0
331 for i, ch := range p.wsbuf {
332 switch ch {
333 case blank, vtab:
334
335 p.wsbuf[i] = ignore
336 continue
337 case indent:
338
339 continue
340 case unindent:
341
342
343
344
345 if i+1 < len(p.wsbuf) && p.wsbuf[i+1] == unindent {
346 continue
347 }
348
349
350
351
352
353
354 if tok != token.RBRACE && pos.Column == next.Column {
355 continue
356 }
357 case newline, formfeed:
358 p.wsbuf[i] = ignore
359 droppedLinebreak = prev == nil
360 }
361 j = i
362 break
363 }
364 p.writeWhitespace(j)
365
366
367 n := 0
368 if pos.IsValid() && p.last.IsValid() {
369 n = pos.Line - p.last.Line
370 if n < 0 {
371 n = 0
372 }
373 }
374
375
376
377
378
379 if p.indent == 0 && droppedLinebreak {
380 n++
381 }
382
383
384
385 if n == 0 && prev != nil && prev.Text[1] == '/' {
386 n = 1
387 }
388
389 if n > 0 {
390
391
392
393 p.writeByte('\f', nlimit(n))
394 }
395 }
396 }
397
398
399
400
401 func isBlank(s string) bool {
402 for i := 0; i < len(s); i++ {
403 if s[i] > ' ' {
404 return false
405 }
406 }
407 return true
408 }
409
410
411 func commonPrefix(a, b string) string {
412 i := 0
413 for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {
414 i++
415 }
416 return a[0:i]
417 }
418
419
420 func trimRight(s string) string {
421 return strings.TrimRightFunc(s, unicode.IsSpace)
422 }
423
424
425
426
427
428
429
430 func stripCommonPrefix(lines []string) {
431 if len(lines) <= 1 {
432 return
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454 var prefix string
455 if len(lines) > 2 {
456 first := true
457 for i, line := range lines[1 : len(lines)-1] {
458 switch {
459 case isBlank(line):
460 lines[1+i] = ""
461 case first:
462 prefix = commonPrefix(line, line)
463 first = false
464 default:
465 prefix = commonPrefix(prefix, line)
466 }
467 }
468 } else {
469 line := lines[1]
470 prefix = commonPrefix(line, line)
471 }
472
473 474 475
476 lineOfStars := false
477 if i := strings.Index(prefix, "*"); i >= 0 {
478
479 if i > 0 && prefix[i-1] == ' ' {
480 i--
481 }
482 prefix = prefix[0:i]
483 lineOfStars = true
484 } else {
485
486
487
488
489
490
491
492 first := lines[0]
493 if isBlank(first[2:]) {
494
495
496
497
498
499 i := len(prefix)
500 for n := 0; n < 3 && i > 0 && prefix[i-1] == ' '; n++ {
501 i--
502 }
503 if i == len(prefix) && i > 0 && prefix[i-1] == '\t' {
504 i--
505 }
506 prefix = prefix[0:i]
507 } else {
508
509 suffix := make([]byte, len(first))
510 n := 2
511 for n < len(first) && first[n] <= ' ' {
512 suffix[n] = first[n]
513 n++
514 }
515 if n > 2 && suffix[2] == '\t' {
516
517 suffix = suffix[2:n]
518 } else {
519
520 suffix[0], suffix[1] = ' ', ' '
521 suffix = suffix[0:n]
522 }
523
524
525 prefix = strings.TrimSuffix(prefix, string(suffix))
526 }
527 }
528
529
530
531
532 last := lines[len(lines)-1]
533 closing := "*/"
534 i := strings.Index(last, closing)
535 if isBlank(last[0:i]) {
536
537 if lineOfStars {
538 closing = " */"
539 }
540 lines[len(lines)-1] = prefix + closing
541 } else {
542
543
544
545 prefix = commonPrefix(prefix, last)
546 }
547
548
549 for i, line := range lines {
550 if i > 0 && line != "" {
551 lines[i] = line[len(prefix):]
552 }
553 }
554 }
555
556 func (p *printer) writeComment(comment *ast.Comment) {
557 text := comment.Text
558 pos := p.posFor(comment.Pos())
559
560 const linePrefix = "//line "
561 if strings.HasPrefix(text, linePrefix) && (!pos.IsValid() || pos.Column == 1) {
562
563 ldir := strings.TrimSpace(text[len(linePrefix):])
564 if i := strings.LastIndex(ldir, ":"); i >= 0 {
565 if line, err := strconv.Atoi(ldir[i+1:]); err == nil && line > 0 {
566
567
568
569
570 indent := p.indent
571 p.indent = 0
572 defer func() {
573 p.pos.Filename = ldir[:i]
574 p.pos.Line = line
575 p.pos.Column = 1
576 p.indent = indent
577 }()
578 }
579 }
580 }
581
582
583 if text[1] == '/' {
584 p.writeString(pos, trimRight(text), true)
585 return
586 }
587
588
589
590 lines := strings.Split(text, "\n")
591
592
593
594
595
596
597
598 if pos.IsValid() && pos.Column == 1 && p.indent > 0 {
599 for i, line := range lines[1:] {
600 lines[1+i] = " " + line
601 }
602 }
603
604 stripCommonPrefix(lines)
605
606
607
608 for i, line := range lines {
609 if i > 0 {
610 p.writeByte('\f', 1)
611 pos = p.pos
612 }
613 if len(line) > 0 {
614 p.writeString(pos, trimRight(line), true)
615 }
616 }
617 }
618
619
620
621
622
623
624
625
626 func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) {
627 for i, ch := range p.wsbuf {
628 switch ch {
629 case blank, vtab:
630
631 p.wsbuf[i] = ignore
632 case indent, unindent:
633
634 case newline, formfeed:
635
636
637 if needsLinebreak {
638 needsLinebreak = false
639 wroteNewline = true
640 } else {
641 if ch == formfeed {
642 droppedFF = true
643 }
644 p.wsbuf[i] = ignore
645 }
646 }
647 }
648 p.writeWhitespace(len(p.wsbuf))
649
650
651 if needsLinebreak {
652 p.writeByte('\n', 1)
653 wroteNewline = true
654 }
655
656 return
657 }
658
659
660
661
662
663
664
665 func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
666 var last *ast.Comment
667 for p.commentBefore(next) {
668 for _, c := range p.comment.List {
669 p.writeCommentPrefix(p.posFor(c.Pos()), next, last, c, tok)
670 p.writeComment(c)
671 last = c
672 }
673 p.nextComment()
674 }
675
676 if last != nil {
677
678
679
680 if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && tok != token.COMMA &&
681 tok != token.RPAREN && tok != token.RBRACK && tok != token.RBRACE {
682 p.writeByte(' ', 1)
683 }
684
685
686 needsLinebreak :=
687 last.Text[1] == '/' ||
688 tok == token.RBRACE && p.mode&noExtraLinebreak == 0 ||
689 tok == token.EOF
690 return p.writeCommentSuffix(needsLinebreak)
691 }
692
693
694
695 p.internalError("intersperseComments called without pending comments")
696 return
697 }
698
699
700 func (p *printer) writeWhitespace(n int) {
701
702 for i := 0; i < n; i++ {
703 switch ch := p.wsbuf[i]; ch {
704 case ignore:
705
706 case indent:
707 p.indent++
708 case unindent:
709 p.indent--
710 if p.indent < 0 {
711 p.internalError("negative indentation:", p.indent)
712 p.indent = 0
713 }
714 case newline, formfeed:
715
716
717
718
719
720
721 if i+1 < n && p.wsbuf[i+1] == unindent {
722
723
724
725
726
727 p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed
728 i--
729 continue
730 }
731 fallthrough
732 default:
733 p.writeByte(byte(ch), 1)
734 }
735 }
736
737
738 i := 0
739 for ; n < len(p.wsbuf); n++ {
740 p.wsbuf[i] = p.wsbuf[n]
741 i++
742 }
743 p.wsbuf = p.wsbuf[0:i]
744 }
745
746
747
748
749
750 func nlimit(n int) int {
751 if n > maxNewlines {
752 n = maxNewlines
753 }
754 return n
755 }
756
757 func mayCombine(prev token.Token, next byte) (b bool) {
758 switch prev {
759 case token.INT:
760 b = next == '.'
761 case token.ADD:
762 b = next == '+'
763 case token.SUB:
764 b = next == '-'
765 case token.QUO:
766 b = next == '*'
767 case token.LSS:
768 b = next == '-' || next == '<'
769 case token.AND:
770 b = next == '&' || next == '^'
771 }
772 return
773 }
774
775
776
777
778
779
780
781
782
783
784
785
786 func (p *printer) print(args ...interface{}) {
787 for _, arg := range args {
788
789 var data string
790 var isLit bool
791 var impliedSemi bool
792
793 switch x := arg.(type) {
794 case pmode:
795
796 p.mode ^= x
797 continue
798
799 case whiteSpace:
800 if x == ignore {
801
802
803
804 continue
805 }
806 i := len(p.wsbuf)
807 if i == cap(p.wsbuf) {
808
809
810
811 p.writeWhitespace(i)
812 i = 0
813 }
814 p.wsbuf = p.wsbuf[0 : i+1]
815 p.wsbuf[i] = x
816 if x == newline || x == formfeed {
817
818
819
820
821 p.impliedSemi = false
822 }
823 p.lastTok = token.ILLEGAL
824 continue
825
826 case *ast.Ident:
827 data = x.Name
828 impliedSemi = true
829 p.lastTok = token.IDENT
830
831 case *ast.BasicLit:
832 data = x.Value
833 isLit = true
834 impliedSemi = true
835 p.lastTok = x.Kind
836
837 case token.Token:
838 s := x.String()
839 if mayCombine(p.lastTok, s[0]) {
840
841
842
843
844
845
846 if len(p.wsbuf) != 0 {
847 p.internalError("whitespace buffer not empty")
848 }
849 p.wsbuf = p.wsbuf[0:1]
850 p.wsbuf[0] = ' '
851 }
852 data = s
853
854 switch x {
855 case token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN,
856 token.INC, token.DEC, token.RPAREN, token.RBRACK, token.RBRACE:
857 impliedSemi = true
858 }
859 p.lastTok = x
860
861 case token.Pos:
862 if x.IsValid() {
863 p.pos = p.posFor(x)
864 }
865 continue
866
867 case string:
868
869 data = x
870 isLit = true
871 impliedSemi = true
872 p.lastTok = token.STRING
873
874 default:
875 fmt.Fprintf(os.Stderr, "print: unsupported argument %v (%T)\n", arg, arg)
876 panic("go/printer type")
877 }
878
879
880 next := p.pos
881 wroteNewline, droppedFF := p.flush(next, p.lastTok)
882
883
884
885
886 if !p.impliedSemi {
887 n := nlimit(next.Line - p.pos.Line)
888
889 if wroteNewline && n == maxNewlines {
890 n = maxNewlines - 1
891 }
892 if n > 0 {
893 ch := byte('\n')
894 if droppedFF {
895 ch = '\f'
896 }
897 p.writeByte(ch, n)
898 impliedSemi = false
899 }
900 }
901
902 p.writeString(next, data, isLit)
903 p.impliedSemi = impliedSemi
904 }
905 }
906
907
908
909
910
911 func (p *printer) commentBefore(next token.Position) (result bool) {
912 return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline)
913 }
914
915
916
917
918
919
920 func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
921 if p.commentBefore(next) {
922
923 wroteNewline, droppedFF = p.intersperseComments(next, tok)
924 } else {
925
926 p.writeWhitespace(len(p.wsbuf))
927 }
928 return
929 }
930
931
932 func getDoc(n ast.Node) *ast.CommentGroup {
933 switch n := n.(type) {
934 case *ast.Field:
935 return n.Doc
936 case *ast.ImportSpec:
937 return n.Doc
938 case *ast.ValueSpec:
939 return n.Doc
940 case *ast.TypeSpec:
941 return n.Doc
942 case *ast.GenDecl:
943 return n.Doc
944 case *ast.FuncDecl:
945 return n.Doc
946 case *ast.File:
947 return n.Doc
948 }
949 return nil
950 }
951
952 func (p *printer) printNode(node interface{}) error {
953
954 var comments []*ast.CommentGroup
955 if cnode, ok := node.(*CommentedNode); ok {
956 node = cnode.Node
957 comments = cnode.Comments
958 }
959
960 if comments != nil {
961
962 n, ok := node.(ast.Node)
963 if !ok {
964 goto unsupported
965 }
966 beg := n.Pos()
967 end := n.End()
968
969
970
971
972 if doc := getDoc(n); doc != nil {
973 beg = doc.Pos()
974 }
975
976
977 i := 0
978 for i < len(comments) && comments[i].End() < beg {
979 i++
980 }
981 j := i
982 for j < len(comments) && comments[j].Pos() < end {
983 j++
984 }
985 if i < j {
986 p.comments = comments[i:j]
987 }
988 } else if n, ok := node.(*ast.File); ok {
989
990 p.comments = n.Comments
991 }
992
993
994 p.useNodeComments = p.comments == nil
995
996
997 p.nextComment()
998
999
1000 switch n := node.(type) {
1001 case ast.Expr:
1002 p.expr(n)
1003 case ast.Stmt:
1004
1005
1006 if _, ok := n.(*ast.LabeledStmt); ok {
1007 p.indent = 1
1008 }
1009 p.stmt(n, false)
1010 case ast.Decl:
1011 p.decl(n)
1012 case ast.Spec:
1013 p.spec(n, 1, false)
1014 case []ast.Stmt:
1015
1016
1017 for _, s := range n {
1018 if _, ok := s.(*ast.LabeledStmt); ok {
1019 p.indent = 1
1020 }
1021 }
1022 p.stmtList(n, 0, false)
1023 case []ast.Decl:
1024 p.declList(n)
1025 case *ast.File:
1026 p.file(n)
1027 default:
1028 goto unsupported
1029 }
1030
1031 return nil
1032
1033 unsupported:
1034 return fmt.Errorf("go/printer: unsupported node type %T", node)
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046 type trimmer struct {
1047 output io.Writer
1048 state int
1049 space []byte
1050 }
1051
1052
1053
1054 const (
1055 inSpace = iota
1056 inEscape
1057 inText
1058 )
1059
1060 func (p *trimmer) resetSpace() {
1061 p.state = inSpace
1062 p.space = p.space[0:0]
1063 }
1064
1065
1066
1067
1068
1069
1070
1071 var aNewline = []byte("\n")
1072
1073 func (p *trimmer) Write(data []byte) (n int, err error) {
1074
1075
1076
1077
1078
1079 m := 0
1080 var b byte
1081 for n, b = range data {
1082 if b == '\v' {
1083 b = '\t'
1084 }
1085 switch p.state {
1086 case inSpace:
1087 switch b {
1088 case '\t', ' ':
1089 p.space = append(p.space, b)
1090 case '\n', '\f':
1091 p.resetSpace()
1092 _, err = p.output.Write(aNewline)
1093 case tabwriter.Escape:
1094 _, err = p.output.Write(p.space)
1095 p.state = inEscape
1096 m = n + 1
1097 default:
1098 _, err = p.output.Write(p.space)
1099 p.state = inText
1100 m = n
1101 }
1102 case inEscape:
1103 if b == tabwriter.Escape {
1104 _, err = p.output.Write(data[m:n])
1105 p.resetSpace()
1106 }
1107 case inText:
1108 switch b {
1109 case '\t', ' ':
1110 _, err = p.output.Write(data[m:n])
1111 p.resetSpace()
1112 p.space = append(p.space, b)
1113 case '\n', '\f':
1114 _, err = p.output.Write(data[m:n])
1115 p.resetSpace()
1116 _, err = p.output.Write(aNewline)
1117 case tabwriter.Escape:
1118 _, err = p.output.Write(data[m:n])
1119 p.state = inEscape
1120 m = n + 1
1121 }
1122 default:
1123 panic("unreachable")
1124 }
1125 if err != nil {
1126 return
1127 }
1128 }
1129 n = len(data)
1130
1131 switch p.state {
1132 case inEscape, inText:
1133 _, err = p.output.Write(data[m:n])
1134 p.resetSpace()
1135 }
1136
1137 return
1138 }
1139
1140
1141
1142
1143
1144 type Mode uint
1145
1146 const (
1147 RawFormat Mode = 1 << iota
1148 TabIndent
1149 UseSpaces
1150 SourcePos
1151 )
1152
1153
1154 type Config struct {
1155 Mode Mode
1156 Tabwidth int
1157 Indent int
1158 }
1159
1160
1161 func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (err error) {
1162
1163 var p printer
1164 p.init(cfg, fset, nodeSizes)
1165 if err = p.printNode(node); err != nil {
1166 return
1167 }
1168
1169 p.impliedSemi = false
1170 p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF)
1171
1172
1173
1174
1175
1176 output = &trimmer{output: output}
1177
1178
1179 if cfg.Mode&RawFormat == 0 {
1180 minwidth := cfg.Tabwidth
1181
1182 padchar := byte('\t')
1183 if cfg.Mode&UseSpaces != 0 {
1184 padchar = ' '
1185 }
1186
1187 twmode := tabwriter.DiscardEmptyColumns
1188 if cfg.Mode&TabIndent != 0 {
1189 minwidth = 0
1190 twmode |= tabwriter.TabIndent
1191 }
1192
1193 output = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode)
1194 }
1195
1196
1197 if _, err = output.Write(p.output); err != nil {
1198 return
1199 }
1200
1201
1202 if tw, _ := output.(*tabwriter.Writer); tw != nil {
1203 err = tw.Flush()
1204 }
1205
1206 return
1207 }
1208
1209
1210
1211
1212 type CommentedNode struct {
1213 Node interface{}
1214 Comments []*ast.CommentGroup
1215 }
1216
1217
1218
1219
1220
1221
1222 func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
1223 return cfg.fprint(output, fset, node, make(map[ast.Node]int))
1224 }
1225
1226
1227
1228
1229 func Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
1230 return (&Config{Tabwidth: 8}).Fprint(output, fset, node)
1231 }
View as plain text