@@ -4,60 +4,74 @@ import (
4
4
"crypto/sha256"
5
5
"errors"
6
6
"fmt"
7
+ "go/format"
7
8
"io/ioutil"
8
9
"os"
9
10
"os/exec"
10
11
"sort"
11
12
"strings"
12
13
)
13
14
14
- // ReorderSource reorders the source code in the given filename. It will be helped by the formatCommand (gofmt or goimports). The method is to
15
- // use the Parse() function to extract types, methods and constructors. Then we replace the original source code with a comment containing the
16
- // sha256 of the source. This is made to not lose the original source code "lenght" while we reinject the ordered source code. Then, we finally
15
+ type ReorderConfig struct {
16
+ Filename string
17
+ FormatCommand string
18
+ ReorderStructs bool
19
+ Diff bool
20
+ Src interface {}
21
+ }
22
+
23
+ // ReorderSource reorders the source code in the given filename.
24
+ // It will be helped by the formatCommand (gofmt or goimports).
25
+ // If gofmt is used, the source code will be formatted with the go/fmt package in memory.
26
+ //
27
+ // This function calls the Parse() function to extract types, methods, vars, consts and constructors.
28
+ // Then it replaces the original source code with a comment containing the
29
+ // sha256 of the source. This is made to not lose the original source code "lenght"
30
+ // while we reinject the ordered source code. Then, we finally
17
31
// remove thses lines from the source code.
18
- func ReorderSource (filename , formatCommand string , reorderStructs bool , src interface {}, diff bool ) (string , error ) {
19
- // in all cases, we must return the original source code if an error occurs
20
- // get the content of the file
32
+ func ReorderSource (opt ReorderConfig ) (string , error ) {
21
33
var content []byte
22
34
var err error
23
- if src == nil || len (src .([]byte )) == 0 {
24
- content , err = ioutil .ReadFile (filename )
35
+ if opt . Src == nil || len (opt . Src .([]byte )) == 0 {
36
+ content , err = ioutil .ReadFile (opt . Filename )
25
37
if err != nil {
26
38
return "" , err
27
39
}
28
40
} else {
29
- content = src .([]byte )
41
+ content = opt . Src .([]byte )
30
42
}
31
43
32
- methods , constructors , structs , err := Parse (filename , formatCommand , content )
44
+ info , err := Parse (opt . Filename , content )
33
45
34
46
if err != nil {
35
47
return string (content ), errors .New ("Error parsing source: " + err .Error ())
36
48
}
37
49
38
- if len (structs ) == 0 {
39
- return string (content ), errors .New ("No structs found in " + filename + ", cannot reorder" )
50
+ if len (info . Structs ) == 0 {
51
+ return string (content ), errors .New ("No structs found in " + opt . Filename + ", cannot reorder" )
40
52
}
41
53
42
54
// sort methods by name
43
- for _ , method := range methods {
55
+ for _ , method := range info . Methods {
44
56
sort .Slice (method , func (i , j int ) bool {
45
57
return method [i ].Name < method [j ].Name
46
58
})
47
59
}
48
60
49
- for _ , method := range constructors {
50
- sort .Slice (method , func (i , j int ) bool {
51
- return method [i ].Name < method [j ].Name
61
+ for _ , constructor := range info . Constructors {
62
+ sort .Slice (constructor , func (i , j int ) bool {
63
+ return constructor [i ].Name < constructor [j ].Name
52
64
})
53
65
}
54
66
55
- structNames := make ([]string , 0 , len (methods ))
56
- for _ , s := range structs {
57
- structNames = append (structNames , s . Name )
67
+ functionNames := make ([]string , 0 , len (info . Functions ))
68
+ for functionName := range info . Functions {
69
+ functionNames = append (functionNames , functionName )
58
70
}
59
- if reorderStructs {
60
- sort .Strings (structNames )
71
+ sort .Strings (functionNames )
72
+
73
+ if opt .ReorderStructs {
74
+ info .StructNames .Sort ()
61
75
}
62
76
63
77
// Get the source code signature - we will use this to mark the lines to remove later
@@ -71,37 +85,68 @@ func ReorderSource(filename, formatCommand string, reorderStructs bool, src inte
71
85
72
86
lineNumberWhereInject := 0
73
87
removedLines := 0
74
- for _ , typename := range structNames {
88
+ for i , sourceCode := range info . Constants {
75
89
if removedLines == 0 {
76
- lineNumberWhereInject = structs [typename ].OpeningLine
90
+ lineNumberWhereInject = info .Constants [i ].OpeningLine
91
+ }
92
+ for ln := sourceCode .OpeningLine - 1 ; ln < sourceCode .ClosingLine ; ln ++ {
93
+ originalContent [ln ] = "// -- " + sign
94
+ }
95
+ source = append (source , "\n " + sourceCode .SourceCode )
96
+ removedLines += len (info .Constants )
97
+ }
98
+ for i , sourceCode := range info .Variables {
99
+ if removedLines == 0 {
100
+ lineNumberWhereInject = info .Variables [i ].OpeningLine
101
+ }
102
+ for ln := sourceCode .OpeningLine - 1 ; ln < sourceCode .ClosingLine ; ln ++ {
103
+ originalContent [ln ] = "// -- " + sign
104
+ }
105
+ source = append (source , "\n " + sourceCode .SourceCode )
106
+ removedLines += len (info .Variables )
107
+ }
108
+ for _ , typename := range * info .StructNames {
109
+ if removedLines == 0 {
110
+ lineNumberWhereInject = info .Structs [typename ].OpeningLine
77
111
}
78
112
// replace the definitions by "// -- line to remove
79
- for ln := structs [typename ].OpeningLine - 1 ; ln < structs [typename ].ClosingLine ; ln ++ {
113
+ for ln := info . Structs [typename ].OpeningLine - 1 ; ln < info . Structs [typename ].ClosingLine ; ln ++ {
80
114
originalContent [ln ] = "// -- " + sign
81
115
}
82
- removedLines += structs [typename ].ClosingLine - structs [typename ].OpeningLine
116
+ removedLines += info . Structs [typename ].ClosingLine - info . Structs [typename ].OpeningLine
83
117
// add the struct definition to "source"
84
- source = append (source , "\n \n " + structs [typename ].SourceCode )
118
+ source = append (source , "\n \n " + info . Structs [typename ].SourceCode )
85
119
86
120
// same for constructors
87
- for _ , constructor := range constructors [typename ] {
121
+ for _ , constructor := range info . Constructors [typename ] {
88
122
for ln := constructor .OpeningLine - 1 ; ln < constructor .ClosingLine ; ln ++ {
89
123
originalContent [ln ] = "// -- " + sign
90
124
}
91
125
// add the constructor to "source"
92
126
source = append (source , "\n " + constructor .SourceCode )
93
127
}
94
- removedLines += len (constructors [typename ])
128
+ removedLines += len (info . Constructors [typename ])
95
129
96
130
// same for methods
97
- for _ , method := range methods [typename ] {
131
+ for _ , method := range info . Methods [typename ] {
98
132
for ln := method .OpeningLine - 1 ; ln < method .ClosingLine ; ln ++ {
99
133
originalContent [ln ] = "// -- " + sign
100
134
}
101
135
// add the method to "source"
102
136
source = append (source , "\n " + method .SourceCode )
103
137
}
104
- removedLines += len (methods [typename ])
138
+ removedLines += len (info .Methods [typename ])
139
+ }
140
+ for _ , name := range functionNames {
141
+ sourceCode := info .Functions [name ]
142
+ if removedLines == 0 {
143
+ lineNumberWhereInject = info .Functions [name ].OpeningLine
144
+ }
145
+ for ln := sourceCode .OpeningLine - 1 ; ln < sourceCode .ClosingLine ; ln ++ {
146
+ originalContent [ln ] = "// -- " + sign
147
+ }
148
+ source = append (source , "\n " + sourceCode .SourceCode )
149
+ removedLines += len (info .Functions )
105
150
}
106
151
107
152
// add the "source" at the found lineNumberWhereInject
@@ -118,32 +163,50 @@ func ReorderSource(filename, formatCommand string, reorderStructs bool, src inte
118
163
output := strings .Join (originalContent , "\n " )
119
164
120
165
// write in a temporary file and use "gofmt" to format it
166
+ newcontent := []byte (output )
167
+ switch opt .FormatCommand {
168
+ case "gofmt" :
169
+ // format the temporary file
170
+ newcontent , err = format .Source ([]byte (output ))
171
+ if err != nil {
172
+ return string (content ), errors .New ("Failed to format source: " + err .Error ())
173
+ }
174
+ default :
175
+ if newcontent , err = formatWithCommand (content , output , opt ); err != nil {
176
+ return string (content ), errors .New ("Failed to format source: " + err .Error ())
177
+ }
178
+ }
179
+
180
+ if opt .Diff {
181
+ return doDiff (content , newcontent , opt .Filename )
182
+ }
183
+ return string (newcontent ), nil
184
+ }
185
+
186
+ func formatWithCommand (content []byte , output string , opt ReorderConfig ) (newcontent []byte , err error ) {
187
+ // we use the format command given by the user
188
+ // on a temporary file we need to create and remove
121
189
tmpfile , err := ioutil .TempFile ("" , "" )
122
190
if err != nil {
123
- return string ( content ) , errors .New ("Failed to create temp file: " + err .Error ())
191
+ return content , errors .New ("Failed to create temp file: " + err .Error ())
124
192
}
125
- defer func () {
126
- os .Remove (tmpfile .Name ()) // clean up
127
- tmpfile .Close ()
128
- }()
193
+ defer os .Remove (tmpfile .Name ())
129
194
195
+ // write the temporary file
130
196
if _ , err := tmpfile .Write ([]byte (output )); err != nil {
131
- return string ( content ) , errors .New ("Failed to write to temporary file: " + err .Error ())
197
+ return content , errors .New ("Failed to write temp file: " + err .Error ())
132
198
}
199
+ tmpfile .Close ()
133
200
134
- cmd := exec .Command (formatCommand , "-w" , tmpfile .Name ())
201
+ // format the temporary file
202
+ cmd := exec .Command (opt .FormatCommand , "-w" , tmpfile .Name ())
135
203
if err := cmd .Run (); err != nil {
136
- return string ( content ) , err
204
+ return content , err
137
205
}
138
-
139
206
// read the temporary file
140
- newcontent , err : = ioutil .ReadFile (tmpfile .Name ())
207
+ newcontent , err = ioutil .ReadFile (tmpfile .Name ())
141
208
if err != nil {
142
- return string (content ), errors .New ("Read Temporary File error: " + err .Error ())
143
- }
144
-
145
- if diff {
146
- return doDiff (content , newcontent , filename )
209
+ return content , errors .New ("Read Temporary File error: " + err .Error ())
147
210
}
148
- return string ( newcontent ) , nil
211
+ return newcontent , nil
149
212
}
0 commit comments