github.com/AndrienkoAleksandr/go@v0.0.19/src/go/printer/gobuild.go (about) 1 // Copyright 2020 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 package printer 6 7 import ( 8 "go/build/constraint" 9 "sort" 10 "text/tabwriter" 11 ) 12 13 func (p *printer) fixGoBuildLines() { 14 if len(p.goBuild)+len(p.plusBuild) == 0 { 15 return 16 } 17 18 // Find latest possible placement of //go:build and // +build comments. 19 // That's just after the last blank line before we find a non-comment. 20 // (We'll add another blank line after our comment block.) 21 // When we start dropping // +build comments, we can skip over /* */ comments too. 22 // Note that we are processing tabwriter input, so every comment 23 // begins and ends with a tabwriter.Escape byte. 24 // And some newlines have turned into \f bytes. 25 insert := 0 26 for pos := 0; ; { 27 // Skip leading space at beginning of line. 28 blank := true 29 for pos < len(p.output) && (p.output[pos] == ' ' || p.output[pos] == '\t') { 30 pos++ 31 } 32 // Skip over // comment if any. 33 if pos+3 < len(p.output) && p.output[pos] == tabwriter.Escape && p.output[pos+1] == '/' && p.output[pos+2] == '/' { 34 blank = false 35 for pos < len(p.output) && !isNL(p.output[pos]) { 36 pos++ 37 } 38 } 39 // Skip over \n at end of line. 40 if pos >= len(p.output) || !isNL(p.output[pos]) { 41 break 42 } 43 pos++ 44 45 if blank { 46 insert = pos 47 } 48 } 49 50 // If there is a //go:build comment before the place we identified, 51 // use that point instead. (Earlier in the file is always fine.) 52 if len(p.goBuild) > 0 && p.goBuild[0] < insert { 53 insert = p.goBuild[0] 54 } else if len(p.plusBuild) > 0 && p.plusBuild[0] < insert { 55 insert = p.plusBuild[0] 56 } 57 58 var x constraint.Expr 59 switch len(p.goBuild) { 60 case 0: 61 // Synthesize //go:build expression from // +build lines. 62 for _, pos := range p.plusBuild { 63 y, err := constraint.Parse(p.commentTextAt(pos)) 64 if err != nil { 65 x = nil 66 break 67 } 68 if x == nil { 69 x = y 70 } else { 71 x = &constraint.AndExpr{X: x, Y: y} 72 } 73 } 74 case 1: 75 // Parse //go:build expression. 76 x, _ = constraint.Parse(p.commentTextAt(p.goBuild[0])) 77 } 78 79 var block []byte 80 if x == nil { 81 // Don't have a valid //go:build expression to treat as truth. 82 // Bring all the lines together but leave them alone. 83 // Note that these are already tabwriter-escaped. 84 for _, pos := range p.goBuild { 85 block = append(block, p.lineAt(pos)...) 86 } 87 for _, pos := range p.plusBuild { 88 block = append(block, p.lineAt(pos)...) 89 } 90 } else { 91 block = append(block, tabwriter.Escape) 92 block = append(block, "//go:build "...) 93 block = append(block, x.String()...) 94 block = append(block, tabwriter.Escape, '\n') 95 if len(p.plusBuild) > 0 { 96 lines, err := constraint.PlusBuildLines(x) 97 if err != nil { 98 lines = []string{"// +build error: " + err.Error()} 99 } 100 for _, line := range lines { 101 block = append(block, tabwriter.Escape) 102 block = append(block, line...) 103 block = append(block, tabwriter.Escape, '\n') 104 } 105 } 106 } 107 block = append(block, '\n') 108 109 // Build sorted list of lines to delete from remainder of output. 110 toDelete := append(p.goBuild, p.plusBuild...) 111 sort.Ints(toDelete) 112 113 // Collect output after insertion point, with lines deleted, into after. 114 var after []byte 115 start := insert 116 for _, end := range toDelete { 117 if end < start { 118 continue 119 } 120 after = appendLines(after, p.output[start:end]) 121 start = end + len(p.lineAt(end)) 122 } 123 after = appendLines(after, p.output[start:]) 124 if n := len(after); n >= 2 && isNL(after[n-1]) && isNL(after[n-2]) { 125 after = after[:n-1] 126 } 127 128 p.output = p.output[:insert] 129 p.output = append(p.output, block...) 130 p.output = append(p.output, after...) 131 } 132 133 // appendLines is like append(x, y...) 134 // but it avoids creating doubled blank lines, 135 // which would not be gofmt-standard output. 136 // It assumes that only whole blocks of lines are being appended, 137 // not line fragments. 138 func appendLines(x, y []byte) []byte { 139 if len(y) > 0 && isNL(y[0]) && // y starts in blank line 140 (len(x) == 0 || len(x) >= 2 && isNL(x[len(x)-1]) && isNL(x[len(x)-2])) { // x is empty or ends in blank line 141 y = y[1:] // delete y's leading blank line 142 } 143 return append(x, y...) 144 } 145 146 func (p *printer) lineAt(start int) []byte { 147 pos := start 148 for pos < len(p.output) && !isNL(p.output[pos]) { 149 pos++ 150 } 151 if pos < len(p.output) { 152 pos++ 153 } 154 return p.output[start:pos] 155 } 156 157 func (p *printer) commentTextAt(start int) string { 158 if start < len(p.output) && p.output[start] == tabwriter.Escape { 159 start++ 160 } 161 pos := start 162 for pos < len(p.output) && p.output[pos] != tabwriter.Escape && !isNL(p.output[pos]) { 163 pos++ 164 } 165 return string(p.output[start:pos]) 166 } 167 168 func isNL(b byte) bool { 169 return b == '\n' || b == '\f' 170 }