print.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Module file printer.
  5. package modfile
  6. import (
  7. "bytes"
  8. "fmt"
  9. "strings"
  10. )
  11. // Format returns a go.mod file as a byte slice, formatted in standard style.
  12. func Format(f *FileSyntax) []byte {
  13. pr := &printer{}
  14. pr.file(f)
  15. // remove trailing blank lines
  16. b := pr.Bytes()
  17. for len(b) > 0 && b[len(b)-1] == '\n' && (len(b) == 1 || b[len(b)-2] == '\n') {
  18. b = b[:len(b)-1]
  19. }
  20. return b
  21. }
  22. // A printer collects the state during printing of a file or expression.
  23. type printer struct {
  24. bytes.Buffer // output buffer
  25. comment []Comment // pending end-of-line comments
  26. margin int // left margin (indent), a number of tabs
  27. }
  28. // printf prints to the buffer.
  29. func (p *printer) printf(format string, args ...interface{}) {
  30. fmt.Fprintf(p, format, args...)
  31. }
  32. // indent returns the position on the current line, in bytes, 0-indexed.
  33. func (p *printer) indent() int {
  34. b := p.Bytes()
  35. n := 0
  36. for n < len(b) && b[len(b)-1-n] != '\n' {
  37. n++
  38. }
  39. return n
  40. }
  41. // newline ends the current line, flushing end-of-line comments.
  42. func (p *printer) newline() {
  43. if len(p.comment) > 0 {
  44. p.printf(" ")
  45. for i, com := range p.comment {
  46. if i > 0 {
  47. p.trim()
  48. p.printf("\n")
  49. for i := 0; i < p.margin; i++ {
  50. p.printf("\t")
  51. }
  52. }
  53. p.printf("%s", strings.TrimSpace(com.Token))
  54. }
  55. p.comment = p.comment[:0]
  56. }
  57. p.trim()
  58. if b := p.Bytes(); len(b) == 0 || (len(b) >= 2 && b[len(b)-1] == '\n' && b[len(b)-2] == '\n') {
  59. // skip the blank line at top of file or after a blank line
  60. } else {
  61. p.printf("\n")
  62. }
  63. for i := 0; i < p.margin; i++ {
  64. p.printf("\t")
  65. }
  66. }
  67. // trim removes trailing spaces and tabs from the current line.
  68. func (p *printer) trim() {
  69. // Remove trailing spaces and tabs from line we're about to end.
  70. b := p.Bytes()
  71. n := len(b)
  72. for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') {
  73. n--
  74. }
  75. p.Truncate(n)
  76. }
  77. // file formats the given file into the print buffer.
  78. func (p *printer) file(f *FileSyntax) {
  79. for _, com := range f.Before {
  80. p.printf("%s", strings.TrimSpace(com.Token))
  81. p.newline()
  82. }
  83. for i, stmt := range f.Stmt {
  84. switch x := stmt.(type) {
  85. case *CommentBlock:
  86. // comments already handled
  87. p.expr(x)
  88. default:
  89. p.expr(x)
  90. p.newline()
  91. }
  92. for _, com := range stmt.Comment().After {
  93. p.printf("%s", strings.TrimSpace(com.Token))
  94. p.newline()
  95. }
  96. if i+1 < len(f.Stmt) {
  97. p.newline()
  98. }
  99. }
  100. }
  101. func (p *printer) expr(x Expr) {
  102. // Emit line-comments preceding this expression.
  103. if before := x.Comment().Before; len(before) > 0 {
  104. // Want to print a line comment.
  105. // Line comments must be at the current margin.
  106. p.trim()
  107. if p.indent() > 0 {
  108. // There's other text on the line. Start a new line.
  109. p.printf("\n")
  110. }
  111. // Re-indent to margin.
  112. for i := 0; i < p.margin; i++ {
  113. p.printf("\t")
  114. }
  115. for _, com := range before {
  116. p.printf("%s", strings.TrimSpace(com.Token))
  117. p.newline()
  118. }
  119. }
  120. switch x := x.(type) {
  121. default:
  122. panic(fmt.Errorf("printer: unexpected type %T", x))
  123. case *CommentBlock:
  124. // done
  125. case *LParen:
  126. p.printf("(")
  127. case *RParen:
  128. p.printf(")")
  129. case *Line:
  130. p.tokens(x.Token)
  131. case *LineBlock:
  132. p.tokens(x.Token)
  133. p.printf(" ")
  134. p.expr(&x.LParen)
  135. p.margin++
  136. for _, l := range x.Line {
  137. p.newline()
  138. p.expr(l)
  139. }
  140. p.margin--
  141. p.newline()
  142. p.expr(&x.RParen)
  143. }
  144. // Queue end-of-line comments for printing when we
  145. // reach the end of the line.
  146. p.comment = append(p.comment, x.Comment().Suffix...)
  147. }
  148. func (p *printer) tokens(tokens []string) {
  149. sep := ""
  150. for _, t := range tokens {
  151. if t == "," || t == ")" || t == "]" || t == "}" {
  152. sep = ""
  153. }
  154. p.printf("%s%s", sep, t)
  155. sep = " "
  156. if t == "(" || t == "[" || t == "{" {
  157. sep = ""
  158. }
  159. }
  160. }