OLD | NEW |
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // The printer package implements printing of AST nodes. | 5 // The printer package implements printing of AST nodes. |
6 package printer | 6 package printer |
7 | 7 |
8 import ( | 8 import ( |
9 "bytes" | 9 "bytes" |
10 "fmt" | 10 "fmt" |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 | 55 |
56 | 56 |
57 // Use ignoreMultiLine if the multiLine information is not important. | 57 // Use ignoreMultiLine if the multiLine information is not important. |
58 var ignoreMultiLine = new(bool) | 58 var ignoreMultiLine = new(bool) |
59 | 59 |
60 | 60 |
61 type printer struct { | 61 type printer struct { |
62 // Configuration (does not change after initialization) | 62 // Configuration (does not change after initialization) |
63 output io.Writer | 63 output io.Writer |
64 Config | 64 Config |
| 65 fset *token.FileSet |
65 errors chan os.Error | 66 errors chan os.Error |
66 | 67 |
67 // Current state | 68 // Current state |
68 nesting int // nesting level (0: top-level (package scope), >0:
functions/decls.) | 69 nesting int // nesting level (0: top-level (package scope), >0:
functions/decls.) |
69 written int // number of bytes written | 70 written int // number of bytes written |
70 indent int // current indentation | 71 indent int // current indentation |
71 escape bool // true if in escape sequence | 72 escape bool // true if in escape sequence |
72 lastTok token.Token // the last token printed (token.ILLEGAL if it's whi
tespace) | 73 lastTok token.Token // the last token printed (token.ILLEGAL if it's whi
tespace) |
73 | 74 |
74 // Buffered whitespace | 75 // Buffered whitespace |
(...skipping 12 matching lines...) Expand all Loading... |
87 // HTML support | 88 // HTML support |
88 lastTaggedLine int // last line for which a line tag was written | 89 lastTaggedLine int // last line for which a line tag was written |
89 | 90 |
90 // The list of all source comments, in order of appearance. | 91 // The list of all source comments, in order of appearance. |
91 comments []*ast.CommentGroup // may be nil | 92 comments []*ast.CommentGroup // may be nil |
92 cindex int // current comment index | 93 cindex int // current comment index |
93 useNodeComments bool // if not set, ignore lead and line
comments of nodes | 94 useNodeComments bool // if not set, ignore lead and line
comments of nodes |
94 } | 95 } |
95 | 96 |
96 | 97 |
97 func (p *printer) init(output io.Writer, cfg *Config) { | 98 func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet) { |
98 p.output = output | 99 p.output = output |
99 p.Config = *cfg | 100 p.Config = *cfg |
| 101 p.fset = fset |
100 p.errors = make(chan os.Error) | 102 p.errors = make(chan os.Error) |
101 p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short | 103 p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short |
102 } | 104 } |
103 | 105 |
104 | 106 |
105 func (p *printer) internalError(msg ...interface{}) { | 107 func (p *printer) internalError(msg ...interface{}) { |
106 if debug { | 108 if debug { |
107 fmt.Print(p.pos.String() + ": ") | 109 fmt.Print(p.pos.String() + ": ") |
108 fmt.Println(msg...) | 110 fmt.Println(msg...) |
109 panic("go/printer") | 111 panic("go/printer") |
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 } | 591 } |
590 } | 592 } |
591 } | 593 } |
592 | 594 |
593 | 595 |
594 func (p *printer) writeComment(comment *ast.Comment) { | 596 func (p *printer) writeComment(comment *ast.Comment) { |
595 text := comment.Text | 597 text := comment.Text |
596 | 598 |
597 // shortcut common case of //-style comments | 599 // shortcut common case of //-style comments |
598 if text[1] == '/' { | 600 if text[1] == '/' { |
599 » » p.writeCommentLine(comment, comment.Pos(), text) | 601 » » p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text
) |
600 return | 602 return |
601 } | 603 } |
602 | 604 |
603 // for /*-style comments, print line by line and let the | 605 // for /*-style comments, print line by line and let the |
604 // write function take care of the proper indentation | 606 // write function take care of the proper indentation |
605 lines := split(text) | 607 lines := split(text) |
606 stripCommonPrefix(lines) | 608 stripCommonPrefix(lines) |
607 | 609 |
608 // write comment lines, separated by formfeed, | 610 // write comment lines, separated by formfeed, |
609 // without a line break after the last line | 611 // without a line break after the last line |
610 linebreak := formfeeds[0:1] | 612 linebreak := formfeeds[0:1] |
611 » pos := comment.Pos() | 613 » pos := p.fset.Position(comment.Pos()) |
612 for i, line := range lines { | 614 for i, line := range lines { |
613 if i > 0 { | 615 if i > 0 { |
614 p.write(linebreak) | 616 p.write(linebreak) |
615 pos = p.pos | 617 pos = p.pos |
616 } | 618 } |
617 if len(line) > 0 { | 619 if len(line) > 0 { |
618 p.writeCommentLine(comment, pos, line) | 620 p.writeCommentLine(comment, pos, line) |
619 } | 621 } |
620 } | 622 } |
621 } | 623 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
662 // intersperseComments consumes all comments that appear before the next token | 664 // intersperseComments consumes all comments that appear before the next token |
663 // tok and prints it together with the buffered whitespace (i.e., the whitespace | 665 // tok and prints it together with the buffered whitespace (i.e., the whitespace |
664 // that needs to be written before the next token). A heuristic is used to mix | 666 // that needs to be written before the next token). A heuristic is used to mix |
665 // the comments and whitespace. intersperseComments returns true if a pending | 667 // the comments and whitespace. intersperseComments returns true if a pending |
666 // formfeed was dropped from the whitespace buffer. | 668 // formfeed was dropped from the whitespace buffer. |
667 // | 669 // |
668 func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro
ppedFF bool) { | 670 func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro
ppedFF bool) { |
669 var last *ast.Comment | 671 var last *ast.Comment |
670 for ; p.commentBefore(next); p.cindex++ { | 672 for ; p.commentBefore(next); p.cindex++ { |
671 for _, c := range p.comments[p.cindex].List { | 673 for _, c := range p.comments[p.cindex].List { |
672 » » » p.writeCommentPrefix(c.Pos(), next, last == nil, tok.IsK
eyword()) | 674 » » » p.writeCommentPrefix(p.fset.Position(c.Pos()), next, las
t == nil, tok.IsKeyword()) |
673 p.writeComment(c) | 675 p.writeComment(c) |
674 last = c | 676 last = c |
675 } | 677 } |
676 } | 678 } |
677 | 679 |
678 if last != nil { | 680 if last != nil { |
679 » » if last.Text[1] == '*' && last.Pos().Line == next.Line { | 681 » » if last.Text[1] == '*' && p.fset.Position(last.Pos()).Line == ne
xt.Line { |
680 // the last comment is a /*-style comment and the next i
tem | 682 // the last comment is a /*-style comment and the next i
tem |
681 // follows on the same line: separate with an extra blan
k | 683 // follows on the same line: separate with an extra blan
k |
682 p.write([]byte{' '}) | 684 p.write([]byte{' '}) |
683 } | 685 } |
684 // ensure that there is a newline after a //-style comment | 686 // ensure that there is a newline after a //-style comment |
685 // or if we are before a closing '}' or at the end of a file | 687 // or if we are before a closing '}' or at the end of a file |
686 return p.writeCommentSuffix(last.Text[1] == '/' || tok == token.
RBRACE || tok == token.EOF) | 688 return p.writeCommentSuffix(last.Text[1] == '/' || tok == token.
RBRACE || tok == token.EOF) |
687 } | 689 } |
688 | 690 |
689 // no comment was written - we should never reach here since | 691 // no comment was written - we should never reach here since |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
835 } | 837 } |
836 p.buffer = p.buffer[0:1] | 838 p.buffer = p.buffer[0:1] |
837 p.buffer[0] = ' ' | 839 p.buffer[0] = ' ' |
838 } | 840 } |
839 if p.Styler != nil { | 841 if p.Styler != nil { |
840 data, tag = p.Styler.Token(x) | 842 data, tag = p.Styler.Token(x) |
841 } else { | 843 } else { |
842 data = []byte(s) | 844 data = []byte(s) |
843 } | 845 } |
844 tok = x | 846 tok = x |
845 » » case token.Position: | 847 » » case token.Pos: |
846 if x.IsValid() { | 848 if x.IsValid() { |
847 » » » » next = x // accurate position of next item | 849 » » » » next = p.fset.Position(x) // accurate position o
f next item |
848 } | 850 } |
849 tok = p.lastTok | 851 tok = p.lastTok |
850 default: | 852 default: |
851 fmt.Fprintf(os.Stderr, "print: unsupported argument type
%T\n", f) | 853 fmt.Fprintf(os.Stderr, "print: unsupported argument type
%T\n", f) |
852 panic("go/printer type") | 854 panic("go/printer type") |
853 } | 855 } |
854 p.lastTok = tok | 856 p.lastTok = tok |
855 p.pos = next | 857 p.pos = next |
856 | 858 |
857 if data != nil { | 859 if data != nil { |
858 droppedFF := p.flush(next, tok) | 860 droppedFF := p.flush(next, tok) |
859 | 861 |
860 // intersperse extra newlines if present in the source | 862 // intersperse extra newlines if present in the source |
861 // (don't do this in flush as it will cause extra newlin
es | 863 // (don't do this in flush as it will cause extra newlin
es |
862 // at the end of a file) - use formfeeds if we dropped o
ne | 864 // at the end of a file) - use formfeeds if we dropped o
ne |
863 // before | 865 // before |
864 p.writeNewlines(next.Line-p.pos.Line, droppedFF) | 866 p.writeNewlines(next.Line-p.pos.Line, droppedFF) |
865 | 867 |
866 p.writeItem(next, data, tag) | 868 p.writeItem(next, data, tag) |
867 } | 869 } |
868 } | 870 } |
869 } | 871 } |
870 | 872 |
871 | 873 |
872 // commentBefore returns true iff the current comment occurs | 874 // commentBefore returns true iff the current comment occurs |
873 // before the next position in the source code. | 875 // before the next position in the source code. |
874 // | 876 // |
875 func (p *printer) commentBefore(next token.Position) bool { | 877 func (p *printer) commentBefore(next token.Position) bool { |
876 » return p.cindex < len(p.comments) && p.comments[p.cindex].List[0].Pos().
Offset < next.Offset | 878 » return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex
].List[0].Pos()).Offset < next.Offset |
877 } | 879 } |
878 | 880 |
879 | 881 |
880 // Flush prints any pending comments and whitespace occuring | 882 // Flush prints any pending comments and whitespace occuring |
881 // textually before the position of the next token tok. Flush | 883 // textually before the position of the next token tok. Flush |
882 // returns true if a pending formfeed character was dropped | 884 // returns true if a pending formfeed character was dropped |
883 // from the whitespace buffer as a result of interspersing | 885 // from the whitespace buffer as a result of interspersing |
884 // comments. | 886 // comments. |
885 // | 887 // |
886 func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { | 888 func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1019 // A Config node controls the output of Fprint. | 1021 // A Config node controls the output of Fprint. |
1020 type Config struct { | 1022 type Config struct { |
1021 Mode uint // default: 0 | 1023 Mode uint // default: 0 |
1022 Tabwidth int // default: 8 | 1024 Tabwidth int // default: 8 |
1023 Styler Styler // default: nil | 1025 Styler Styler // default: nil |
1024 } | 1026 } |
1025 | 1027 |
1026 | 1028 |
1027 // Fprint "pretty-prints" an AST node to output and returns the number | 1029 // Fprint "pretty-prints" an AST node to output and returns the number |
1028 // of bytes written and an error (if any) for a given configuration cfg. | 1030 // of bytes written and an error (if any) for a given configuration cfg. |
| 1031 // Position information is interpreted relative to the file set fset. |
1029 // The node type must be *ast.File, or assignment-compatible to ast.Expr, | 1032 // The node type must be *ast.File, or assignment-compatible to ast.Expr, |
1030 // ast.Decl, ast.Spec, or ast.Stmt. | 1033 // ast.Decl, ast.Spec, or ast.Stmt. |
1031 // | 1034 // |
1032 func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) { | 1035 func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
}) (int, os.Error) { |
1033 // redirect output through a trimmer to eliminate trailing whitespace | 1036 // redirect output through a trimmer to eliminate trailing whitespace |
1034 // (Input to a tabwriter must be untrimmed since trailing tabs provide | 1037 // (Input to a tabwriter must be untrimmed since trailing tabs provide |
1035 // formatting information. The tabwriter could provide trimming | 1038 // formatting information. The tabwriter could provide trimming |
1036 // functionality but no tabwriter is used when RawFormat is set.) | 1039 // functionality but no tabwriter is used when RawFormat is set.) |
1037 output = &trimmer{output: output} | 1040 output = &trimmer{output: output} |
1038 | 1041 |
1039 // setup tabwriter if needed and redirect output | 1042 // setup tabwriter if needed and redirect output |
1040 var tw *tabwriter.Writer | 1043 var tw *tabwriter.Writer |
1041 if cfg.Mode&RawFormat == 0 { | 1044 if cfg.Mode&RawFormat == 0 { |
1042 minwidth := cfg.Tabwidth | 1045 minwidth := cfg.Tabwidth |
(...skipping 11 matching lines...) Expand all Loading... |
1054 minwidth = 0 | 1057 minwidth = 0 |
1055 twmode |= tabwriter.TabIndent | 1058 twmode |= tabwriter.TabIndent |
1056 } | 1059 } |
1057 | 1060 |
1058 tw = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padc
har, twmode) | 1061 tw = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padc
har, twmode) |
1059 output = tw | 1062 output = tw |
1060 } | 1063 } |
1061 | 1064 |
1062 // setup printer and print node | 1065 // setup printer and print node |
1063 var p printer | 1066 var p printer |
1064 » p.init(output, cfg) | 1067 » p.init(output, cfg, fset) |
1065 go func() { | 1068 go func() { |
1066 switch n := node.(type) { | 1069 switch n := node.(type) { |
1067 case ast.Expr: | 1070 case ast.Expr: |
1068 p.nesting = 1 | 1071 p.nesting = 1 |
1069 p.useNodeComments = true | 1072 p.useNodeComments = true |
1070 p.expr(n, ignoreMultiLine) | 1073 p.expr(n, ignoreMultiLine) |
1071 case ast.Stmt: | 1074 case ast.Stmt: |
1072 p.nesting = 1 | 1075 p.nesting = 1 |
1073 p.useNodeComments = true | 1076 p.useNodeComments = true |
1074 // A labeled statement will un-indent to position the | 1077 // A labeled statement will un-indent to position the |
(...skipping 29 matching lines...) Expand all Loading... |
1104 tw.Flush() // ignore errors | 1107 tw.Flush() // ignore errors |
1105 } | 1108 } |
1106 | 1109 |
1107 return p.written, err | 1110 return p.written, err |
1108 } | 1111 } |
1109 | 1112 |
1110 | 1113 |
1111 // Fprint "pretty-prints" an AST node to output. | 1114 // Fprint "pretty-prints" an AST node to output. |
1112 // It calls Config.Fprint with default settings. | 1115 // It calls Config.Fprint with default settings. |
1113 // | 1116 // |
1114 func Fprint(output io.Writer, node interface{}) os.Error { | 1117 func Fprint(output io.Writer, fset *token.FileSet, node interface{}) os.Error { |
1115 » _, err := (&Config{Tabwidth: 8}).Fprint(output, node) // don't care abou
t number of bytes written | 1118 » _, err := (&Config{Tabwidth: 8}).Fprint(output, fset, node) // don't car
e about number of bytes written |
1116 return err | 1119 return err |
1117 } | 1120 } |
OLD | NEW |