github.com/sirkon/goproxy@v1.4.8/internal/modfile/print.go (about) 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 5 // Module file printer. 6 7 package modfile 8 9 import ( 10 "bytes" 11 "fmt" 12 "strings" 13 ) 14 15 func Format(f *FileSyntax) []byte { 16 pr := &printer{} 17 pr.file(f) 18 return pr.Bytes() 19 } 20 21 // A printer collects the state during printing of a file or expression. 22 type printer struct { 23 bytes.Buffer // output buffer 24 comment []Comment // pending end-of-line comments 25 margin int // left margin (indent), a number of tabs 26 } 27 28 // printf prints to the buffer. 29 func (p *printer) printf(format string, args ...interface{}) { 30 fmt.Fprintf(p, format, args...) 31 } 32 33 // indent returns the position on the current line, in bytes, 0-indexed. 34 func (p *printer) indent() int { 35 b := p.Bytes() 36 n := 0 37 for n < len(b) && b[len(b)-1-n] != '\n' { 38 n++ 39 } 40 return n 41 } 42 43 // newline ends the current line, flushing end-of-line comments. 44 func (p *printer) newline() { 45 if len(p.comment) > 0 { 46 p.printf(" ") 47 for i, com := range p.comment { 48 if i > 0 { 49 p.trim() 50 p.printf("\n") 51 for i := 0; i < p.margin; i++ { 52 p.printf("\t") 53 } 54 } 55 p.printf("%s", strings.TrimSpace(com.Token)) 56 } 57 p.comment = p.comment[:0] 58 } 59 60 p.trim() 61 p.printf("\n") 62 for i := 0; i < p.margin; i++ { 63 p.printf("\t") 64 } 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 78 // file formats the given file into the print buffer. 79 func (p *printer) file(f *FileSyntax) { 80 for _, com := range f.Before { 81 p.printf("%s", strings.TrimSpace(com.Token)) 82 p.newline() 83 } 84 85 for i, stmt := range f.Stmt { 86 switch x := stmt.(type) { 87 case *CommentBlock: 88 // comments already handled 89 p.expr(x) 90 91 default: 92 p.expr(x) 93 p.newline() 94 } 95 96 for _, com := range stmt.Comment().After { 97 p.printf("%s", strings.TrimSpace(com.Token)) 98 p.newline() 99 } 100 101 if i+1 < len(f.Stmt) { 102 p.newline() 103 } 104 } 105 } 106 107 func (p *printer) expr(x Expr) { 108 // Emit line-comments preceding this expression. 109 if before := x.Comment().Before; len(before) > 0 { 110 // Want to print a line comment. 111 // Line comments must be at the current margin. 112 p.trim() 113 if p.indent() > 0 { 114 // There's other text on the line. Start a new line. 115 p.printf("\n") 116 } 117 // Re-indent to margin. 118 for i := 0; i < p.margin; i++ { 119 p.printf("\t") 120 } 121 for _, com := range before { 122 p.printf("%s", strings.TrimSpace(com.Token)) 123 p.newline() 124 } 125 } 126 127 switch x := x.(type) { 128 default: 129 panic(fmt.Errorf("printer: unexpected type %T", x)) 130 131 case *CommentBlock: 132 // done 133 134 case *LParen: 135 p.printf("(") 136 case *RParen: 137 p.printf(")") 138 139 case *Line: 140 sep := "" 141 for _, tok := range x.Token { 142 p.printf("%s%s", sep, tok) 143 sep = " " 144 } 145 146 case *LineBlock: 147 for _, tok := range x.Token { 148 p.printf("%s ", tok) 149 } 150 p.expr(&x.LParen) 151 p.margin++ 152 for _, l := range x.Line { 153 p.newline() 154 p.expr(l) 155 } 156 p.margin-- 157 p.newline() 158 p.expr(&x.RParen) 159 } 160 161 // Queue end-of-line comments for printing when we 162 // reach the end of the line. 163 p.comment = append(p.comment, x.Comment().Suffix...) 164 }