...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package format
17
18
19
20
21
22
23
24
25 import (
26 "bytes"
27 "fmt"
28 "strings"
29 "text/tabwriter"
30
31 "cuelang.org/go/cue/ast"
32 "cuelang.org/go/cue/parser"
33 "cuelang.org/go/cue/token"
34 )
35
36
37 type Option func(c *config)
38
39
40
41 func Simplify() Option {
42 return func(c *config) { c.simplify = true }
43 }
44
45
46
47 func UseSpaces(tabwidth int) Option {
48 return func(c *config) {
49 c.UseSpaces = true
50 c.Tabwidth = tabwidth
51 }
52 }
53
54
55
56 func TabIndent(indent bool) Option {
57 return func(c *config) { c.TabIndent = indent }
58 }
59
60
61
62 func IndentPrefix(n int) Option {
63 return func(c *config) { c.Indent = n }
64 }
65
66
67
68 func sortImportsOption() Option {
69 return func(c *config) { c.sortImports = true }
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 func Node(node ast.Node, opt ...Option) ([]byte, error) {
91 cfg := newConfig(opt)
92 return cfg.fprint(node)
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107 func Source(b []byte, opt ...Option) ([]byte, error) {
108 cfg := newConfig(opt)
109
110 f, err := parser.ParseFile("", b, parser.ParseComments)
111 if err != nil {
112 return nil, fmt.Errorf("parse: %s", err)
113 }
114
115
116 return cfg.fprint(f)
117 }
118
119 type config struct {
120 UseSpaces bool
121 TabIndent bool
122 Tabwidth int
123 Indent int
124
125 simplify bool
126 sortImports bool
127 }
128
129 func newConfig(opt []Option) *config {
130 cfg := &config{
131 Tabwidth: 8,
132 TabIndent: true,
133 UseSpaces: true,
134 }
135 for _, o := range opt {
136 o(cfg)
137 }
138 return cfg
139 }
140
141
142 func (cfg *config) fprint(node interface{}) (out []byte, err error) {
143 var p printer
144 p.init(cfg)
145 if err = printNode(node, &p); err != nil {
146 return p.output, err
147 }
148
149 padchar := byte('\t')
150 if cfg.UseSpaces {
151 padchar = byte(' ')
152 }
153
154 twmode := tabwriter.StripEscape | tabwriter.TabIndent | tabwriter.DiscardEmptyColumns
155 if cfg.TabIndent {
156 twmode |= tabwriter.TabIndent
157 }
158
159 buf := &bytes.Buffer{}
160 tw := tabwriter.NewWriter(buf, 0, cfg.Tabwidth, 1, padchar, twmode)
161
162
163 if _, err = tw.Write(p.output); err != nil {
164 return
165 }
166
167 err = tw.Flush()
168 if err != nil {
169 return buf.Bytes(), err
170 }
171
172 b := buf.Bytes()
173 if !cfg.TabIndent {
174 b = bytes.ReplaceAll(b, []byte{'\t'}, bytes.Repeat([]byte{' '}, cfg.Tabwidth))
175 }
176 return b, nil
177 }
178
179
180
181 type formatter struct {
182 *printer
183
184 stack []frame
185 current frame
186 nestExpr int
187 }
188
189 func newFormatter(p *printer) *formatter {
190 f := &formatter{
191 printer: p,
192 current: frame{
193 settings: settings{
194 nodeSep: newline,
195 parentSep: newline,
196 },
197 },
198 }
199 return f
200 }
201
202 type whiteSpace int
203
204 const (
205 ignore whiteSpace = 0
206
207
208 blank whiteSpace = 1 << iota
209 vtab
210 noblank
211
212 nooverride
213
214 comma
215 trailcomma
216 declcomma
217
218 newline
219 formfeed
220 newsection
221
222 indent
223 unindent
224 indented
225 )
226
227 type frame struct {
228 cg []*ast.CommentGroup
229 pos int8
230
231 settings
232 }
233
234 type settings struct {
235
236
237 nodeSep whiteSpace
238 parentSep whiteSpace
239 override whiteSpace
240 }
241
242
243 func init() {
244 s := settings{}
245 _ = s.override
246 }
247
248 func (f *formatter) print(a ...interface{}) {
249 for _, x := range a {
250 f.Print(x)
251 switch x.(type) {
252 case string, token.Token:
253 f.current.pos++
254 }
255 }
256 }
257
258 func (f *formatter) formfeed() whiteSpace {
259 if f.current.nodeSep == blank {
260 return blank
261 }
262 return formfeed
263 }
264
265 func (f *formatter) wsOverride(def whiteSpace) whiteSpace {
266 if f.current.override == ignore {
267 return def
268 }
269 return f.current.override
270 }
271
272 func (f *formatter) onOneLine(node ast.Node) bool {
273 a := node.Pos()
274 b := node.End()
275 if a.IsValid() && b.IsValid() {
276 return f.lineFor(a) == f.lineFor(b)
277 }
278
279 return false
280 }
281
282 func (f *formatter) before(node ast.Node) bool {
283 f.stack = append(f.stack, f.current)
284 f.current = frame{settings: f.current.settings}
285 f.current.parentSep = f.current.nodeSep
286
287 if node != nil {
288 s, ok := node.(*ast.StructLit)
289 if ok && len(s.Elts) <= 1 && f.current.nodeSep != blank && f.onOneLine(node) {
290 f.current.nodeSep = blank
291 }
292 f.current.cg = node.Comments()
293 f.visitComments(f.current.pos)
294 return true
295 }
296 return false
297 }
298
299 func (f *formatter) after(node ast.Node) {
300 f.visitComments(127)
301 p := len(f.stack) - 1
302 f.current = f.stack[p]
303 f.stack = f.stack[:p]
304 f.current.pos++
305 f.visitComments(f.current.pos)
306 }
307
308 func (f *formatter) visitComments(until int8) {
309 c := &f.current
310
311 printed := false
312 for ; len(c.cg) > 0 && c.cg[0].Position <= until; c.cg = c.cg[1:] {
313 if printed {
314 f.Print(newsection)
315 }
316 printed = true
317 f.printComment(c.cg[0])
318 }
319 }
320
321 func (f *formatter) printComment(cg *ast.CommentGroup) {
322 f.Print(cg)
323
324 printBlank := false
325 if cg.Doc && len(f.output) > 0 {
326 f.Print(newline)
327 printBlank = true
328 }
329 for _, c := range cg.List {
330 isEnd := strings.HasPrefix(c.Text, "//")
331 if !printBlank {
332 if isEnd {
333 f.Print(vtab)
334 } else {
335 f.Print(blank)
336 }
337 }
338 f.Print(c.Slash)
339 f.Print(c)
340 if isEnd {
341 f.Print(newline)
342 if cg.Doc {
343 f.Print(nooverride)
344 }
345 }
346 }
347 }
348
View as plain text