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