@@ -17,115 +17,148 @@ package cmd
17
17
import (
18
18
"bytes"
19
19
"fmt"
20
+ "io"
21
+ "io/fs"
20
22
"os"
21
23
"path/filepath"
24
+ "slices"
25
+ "strings"
22
26
27
+ "github.com/rogpeppe/go-internal/diff"
28
+ "github.com/spf13/cobra"
29
+
30
+ "cuelang.org/go/cue/build"
23
31
"cuelang.org/go/cue/errors"
24
32
"cuelang.org/go/cue/format"
25
33
"cuelang.org/go/cue/load"
26
34
"cuelang.org/go/cue/parser"
27
35
"cuelang.org/go/cue/token"
36
+ "cuelang.org/go/internal/filetypes"
28
37
"cuelang.org/go/internal/source"
29
-
30
- "github.com/rogpeppe/go-internal/diff"
31
- "github.com/spf13/cobra"
32
38
)
33
39
34
40
func newFmtCmd (c * Command ) * cobra.Command {
35
41
cmd := & cobra.Command {
36
42
Use : "fmt [-s] [inputs]" ,
37
43
Short : "formats CUE configuration files" ,
38
44
Long : `Fmt formats the given files or the files for the given packages in place
45
+
46
+ Arguments are interpreted as import paths (see 'cue help inputs') unless --files is set,
47
+ in which case the arguments are file paths to descend into and format all CUE files.
48
+ Directories named "cue.mod" and those beginning with "." and "_" are skipped unless
49
+ given as explicit arguments.
39
50
` ,
40
51
RunE : mkRunE (c , func (cmd * Command , args []string ) error {
41
- builds := loadFromArgs (args , & load.Config {
42
- Tests : true ,
43
- Tools : true ,
44
- AllCUEFiles : true ,
45
- Package : "*" ,
46
- })
47
- if builds == nil {
48
- return errors .Newf (token .NoPos , "invalid args" )
49
- }
52
+ cwd , _ := os .Getwd ()
53
+ check := flagCheck .Bool (cmd )
54
+ doDiff := flagDiff .Bool (cmd )
50
55
51
- opts := []format.Option {}
56
+ formatOpts := []format.Option {}
52
57
if flagSimplify .Bool (cmd ) {
53
- opts = append (opts , format .Simplify ())
58
+ formatOpts = append (formatOpts , format .Simplify ())
54
59
}
55
60
56
61
var foundBadlyFormatted bool
57
- check := flagCheck .Bool (cmd )
58
- doDiff := flagDiff .Bool (cmd )
59
- cwd , _ := os .Getwd ()
60
- stdout := cmd .OutOrStdout ()
61
-
62
- for _ , inst := range builds {
63
- if err := inst .Err ; err != nil {
64
- switch {
65
- case errors .As (err , new (* load.PackageError )) && len (inst .BuildFiles ) != 0 :
66
- // Ignore package errors if there are files to format.
67
- case errors .As (err , new (* load.NoFilesError )):
68
- default :
69
- return err
70
- }
62
+ if ! flagFiles .Bool (cmd ) { // format packages
63
+ builds := loadFromArgs (args , & load.Config {
64
+ Tests : true ,
65
+ Tools : true ,
66
+ AllCUEFiles : true ,
67
+ Package : "*" ,
68
+ })
69
+ if len (builds ) == 0 {
70
+ return errors .Newf (token .NoPos , "invalid args" )
71
71
}
72
- for _ , file := range inst .BuildFiles {
73
- shouldFormat := inst .User || file .Filename == "-" || filepath .Dir (file .Filename ) == inst .Dir
74
- if ! shouldFormat {
75
- continue
72
+
73
+ for _ , inst := range builds {
74
+ if inst .Err != nil {
75
+ switch {
76
+ case errors .As (inst .Err , new (* load.PackageError )) && len (inst .BuildFiles ) != 0 :
77
+ // Ignore package errors if there are files to format.
78
+ case errors .As (inst .Err , new (* load.NoFilesError )):
79
+ default :
80
+ exitOnErr (cmd , inst .Err , false )
81
+ continue
82
+ }
76
83
}
84
+ for _ , file := range inst .BuildFiles {
85
+ shouldFormat := inst .User || file .Filename == "-" || filepath .Dir (file .Filename ) == inst .Dir
86
+ if ! shouldFormat {
87
+ continue
88
+ }
77
89
78
- // We buffer the input and output bytes to compare them.
79
- // This allows us to determine whether a file is already
80
- // formatted, without modifying the file.
81
- src , ok := file .Source .([]byte )
82
- if ! ok {
83
- var err error
84
- src , err = source .ReadAll (file .Filename , file .Source )
90
+ wasModified , err := formatFile (file , formatOpts , cwd , doDiff , check , cmd )
85
91
if err != nil {
86
92
return err
87
93
}
94
+ if wasModified {
95
+ foundBadlyFormatted = true
96
+ }
88
97
}
98
+ }
99
+ } else { // format individual files
100
+ hasDots := slices .ContainsFunc (args , func (arg string ) bool {
101
+ return strings .Contains (arg , "..." )
102
+ })
103
+ if hasDots {
104
+ return errors .New (`cannot use "..." in --files mode` )
105
+ }
106
+
107
+ if len (args ) == 0 {
108
+ args = []string {"." }
109
+ }
89
110
90
- file , err := parser .ParseFile (file .Filename , src , parser .ParseComments )
111
+ processFile := func (path string ) error {
112
+ file , err := filetypes .ParseFile (path , filetypes .Input )
91
113
if err != nil {
92
114
return err
93
115
}
94
116
95
- formatted , err := format .Node (file , opts ... )
117
+ if path == "-" {
118
+ contents , err := io .ReadAll (cmd .InOrStdin ())
119
+ exitOnErr (cmd , err , false )
120
+ file .Source = contents
121
+ }
122
+
123
+ wasModified , err := formatFile (file , formatOpts , cwd , doDiff , check , cmd )
96
124
if err != nil {
97
125
return err
98
126
}
99
-
100
- // Always write to stdout if the file is read from stdin.
101
- if file .Filename == "-" && ! doDiff && ! check {
102
- stdout .Write (formatted )
127
+ if wasModified {
128
+ foundBadlyFormatted = true
103
129
}
104
-
105
- // File is already well formatted; we can stop here.
106
- if bytes .Equal (formatted , src ) {
130
+ return nil
131
+ }
132
+ for _ , arg := range args {
133
+ if arg == "-" {
134
+ err := processFile (arg )
135
+ exitOnErr (cmd , err , false )
107
136
continue
108
137
}
109
138
110
- foundBadlyFormatted = true
111
- path , err := filepath .Rel (cwd , file .Filename )
112
- if err != nil {
113
- path = file .Filename
114
- }
115
-
116
- switch {
117
- case doDiff :
118
- d := diff .Diff (path + ".orig" , src , path , formatted )
119
- fmt .Fprintln (stdout , string (d ))
120
- case check :
121
- fmt .Fprintln (stdout , path )
122
- case file .Filename == "-" :
123
- // already wrote the formatted source to stdout above
124
- default :
125
- if err := os .WriteFile (file .Filename , formatted , 0644 ); err != nil {
139
+ arg = filepath .Clean (arg )
140
+ err := filepath .WalkDir (arg , func (path string , d fs.DirEntry , err error ) error {
141
+ if err != nil {
126
142
return err
127
143
}
128
- }
144
+
145
+ if d .IsDir () {
146
+ name := d .Name ()
147
+ isMod := name == "cue.mod"
148
+ isDot := strings .HasPrefix (name , "." ) && name != "." && name != ".."
149
+ if path != arg && (isMod || isDot || strings .HasPrefix (name , "_" )) {
150
+ return filepath .SkipDir
151
+ }
152
+ return nil
153
+ }
154
+
155
+ if ! strings .HasSuffix (path , ".cue" ) {
156
+ return nil
157
+ }
158
+
159
+ return processFile (path )
160
+ })
161
+ exitOnErr (cmd , err , false )
129
162
}
130
163
}
131
164
@@ -139,6 +172,63 @@ func newFmtCmd(c *Command) *cobra.Command {
139
172
140
173
cmd .Flags ().Bool (string (flagCheck ), false , "exits with non-zero status if any files are not formatted" )
141
174
cmd .Flags ().BoolP (string (flagDiff ), "d" , false , "display diffs instead of rewriting files" )
175
+ cmd .Flags ().Bool (string (flagFiles ), false , "treat arguments as file paths to descend into rather than import paths" )
142
176
143
177
return cmd
144
178
}
179
+
180
+ // formatFile formats a single file.
181
+ // It returns true if the file was not well formatted.
182
+ func formatFile (file * build.File , opts []format.Option , cwd string , doDiff , check bool , cmd * Command ) (bool , error ) {
183
+ // We buffer the input and output bytes to compare them.
184
+ // This allows us to determine whether a file is already
185
+ // formatted, without modifying the file.
186
+ src , ok := file .Source .([]byte )
187
+ if ! ok {
188
+ var err error
189
+ src , err = source .ReadAll (file .Filename , file .Source )
190
+ if err != nil {
191
+ return false , err
192
+ }
193
+ }
194
+
195
+ syntax , err := parser .ParseFile (file .Filename , src , parser .ParseComments )
196
+ if err != nil {
197
+ return false , err
198
+ }
199
+
200
+ formatted , err := format .Node (syntax , opts ... )
201
+ if err != nil {
202
+ return false , err
203
+ }
204
+
205
+ stdout := cmd .OutOrStdout ()
206
+ // Always write to stdout if the file is read from stdin.
207
+ if file .Filename == "-" && ! doDiff && ! check {
208
+ stdout .Write (formatted )
209
+ }
210
+
211
+ // File is already well formatted; we can stop here.
212
+ if bytes .Equal (formatted , src ) {
213
+ return false , nil
214
+ }
215
+
216
+ path , err := filepath .Rel (cwd , file .Filename )
217
+ if err != nil {
218
+ path = file .Filename
219
+ }
220
+
221
+ switch {
222
+ case doDiff :
223
+ d := diff .Diff (path + ".orig" , src , path , formatted )
224
+ fmt .Fprintln (stdout , string (d ))
225
+ case check :
226
+ fmt .Fprintln (stdout , path )
227
+ case file .Filename == "-" :
228
+ // already wrote the formatted source to stdout above
229
+ default :
230
+ err := os .WriteFile (file .Filename , formatted , 0644 )
231
+ exitOnErr (cmd , err , false )
232
+ }
233
+ return true , nil
234
+ }
0 commit comments