Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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" |
11 "go/ast" | 11 "go/ast" |
12 "go/token" | 12 "go/token" |
13 "io" | 13 "io" |
14 "os" | 14 "os" |
15 "path/filepath" | 15 "path/filepath" |
16 "runtime" | 16 "runtime" |
17 "strings" | |
17 "tabwriter" | 18 "tabwriter" |
18 ) | 19 ) |
19 | 20 |
20 | 21 |
21 const debug = false // enable for debugging | 22 const debug = false // enable for debugging |
22 | 23 |
23 | 24 |
24 type whiteSpace int | 25 type whiteSpace int |
25 | 26 |
26 const ( | 27 const ( |
27 ignore = whiteSpace(0) | 28 ignore = whiteSpace(0) |
28 blank = whiteSpace(' ') | 29 blank = whiteSpace(' ') |
29 vtab = whiteSpace('\v') | 30 vtab = whiteSpace('\v') |
30 newline = whiteSpace('\n') | 31 newline = whiteSpace('\n') |
31 formfeed = whiteSpace('\f') | 32 formfeed = whiteSpace('\f') |
32 indent = whiteSpace('>') | 33 indent = whiteSpace('>') |
33 unindent = whiteSpace('<') | 34 unindent = whiteSpace('<') |
34 ) | 35 ) |
35 | 36 |
36 | 37 |
37 const ( | 38 const ( |
38 » esc2 = '\xfe' // an escape byte that cannot occur in regular UTF-8 | 39 » esc2 = '\xfe' // an escape byte that cannot oc cur in regular UTF-8 |
39 » _ = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape | 40 » _ = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape |
41 » esc2str = "\xfe" | |
40 ) | 42 ) |
41 | 43 |
42 | 44 |
43 var ( | 45 var ( |
44 esc = []byte{tabwriter.Escape} | 46 esc = []byte{tabwriter.Escape} |
45 htab = []byte{'\t'} | 47 htab = []byte{'\t'} |
46 htabs = []byte("\t\t\t\t\t\t\t\t") | 48 htabs = []byte("\t\t\t\t\t\t\t\t") |
47 newlines = []byte("\n\n\n\n\n\n\n\n") // more than the max determined b y nlines | 49 newlines = []byte("\n\n\n\n\n\n\n\n") // more than the max determined b y nlines |
48 formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than the max determined b y nlines | 50 formfeeds = []byte("\f\f\f\f\f\f\f\f") // more than the max determined b y nlines |
49 ) | 51 ) |
(...skipping 24 matching lines...) Expand all Loading... | |
74 fset *token.FileSet | 76 fset *token.FileSet |
75 errors chan os.Error | 77 errors chan os.Error |
76 | 78 |
77 // Current state | 79 // Current state |
78 nesting int // nesting level (0: top-level (package scope), >0: functions/decls.) | 80 nesting int // nesting level (0: top-level (package scope), >0: functions/decls.) |
79 written int // number of bytes written | 81 written int // number of bytes written |
80 indent int // current indentation | 82 indent int // current indentation |
81 mode pmode // current printer mode | 83 mode pmode // current printer mode |
82 lastTok token.Token // the last token printed (token.ILLEGAL if it's whi tespace) | 84 lastTok token.Token // the last token printed (token.ILLEGAL if it's whi tespace) |
83 | 85 |
84 » // Buffered whitespace | 86 » // Reused buffers |
85 » buffer []whiteSpace | 87 » wsbuf []whiteSpace // delayed white space |
88 » litbuf bytes.Buffer // for creation of escaped literals and comments | |
86 | 89 |
87 // The (possibly estimated) position in the generated output; | 90 // The (possibly estimated) position in the generated output; |
88 // in AST space (i.e., pos is set whenever a token position is | 91 // in AST space (i.e., pos is set whenever a token position is |
89 // known accurately, and updated dependending on what has been | 92 // known accurately, and updated dependending on what has been |
90 // written). | 93 // written). |
91 pos token.Position | 94 pos token.Position |
92 | 95 |
93 // The value of pos immediately after the last item has been | 96 // The value of pos immediately after the last item has been |
94 // written using writeItem. | 97 // written using writeItem. |
95 last token.Position | 98 last token.Position |
96 | 99 |
97 // The list of all source comments, in order of appearance. | 100 // The list of all source comments, in order of appearance. |
98 comments []*ast.CommentGroup // may be nil | 101 comments []*ast.CommentGroup // may be nil |
99 cindex int // current comment index | 102 cindex int // current comment index |
100 useNodeComments bool // if not set, ignore lead and line comments of nodes | 103 useNodeComments bool // if not set, ignore lead and line comments of nodes |
101 | 104 |
102 // Cache of already computed node sizes. | 105 // Cache of already computed node sizes. |
103 nodeSizes map[ast.Node]int | 106 nodeSizes map[ast.Node]int |
104 } | 107 } |
105 | 108 |
106 | 109 |
107 func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeS izes map[ast.Node]int) { | 110 func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeS izes map[ast.Node]int) { |
108 p.output = output | 111 p.output = output |
109 p.Config = *cfg | 112 p.Config = *cfg |
110 p.fset = fset | 113 p.fset = fset |
111 p.errors = make(chan os.Error) | 114 p.errors = make(chan os.Error) |
112 » p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short | 115 » p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short |
113 p.nodeSizes = nodeSizes | 116 p.nodeSizes = nodeSizes |
114 } | 117 } |
115 | 118 |
116 | 119 |
117 func (p *printer) internalError(msg ...interface{}) { | 120 func (p *printer) internalError(msg ...interface{}) { |
118 if debug { | 121 if debug { |
119 fmt.Print(p.pos.String() + ": ") | 122 fmt.Print(p.pos.String() + ": ") |
120 fmt.Println(msg...) | 123 fmt.Println(msg...) |
121 panic("go/printer") | 124 panic("go/printer") |
122 } | 125 } |
126 } | |
127 | |
128 | |
129 // escape escapes string s by bracketing it with tabwriter.Escape. | |
130 // Escapes strings pass through tabwriter unchanged. (Note that | |
rsc
2011/03/29 03:52:20
s/Escapes/Escaped/
gri
2011/03/29 04:44:34
Done.
| |
131 // valid Go programs cannot contain tabwriter.Escape bytes since | |
132 // they do not appear in legal UTF-8 sequences). | |
133 // | |
134 func (p *printer) escape(s string) string { | |
135 p.litbuf.Reset() | |
136 p.litbuf.WriteByte(tabwriter.Escape) | |
137 p.litbuf.WriteString(s) | |
138 p.litbuf.WriteByte(tabwriter.Escape) | |
139 return p.litbuf.String() | |
rsc
2011/03/29 03:52:20
I haven't examined this in detail, but it appears
gri
2011/03/29 04:44:34
Correct. I want to push the strings a bit further
| |
123 } | 140 } |
124 | 141 |
125 | 142 |
126 // nlines returns the adjusted number of linebreaks given the desired number | 143 // nlines returns the adjusted number of linebreaks given the desired number |
127 // of breaks n such that min <= result <= max where max depends on the current | 144 // of breaks n such that min <= result <= max where max depends on the current |
128 // nesting level. | 145 // nesting level. |
129 // | 146 // |
130 func (p *printer) nlines(n, min int) int { | 147 func (p *printer) nlines(n, min int) int { |
131 if n < min { | 148 if n < min { |
132 return min | 149 return min |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
223 } | 240 } |
224 } | 241 } |
225 | 242 |
226 | 243 |
227 // writeItem writes data at position pos. data is the text corresponding to | 244 // writeItem writes data at position pos. data is the text corresponding to |
228 // a single lexical token, but may also be comment text. pos is the actual | 245 // a single lexical token, but may also be comment text. pos is the actual |
229 // (or at least very accurately estimated) position of the data in the original | 246 // (or at least very accurately estimated) position of the data in the original |
230 // source text. writeItem updates p.last to the position immediately following | 247 // source text. writeItem updates p.last to the position immediately following |
231 // the data. | 248 // the data. |
232 // | 249 // |
233 func (p *printer) writeItem(pos token.Position, data []byte) { | 250 func (p *printer) writeItem(pos token.Position, data string) { |
234 if pos.IsValid() { | 251 if pos.IsValid() { |
235 // continue with previous position if we don't have a valid pos | 252 // continue with previous position if we don't have a valid pos |
236 if p.last.IsValid() && p.last.Filename != pos.Filename { | 253 if p.last.IsValid() && p.last.Filename != pos.Filename { |
237 // the file has changed - reset state | 254 // the file has changed - reset state |
238 // (used when printing merged ASTs of different files | 255 // (used when printing merged ASTs of different files |
239 // e.g., the result of ast.MergePackageFiles) | 256 // e.g., the result of ast.MergePackageFiles) |
240 p.indent = 0 | 257 p.indent = 0 |
241 p.mode = 0 | 258 p.mode = 0 |
242 » » » p.buffer = p.buffer[0:0] | 259 » » » p.wsbuf = p.wsbuf[0:0] |
243 } | 260 } |
244 p.pos = pos | 261 p.pos = pos |
245 } | 262 } |
246 if debug { | 263 if debug { |
247 // do not update p.pos - use write0 | 264 // do not update p.pos - use write0 |
248 _, filename := filepath.Split(pos.Filename) | 265 _, filename := filepath.Split(pos.Filename) |
249 p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, po s.Column))) | 266 p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, po s.Column))) |
250 } | 267 } |
251 » p.write(data) | 268 » p.write([]byte(data)) |
252 p.last = p.pos | 269 p.last = p.pos |
253 } | 270 } |
254 | 271 |
255 | 272 |
256 // writeCommentPrefix writes the whitespace before a comment. | 273 // writeCommentPrefix writes the whitespace before a comment. |
257 // If there is any pending whitespace, it consumes as much of | 274 // If there is any pending whitespace, it consumes as much of |
258 // it as is likely to help position the comment nicely. | 275 // it as is likely to help position the comment nicely. |
259 // pos is the comment position, next the position of the item | 276 // pos is the comment position, next the position of the item |
260 // after all pending comments, prev is the previous comment in | 277 // after all pending comments, prev is the previous comment in |
261 // a group of comments (or nil), and isKeyword indicates if the | 278 // a group of comments (or nil), and isKeyword indicates if the |
(...skipping 11 matching lines...) Expand all Loading... | |
273 return | 290 return |
274 } | 291 } |
275 | 292 |
276 if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') { | 293 if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') { |
277 // comment on the same line as last item: | 294 // comment on the same line as last item: |
278 // separate with at least one separator | 295 // separate with at least one separator |
279 hasSep := false | 296 hasSep := false |
280 if prev == nil { | 297 if prev == nil { |
281 // first comment of a comment group | 298 // first comment of a comment group |
282 j := 0 | 299 j := 0 |
283 » » » for i, ch := range p.buffer { | 300 » » » for i, ch := range p.wsbuf { |
284 switch ch { | 301 switch ch { |
285 case blank: | 302 case blank: |
286 // ignore any blanks before a comment | 303 // ignore any blanks before a comment |
287 » » » » » p.buffer[i] = ignore | 304 » » » » » p.wsbuf[i] = ignore |
288 continue | 305 continue |
289 case vtab: | 306 case vtab: |
290 // respect existing tabs - important | 307 // respect existing tabs - important |
291 // for proper formatting of commented st ructs | 308 // for proper formatting of commented st ructs |
292 hasSep = true | 309 hasSep = true |
293 continue | 310 continue |
294 case indent: | 311 case indent: |
295 // apply pending indentation | 312 // apply pending indentation |
296 continue | 313 continue |
297 } | 314 } |
(...skipping 13 matching lines...) Expand all Loading... | |
311 p.write(htab) | 328 p.write(htab) |
312 } | 329 } |
313 } | 330 } |
314 | 331 |
315 } else { | 332 } else { |
316 // comment on a different line: | 333 // comment on a different line: |
317 // separate with at least one line break | 334 // separate with at least one line break |
318 if prev == nil { | 335 if prev == nil { |
319 // first comment of a comment group | 336 // first comment of a comment group |
320 j := 0 | 337 j := 0 |
321 » » » for i, ch := range p.buffer { | 338 » » » for i, ch := range p.wsbuf { |
322 switch ch { | 339 switch ch { |
323 case blank, vtab: | 340 case blank, vtab: |
324 // ignore any horizontal whitespace befo re line breaks | 341 // ignore any horizontal whitespace befo re line breaks |
325 » » » » » p.buffer[i] = ignore | 342 » » » » » p.wsbuf[i] = ignore |
326 continue | 343 continue |
327 case indent: | 344 case indent: |
328 // apply pending indentation | 345 // apply pending indentation |
329 continue | 346 continue |
330 case unindent: | 347 case unindent: |
331 // if the next token is a keyword, apply the outdent | 348 // if the next token is a keyword, apply the outdent |
332 // if it appears that the comment is ali gned with the | 349 // if it appears that the comment is ali gned with the |
333 // keyword; otherwise assume the outdent is part of a | 350 // keyword; otherwise assume the outdent is part of a |
334 // closing block and stop (this scenario appears with | 351 // closing block and stop (this scenario appears with |
335 // comments before a case label where th e comments | 352 // comments before a case label where th e comments |
336 // apply to the next case instead of the current one) | 353 // apply to the next case instead of the current one) |
337 if isKeyword && pos.Column == next.Colum n { | 354 if isKeyword && pos.Column == next.Colum n { |
338 continue | 355 continue |
339 } | 356 } |
340 case newline, formfeed: | 357 case newline, formfeed: |
341 // TODO(gri): may want to keep formfeed info in some cases | 358 // TODO(gri): may want to keep formfeed info in some cases |
342 » » » » » p.buffer[i] = ignore | 359 » » » » » p.wsbuf[i] = ignore |
343 } | 360 } |
344 j = i | 361 j = i |
345 break | 362 break |
346 } | 363 } |
347 p.writeWhitespace(j) | 364 p.writeWhitespace(j) |
348 } | 365 } |
349 // use formfeeds to break columns before a comment; | 366 // use formfeeds to break columns before a comment; |
350 // this is analogous to using formfeeds to separate | 367 // this is analogous to using formfeeds to separate |
351 // individual lines of /*-style comments - but make | 368 // individual lines of /*-style comments - but make |
352 // sure there is at least one line break if the previous | 369 // sure there is at least one line break if the previous |
353 // comment was a line comment | 370 // comment was a line comment |
354 n := pos.Line - p.last.Line // if !pos.IsValid(), pos.Line == 0, and n will be 0 | 371 n := pos.Line - p.last.Line // if !pos.IsValid(), pos.Line == 0, and n will be 0 |
355 if n <= 0 && prev != nil && prev.Text[1] == '/' { | 372 if n <= 0 && prev != nil && prev.Text[1] == '/' { |
356 n = 1 | 373 n = 1 |
357 } | 374 } |
358 p.writeNewlines(n, true) | 375 p.writeNewlines(n, true) |
359 } | 376 } |
360 } | 377 } |
361 | 378 |
362 | 379 |
363 func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, lin e []byte) { | 380 // TODO(gri): It should be possible to convert the code below from using |
364 » // line must pass through unchanged, bracket it with tabwriter.Escape | 381 // []byte to string and in the process eliminate some conversions. |
365 » line = bytes.Join([][]byte{esc, line, esc}, nil) | |
366 » p.writeItem(pos, line) | |
367 } | |
368 | |
369 | 382 |
370 // Split comment text into lines | 383 // Split comment text into lines |
371 func split(text []byte) [][]byte { | 384 func split(text []byte) [][]byte { |
372 // count lines (comment text never ends in a newline) | 385 // count lines (comment text never ends in a newline) |
373 n := 1 | 386 n := 1 |
374 for _, c := range text { | 387 for _, c := range text { |
375 if c == '\n' { | 388 if c == '\n' { |
376 n++ | 389 n++ |
377 } | 390 } |
378 } | 391 } |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
535 // Remove the common prefix from all but the first and empty lines. | 548 // Remove the common prefix from all but the first and empty lines. |
536 for i, line := range lines[1:] { | 549 for i, line := range lines[1:] { |
537 if len(line) != 0 { | 550 if len(line) != 0 { |
538 lines[1+i] = line[len(prefix):] // range starts at line 1 | 551 lines[1+i] = line[len(prefix):] // range starts at line 1 |
539 } | 552 } |
540 } | 553 } |
541 } | 554 } |
542 | 555 |
543 | 556 |
544 func (p *printer) writeComment(comment *ast.Comment) { | 557 func (p *printer) writeComment(comment *ast.Comment) { |
545 » text := []byte(comment.Text) | 558 » text := comment.Text |
546 | 559 |
547 // shortcut common case of //-style comments | 560 // shortcut common case of //-style comments |
548 if text[1] == '/' { | 561 if text[1] == '/' { |
549 » » p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text ) | 562 » » p.writeItem(p.fset.Position(comment.Pos()), p.escape(text)) |
550 return | 563 return |
551 } | 564 } |
552 | 565 |
553 // for /*-style comments, print line by line and let the | 566 // for /*-style comments, print line by line and let the |
554 // write function take care of the proper indentation | 567 // write function take care of the proper indentation |
555 » lines := split(text) | 568 » lines := split([]byte(text)) |
556 stripCommonPrefix(lines) | 569 stripCommonPrefix(lines) |
557 | 570 |
558 // write comment lines, separated by formfeed, | 571 // write comment lines, separated by formfeed, |
559 // without a line break after the last line | 572 // without a line break after the last line |
560 linebreak := formfeeds[0:1] | 573 linebreak := formfeeds[0:1] |
561 pos := p.fset.Position(comment.Pos()) | 574 pos := p.fset.Position(comment.Pos()) |
562 for i, line := range lines { | 575 for i, line := range lines { |
563 if i > 0 { | 576 if i > 0 { |
564 p.write(linebreak) | 577 p.write(linebreak) |
565 pos = p.pos | 578 pos = p.pos |
566 } | 579 } |
567 if len(line) > 0 { | 580 if len(line) > 0 { |
568 » » » p.writeCommentLine(comment, pos, line) | 581 » » » p.writeItem(pos, p.escape(string(line))) |
569 } | 582 } |
570 } | 583 } |
571 } | 584 } |
572 | 585 |
573 | 586 |
574 // writeCommentSuffix writes a line break after a comment if indicated | 587 // writeCommentSuffix writes a line break after a comment if indicated |
575 // and processes any leftover indentation information. If a line break | 588 // and processes any leftover indentation information. If a line break |
576 // is needed, the kind of break (newline vs formfeed) depends on the | 589 // is needed, the kind of break (newline vs formfeed) depends on the |
577 // pending whitespace. writeCommentSuffix returns true if a pending | 590 // pending whitespace. writeCommentSuffix returns true if a pending |
578 // formfeed was dropped from the whitespace buffer. | 591 // formfeed was dropped from the whitespace buffer. |
579 // | 592 // |
580 func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { | 593 func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { |
581 » for i, ch := range p.buffer { | 594 » for i, ch := range p.wsbuf { |
582 switch ch { | 595 switch ch { |
583 case blank, vtab: | 596 case blank, vtab: |
584 // ignore trailing whitespace | 597 // ignore trailing whitespace |
585 » » » p.buffer[i] = ignore | 598 » » » p.wsbuf[i] = ignore |
586 case indent, unindent: | 599 case indent, unindent: |
587 // don't loose indentation information | 600 // don't loose indentation information |
588 case newline, formfeed: | 601 case newline, formfeed: |
589 // if we need a line break, keep exactly one | 602 // if we need a line break, keep exactly one |
590 // but remember if we dropped any formfeeds | 603 // but remember if we dropped any formfeeds |
591 if needsLinebreak { | 604 if needsLinebreak { |
592 needsLinebreak = false | 605 needsLinebreak = false |
593 } else { | 606 } else { |
594 if ch == formfeed { | 607 if ch == formfeed { |
595 droppedFF = true | 608 droppedFF = true |
596 } | 609 } |
597 » » » » p.buffer[i] = ignore | 610 » » » » p.wsbuf[i] = ignore |
598 » » » } | 611 » » » } |
599 » » } | 612 » » } |
600 » } | 613 » } |
601 » p.writeWhitespace(len(p.buffer)) | 614 » p.writeWhitespace(len(p.wsbuf)) |
602 | 615 |
603 // make sure we have a line break | 616 // make sure we have a line break |
604 if needsLinebreak { | 617 if needsLinebreak { |
605 p.write([]byte{'\n'}) | 618 p.write([]byte{'\n'}) |
606 } | 619 } |
607 | 620 |
608 return | 621 return |
609 } | 622 } |
610 | 623 |
611 | 624 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
645 p.internalError("intersperseComments called without pending comments") | 658 p.internalError("intersperseComments called without pending comments") |
646 return false | 659 return false |
647 } | 660 } |
648 | 661 |
649 | 662 |
650 // whiteWhitespace writes the first n whitespace entries. | 663 // whiteWhitespace writes the first n whitespace entries. |
651 func (p *printer) writeWhitespace(n int) { | 664 func (p *printer) writeWhitespace(n int) { |
652 // write entries | 665 // write entries |
653 var data [1]byte | 666 var data [1]byte |
654 for i := 0; i < n; i++ { | 667 for i := 0; i < n; i++ { |
655 » » switch ch := p.buffer[i]; ch { | 668 » » switch ch := p.wsbuf[i]; ch { |
656 case ignore: | 669 case ignore: |
657 // ignore! | 670 // ignore! |
658 case indent: | 671 case indent: |
659 p.indent++ | 672 p.indent++ |
660 case unindent: | 673 case unindent: |
661 p.indent-- | 674 p.indent-- |
662 if p.indent < 0 { | 675 if p.indent < 0 { |
663 p.internalError("negative indentation:", p.inden t) | 676 p.internalError("negative indentation:", p.inden t) |
664 p.indent = 0 | 677 p.indent = 0 |
665 } | 678 } |
666 case newline, formfeed: | 679 case newline, formfeed: |
667 // A line break immediately followed by a "correcting" | 680 // A line break immediately followed by a "correcting" |
668 // unindent is swapped with the unindent - this permits | 681 // unindent is swapped with the unindent - this permits |
669 // proper label positioning. If a comment is between | 682 // proper label positioning. If a comment is between |
670 // the line break and the label, the unindent is not | 683 // the line break and the label, the unindent is not |
671 // part of the comment whitespace prefix and the comment | 684 // part of the comment whitespace prefix and the comment |
672 // will be positioned correctly indented. | 685 // will be positioned correctly indented. |
673 » » » if i+1 < n && p.buffer[i+1] == unindent { | 686 » » » if i+1 < n && p.wsbuf[i+1] == unindent { |
674 // Use a formfeed to terminate the current secti on. | 687 // Use a formfeed to terminate the current secti on. |
675 // Otherwise, a long label name on the next line leading | 688 // Otherwise, a long label name on the next line leading |
676 // to a wide column may increase the indentation column | 689 // to a wide column may increase the indentation column |
677 // of lines before the label; effectively leadin g to wrong | 690 // of lines before the label; effectively leadin g to wrong |
678 // indentation. | 691 // indentation. |
679 » » » » p.buffer[i], p.buffer[i+1] = unindent, formfeed | 692 » » » » p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed |
680 i-- // do it again | 693 i-- // do it again |
681 continue | 694 continue |
682 } | 695 } |
683 fallthrough | 696 fallthrough |
684 default: | 697 default: |
685 data[0] = byte(ch) | 698 data[0] = byte(ch) |
686 p.write(data[0:]) | 699 p.write(data[0:]) |
687 } | 700 } |
688 } | 701 } |
689 | 702 |
690 // shift remaining entries down | 703 // shift remaining entries down |
691 i := 0 | 704 i := 0 |
692 » for ; n < len(p.buffer); n++ { | 705 » for ; n < len(p.wsbuf); n++ { |
693 » » p.buffer[i] = p.buffer[n] | 706 » » p.wsbuf[i] = p.wsbuf[n] |
694 i++ | 707 i++ |
695 } | 708 } |
696 » p.buffer = p.buffer[0:i] | 709 » p.wsbuf = p.wsbuf[0:i] |
697 } | 710 } |
698 | 711 |
699 | 712 |
700 // ---------------------------------------------------------------------------- | 713 // ---------------------------------------------------------------------------- |
701 // Printing interface | 714 // Printing interface |
702 | 715 |
703 | 716 |
704 func mayCombine(prev token.Token, next byte) (b bool) { | 717 func mayCombine(prev token.Token, next byte) (b bool) { |
705 switch prev { | 718 switch prev { |
706 case token.INT: | 719 case token.INT: |
(...skipping 20 matching lines...) Expand all Loading... | |
727 // | 740 // |
728 // Whitespace is accumulated until a non-whitespace token appears. Any | 741 // Whitespace is accumulated until a non-whitespace token appears. Any |
729 // comments that need to appear before that token are printed first, | 742 // comments that need to appear before that token are printed first, |
730 // taking into account the amount and structure of any pending white- | 743 // taking into account the amount and structure of any pending white- |
731 // space for best comment placement. Then, any leftover whitespace is | 744 // space for best comment placement. Then, any leftover whitespace is |
732 // printed, followed by the actual token. | 745 // printed, followed by the actual token. |
733 // | 746 // |
734 func (p *printer) print(args ...interface{}) { | 747 func (p *printer) print(args ...interface{}) { |
735 for _, f := range args { | 748 for _, f := range args { |
736 next := p.pos // estimated position of next item | 749 next := p.pos // estimated position of next item |
737 » » var data []byte | 750 » » var data string |
738 var tok token.Token | 751 var tok token.Token |
739 | 752 |
740 switch x := f.(type) { | 753 switch x := f.(type) { |
741 case pmode: | 754 case pmode: |
742 // toggle printer mode | 755 // toggle printer mode |
743 p.mode ^= x | 756 p.mode ^= x |
744 case whiteSpace: | 757 case whiteSpace: |
745 if x == ignore { | 758 if x == ignore { |
746 // don't add ignore's to the buffer; they | 759 // don't add ignore's to the buffer; they |
747 // may screw up "correcting" unindents (see | 760 // may screw up "correcting" unindents (see |
748 // LabeledStmt) | 761 // LabeledStmt) |
749 break | 762 break |
750 } | 763 } |
751 » » » i := len(p.buffer) | 764 » » » i := len(p.wsbuf) |
752 » » » if i == cap(p.buffer) { | 765 » » » if i == cap(p.wsbuf) { |
753 // Whitespace sequences are very short so this s hould | 766 // Whitespace sequences are very short so this s hould |
754 // never happen. Handle gracefully (but possibly with | 767 // never happen. Handle gracefully (but possibly with |
755 // bad comment placement) if it does happen. | 768 // bad comment placement) if it does happen. |
756 p.writeWhitespace(i) | 769 p.writeWhitespace(i) |
757 i = 0 | 770 i = 0 |
758 } | 771 } |
759 » » » p.buffer = p.buffer[0 : i+1] | 772 » » » p.wsbuf = p.wsbuf[0 : i+1] |
760 » » » p.buffer[i] = x | 773 » » » p.wsbuf[i] = x |
761 case *ast.Ident: | 774 case *ast.Ident: |
762 » » » data = []byte(x.Name) | 775 » » » data = x.Name |
763 tok = token.IDENT | 776 tok = token.IDENT |
764 case *ast.BasicLit: | 777 case *ast.BasicLit: |
765 // escape all literals so they pass through unchanged | |
766 // (note that valid Go programs cannot contain | |
767 // tabwriter.Escape bytes since they do not appear in | |
768 // legal UTF-8 sequences) | |
769 data = make([]byte, 0, len(x.Value)+2) | |
770 data = append(data, tabwriter.Escape) | |
771 data = append(data, []byte(x.Value)...) | |
772 data = append(data, tabwriter.Escape) | |
773 tok = x.Kind | |
774 // If we have a raw string that spans multiple lines and | 778 // If we have a raw string that spans multiple lines and |
775 // the opening quote (`) is on a line preceded only by | 779 // the opening quote (`) is on a line preceded only by |
776 // indentation, we don't want to write that indentation | 780 // indentation, we don't want to write that indentation |
777 // because the following lines of the raw string are not | 781 // because the following lines of the raw string are not |
778 // indented. It's easiest to correct the output at the e nd | 782 // indented. It's easiest to correct the output at the e nd |
779 // via the trimmer (because of the complex handling of | 783 // via the trimmer (because of the complex handling of |
780 // white space). | 784 // white space). |
781 // Mark multi-line raw strings by replacing the opening | 785 // Mark multi-line raw strings by replacing the opening |
782 // quote with esc2 and have the trimmer take care of fix ing | 786 // quote with esc2 and have the trimmer take care of fix ing |
783 » » » // it up. (Do this _after_ making a copy of data!) | 787 » » » // it up. |
784 » » » if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 { | 788 » » » if x.Value[0] == '`' && strings.Index(x.Value, "\n") > 0 { |
785 » » » » data[1] = esc2 | 789 » » » » data = p.escape(esc2str + x.Value[1:]) |
786 » » » } | 790 » » » } else { |
791 » » » » data = p.escape(x.Value) | |
792 » » » } | |
793 » » » tok = x.Kind | |
787 case token.Token: | 794 case token.Token: |
788 s := x.String() | 795 s := x.String() |
789 if mayCombine(p.lastTok, s[0]) { | 796 if mayCombine(p.lastTok, s[0]) { |
790 // the previous and the current token must be | 797 // the previous and the current token must be |
791 // separated by a blank otherwise they combine | 798 // separated by a blank otherwise they combine |
792 // into a different incorrect token sequence | 799 // into a different incorrect token sequence |
793 // (except for token.INT followed by a '.' this | 800 // (except for token.INT followed by a '.' this |
794 // should never happen because it is taken care | 801 // should never happen because it is taken care |
795 // of via binary expression formatting) | 802 // of via binary expression formatting) |
796 » » » » if len(p.buffer) != 0 { | 803 » » » » if len(p.wsbuf) != 0 { |
797 p.internalError("whitespace buffer not e mpty") | 804 p.internalError("whitespace buffer not e mpty") |
798 } | 805 } |
799 » » » » p.buffer = p.buffer[0:1] | 806 » » » » p.wsbuf = p.wsbuf[0:1] |
800 » » » » p.buffer[0] = ' ' | 807 » » » » p.wsbuf[0] = ' ' |
801 » » » } | 808 » » » } |
802 » » » data = []byte(s) | 809 » » » data = s |
803 tok = x | 810 tok = x |
804 case token.Pos: | 811 case token.Pos: |
805 if x.IsValid() { | 812 if x.IsValid() { |
806 next = p.fset.Position(x) // accurate position o f next item | 813 next = p.fset.Position(x) // accurate position o f next item |
807 } | 814 } |
808 tok = p.lastTok | 815 tok = p.lastTok |
809 default: | 816 default: |
810 fmt.Fprintf(os.Stderr, "print: unsupported argument type %T\n", f) | 817 fmt.Fprintf(os.Stderr, "print: unsupported argument type %T\n", f) |
811 panic("go/printer type") | 818 panic("go/printer type") |
812 } | 819 } |
813 p.lastTok = tok | 820 p.lastTok = tok |
814 p.pos = next | 821 p.pos = next |
815 | 822 |
816 » » if data != nil { | 823 » » if data != "" { |
817 droppedFF := p.flush(next, tok) | 824 droppedFF := p.flush(next, tok) |
818 | 825 |
819 // intersperse extra newlines if present in the source | 826 // intersperse extra newlines if present in the source |
820 // (don't do this in flush as it will cause extra newlin es | 827 // (don't do this in flush as it will cause extra newlin es |
821 // at the end of a file) - use formfeeds if we dropped o ne | 828 // at the end of a file) - use formfeeds if we dropped o ne |
822 // before | 829 // before |
823 p.writeNewlines(next.Line-p.pos.Line, droppedFF) | 830 p.writeNewlines(next.Line-p.pos.Line, droppedFF) |
824 | 831 |
825 p.writeItem(next, data) | 832 p.writeItem(next, data) |
826 } | 833 } |
(...skipping 14 matching lines...) Expand all Loading... | |
841 // returns true if a pending formfeed character was dropped | 848 // returns true if a pending formfeed character was dropped |
842 // from the whitespace buffer as a result of interspersing | 849 // from the whitespace buffer as a result of interspersing |
843 // comments. | 850 // comments. |
844 // | 851 // |
845 func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { | 852 func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { |
846 if p.commentBefore(next) { | 853 if p.commentBefore(next) { |
847 // if there are comments before the next item, intersperse them | 854 // if there are comments before the next item, intersperse them |
848 droppedFF = p.intersperseComments(next, tok) | 855 droppedFF = p.intersperseComments(next, tok) |
849 } else { | 856 } else { |
850 // otherwise, write any leftover whitespace | 857 // otherwise, write any leftover whitespace |
851 » » p.writeWhitespace(len(p.buffer)) | 858 » » p.writeWhitespace(len(p.wsbuf)) |
852 } | 859 } |
853 return | 860 return |
854 } | 861 } |
855 | 862 |
856 | 863 |
857 // ---------------------------------------------------------------------------- | 864 // ---------------------------------------------------------------------------- |
858 // Trimmer | 865 // Trimmer |
859 | 866 |
860 // A trimmer is an io.Writer filter for stripping tabwriter.Escape | 867 // A trimmer is an io.Writer filter for stripping tabwriter.Escape |
861 // characters, trailing blanks and tabs, and for converting formfeed | 868 // characters, trailing blanks and tabs, and for converting formfeed |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1083 } | 1090 } |
1084 | 1091 |
1085 | 1092 |
1086 // Fprint "pretty-prints" an AST node to output. | 1093 // Fprint "pretty-prints" an AST node to output. |
1087 // It calls Config.Fprint with default settings. | 1094 // It calls Config.Fprint with default settings. |
1088 // | 1095 // |
1089 func Fprint(output io.Writer, fset *token.FileSet, node interface{}) os.Error { | 1096 func Fprint(output io.Writer, fset *token.FileSet, node interface{}) os.Error { |
1090 _, err := (&Config{Tabwidth: 8}).Fprint(output, fset, node) // don't car e about number of bytes written | 1097 _, err := (&Config{Tabwidth: 8}).Fprint(output, fset, node) // don't car e about number of bytes written |
1091 return err | 1098 return err |
1092 } | 1099 } |
LEFT | RIGHT |