2
2
" Use of this source code is governed by a BSD-style
3
3
" license that can be found in the LICENSE file.
4
4
"
5
- " fmt.vim: Vim command to format Go files with gofmt.
6
- "
7
- " This filetype plugin add a new commands for go buffers:
8
- "
9
- " :Fmt
10
- "
11
- " Filter the current Go buffer through gofmt.
12
- " It tries to preserve cursor position and avoids
13
- " replacing the buffer with stderr output.
14
- "
15
- " Options:
16
- "
17
- " g:go_fmt_command [default="gofmt"]
18
- "
19
- " Flag naming the gofmt executable to use.
20
- "
21
- " g:go_fmt_autosave [default=1]
22
- "
23
- " Flag to auto call :Fmt when saved file
24
- "
5
+ " fmt.vim: Vim command to format Go files with gofmt (and gofmt compatible
6
+ " toorls, such as goimports).
25
7
26
8
if ! exists (" g:go_fmt_command" )
27
9
let g: go_fmt_command = " gofmt"
@@ -67,6 +49,13 @@ function! go#fmt#Format(withGoimport) abort
67
49
catch
68
50
let l: curw = winsaveview ()
69
51
endtry
52
+
53
+ " save our undo file to be restored after we are done. This is needed to
54
+ " prevent an additional undo jump due to BufWritePre auto command and also
55
+ " restore 'redo' history because it's getting being destroyed every
56
+ " BufWritePre
57
+ let tmpundofile = tempname ()
58
+ exe ' wundo! ' . tmpundofile
70
59
else
71
60
" Save cursor position and many other things.
72
61
let l: curw = winsaveview ()
@@ -75,43 +64,116 @@ function! go#fmt#Format(withGoimport) abort
75
64
" Write current unsaved buffer to a temp file
76
65
let l: tmpname = tempname ()
77
66
call writefile (getline (1 , ' $' ), l: tmpname )
67
+ if go#util#IsWin ()
68
+ let l: tmpname = tr (l: tmpname , ' \' , ' /' )
69
+ endif
70
+
71
+ let bin_name = g: go_fmt_command
72
+ if a: withGoimport == 1
73
+ let bin_name = g: go_goimports_bin
74
+ endif
75
+
76
+ let out = go#fmt#run (bin_name, l: tmpname , expand (' %' ))
77
+ if go#util#ShellError () == 0
78
+ call s: update_file (l: tmpname , expand (' %' ))
79
+ elseif g: go_fmt_fail_silently == 0
80
+ let errors = s: parse_errors (out)
81
+ call s: show_errors (errors)
82
+ endif
83
+
84
+ " We didn't use the temp file, so clean up
85
+ call delete (l: tmpname )
78
86
79
87
if g: go_fmt_experimental == 1
80
- " save our undo file to be restored after we are done. This is needed to
81
- " prevent an additional undo jump due to BufWritePre auto command and also
82
- " restore 'redo' history because it's getting being destroyed every
83
- " BufWritePre
84
- let tmpundofile = tempname ()
85
- exe ' wundo! ' . tmpundofile
88
+ " restore our undo history
89
+ silent ! exe ' rundo ' . tmpundofile
90
+ call delete (tmpundofile)
91
+
92
+ " Restore our cursor/windows positions, folds, etc.
93
+ if empty (l: curw )
94
+ silent ! loadview
95
+ else
96
+ call winrestview (l: curw )
97
+ endif
98
+ else
99
+ " Restore our cursor/windows positions.
100
+ call winrestview (l: curw )
86
101
endif
102
+ endfunction
87
103
88
- " get the command first so we can test it
89
- let bin_name = g: go_fmt_command
90
- if a: withGoimport == 1
91
- let bin_name = g: go_goimports_bin
104
+ " update_file updates the target file with the given formatted source
105
+ function ! s: update_file (source , target)
106
+ " remove undo point caused via BufWritePre
107
+ try | silent undojoin | catch | endtry
108
+
109
+ let old_fileformat = &fileformat
110
+ if exists (" *getfperm" )
111
+ " save file permissions
112
+ let original_fperm = getfperm (a: target )
92
113
endif
93
114
94
- " check if the user has installed command binary.
95
- " For example if it's goimports, let us check if it's installed,
96
- " if not the user get's a warning via go#path#CheckBinPath()
97
- let bin_path = go#path#CheckBinPath (bin_name)
98
- if empty (bin_path)
99
- return
115
+ call rename (a: source , a: target )
116
+
117
+ " restore file permissions
118
+ if exists (" *setfperm" ) && original_fperm != ' '
119
+ call setfperm (a: target , original_fperm)
100
120
endif
101
121
102
- if bin_name != " gofmt"
122
+ " reload buffer to reflect latest changes
123
+ silent edit !
124
+
125
+ let &fileformat = old_fileformat
126
+ let &syntax = &syntax
127
+
128
+ " clean up previous location list
129
+ let l: listtype = " locationlist"
130
+ call go#list#Clean (l: listtype )
131
+ call go#list#Window (l: listtype )
132
+ endfunction
133
+
134
+ " run runs the gofmt/goimport command for the given source file and returns
135
+ " the the output of the executed command. Target is the real file to be
136
+ " formated.
137
+ function ! go#fmt#run (bin_name, source , target)
138
+ let cmd = s: fmt_cmd (a: bin_name , a: source , a: target )
139
+ if cmd[0 ] == " goimports"
103
140
" change GOPATH too, so goimports can pick up the correct library
104
141
let old_gopath = $GOPATH
105
142
let $GOPATH = go#path#Detect ()
106
143
endif
107
144
108
- " populate the final command with user based fmt options
109
- let command = bin_path . ' -w '
110
- if a: withGoimport != 1
111
- let command = command . g: go_fmt_options
145
+ let command = join (cmd, " " )
146
+
147
+ " execute our command...
148
+ let out = go#util#System (command )
149
+
150
+ if cmd[0 ] == " goimports"
151
+ let $GOPATH = old_gopath
152
+ endif
153
+
154
+ return out
155
+ endfunction
156
+
157
+ " fmt_cmd returns a dict that contains the command to execute gofmt (or
158
+ " goimports). args is dict with
159
+ function ! s: fmt_cmd (bin_name, source , target)
160
+ " check if the user has installed command binary.
161
+ " For example if it's goimports, let us check if it's installed,
162
+ " if not the user get's a warning via go#path#CheckBinPath()
163
+ let bin_path = go#path#CheckBinPath (a: bin_name )
164
+ if empty (bin_path)
165
+ return
112
166
endif
113
167
114
- if bin_name == " goimports"
168
+ " start constructing the command
169
+ let cmd = [bin_path]
170
+ call add (cmd, " -w" )
171
+
172
+ if a: bin_name != " goimports"
173
+ call extend (cmd, split (g: go_fmt_options , " " ))
174
+ else
175
+ " lazy check if goimports support `-srcdir`. We should eventually remove
176
+ " this in the future
115
177
if ! exists (' b:goimports_vendor_compatible' )
116
178
let out = go#util#System (bin_path . " --help" )
117
179
if out !~ " -srcdir"
@@ -124,95 +186,47 @@ function! go#fmt#Format(withGoimport) abort
124
186
if exists (' b:goimports_vendor_compatible' ) && b: goimports_vendor_compatible
125
187
let ssl_save = &shellslash
126
188
set noshellslash
127
- let command = command . ' -srcdir ' . shellescape (expand ( " % :p" ))
189
+ call extend (cmd, [ " -srcdir" , shellescape (fnamemodify ( a: target , " :p" ))] )
128
190
let &shellslash = ssl_save
129
191
endif
130
192
endif
131
193
132
- " execute our command...
133
- if go#util#IsWin ()
134
- let l: tmpname = tr (l: tmpname , ' \' , ' /' )
135
- endif
136
- let out = go#util#System (command . " " . l: tmpname )
137
-
138
- if bin_name != " gofmt"
139
- let $GOPATH = old_gopath
140
- endif
194
+ call add (cmd, a: source )
195
+ return cmd
196
+ endfunction
141
197
142
- let l: listtype = " locationlist"
143
- " if there is no error on the temp file replace the output with the current
144
- " file (if this fails, we can always check the outputs first line with:
145
- " splitted =~ 'package \w\+')
146
- if go#util#ShellError () == 0
147
- " remove undo point caused via BufWritePre
148
- try | silent undojoin | catch | endtry
149
-
150
- " Replace current file with temp file, then reload buffer
151
- let old_fileformat = &fileformat
152
- if exists (" *getfperm" )
153
- " save old file permissions
154
- let original_fperm = getfperm (expand (' %' ))
155
- endif
156
- call rename (l: tmpname , expand (' %' ))
157
- " restore old file permissions
158
- if exists (" *setfperm" ) && original_fperm != ' '
159
- call setfperm (expand (' %' ), original_fperm)
160
- endif
161
- silent edit !
162
- let &fileformat = old_fileformat
163
- let &syntax = &syntax
164
-
165
- " clean up previous location list, but only if it's due to fmt
166
- if exists (' b:got_fmt_error' ) && b: got_fmt_error
167
- let b: got_fmt_error = 0
168
- call go#list#Clean (l: listtype )
169
- call go#list#Window (l: listtype )
170
- endif
171
- elseif g: go_fmt_fail_silently == 0
172
- let splitted = split (out, ' \n' )
173
- " otherwise get the errors and put them to location list
174
- let errors = []
175
- for line in splitted
176
- let tokens = matchlist (line , ' ^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)' )
177
- if ! empty (tokens)
178
- call add (errors, {" filename" : @% ,
179
- \" lnum" : tokens[2],
180
- \" col " : tokens[3],
181
- \" text" : tokens[4]})
182
- endif
183
- endfor
184
- if empty (errors)
185
- % | " Couldn't detect gofmt error format, output errors
186
- endif
187
- if ! empty (errors)
188
- call go#list#Populate (l: listtype , errors, ' Format' )
189
- echohl Error | echomsg " Gofmt returned error" | echohl None
198
+ " parse_errors parses the given errors and returns a list of parsed errors
199
+ function ! s: parse_errors (content) abort
200
+ let splitted = split (a: content , ' \n' )
201
+
202
+ " list of errors to be put into location list
203
+ let errors = []
204
+ for line in splitted
205
+ let tokens = matchlist (line , ' ^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)' )
206
+ if ! empty (tokens)
207
+ call add (errors,{
208
+ \" lnum" : tokens[2],
209
+ \" col " : tokens[3],
210
+ \" text" : tokens[4],
211
+ \ })
190
212
endif
213
+ endfor
191
214
192
- let b: got_fmt_error = 1
193
- call go#list#Window ( l: listtype , len (errors))
215
+ return errors
216
+ endfunction
194
217
195
- " We didn't use the temp file, so clean up
196
- call delete (l: tmpname )
218
+ " show_errors opens a location list and shows the given errors. If the given
219
+ " errors is empty, it closes the the location list
220
+ function ! s: show_errors (errors) abort
221
+ let l: listtype = " locationlist"
222
+ if ! empty (a: errors )
223
+ call go#list#Populate (l: listtype , a: errors , ' Format' )
224
+ echohl Error | echomsg " Gofmt returned error" | echohl None
197
225
endif
198
226
199
- if g: go_fmt_experimental == 1
200
- " restore our undo history
201
- silent ! exe ' rundo ' . tmpundofile
202
- call delete (tmpundofile)
203
- endif
204
-
205
- if g: go_fmt_experimental == 1
206
- " Restore our cursor/windows positions, folds, etc.
207
- if empty (l: curw )
208
- silent ! loadview
209
- else
210
- call winrestview (l: curw )
211
- endif
212
- else
213
- " Restore our cursor/windows positions.
214
- call winrestview (l: curw )
215
- endif
227
+ " this closes the window if there are no errors or it opens
228
+ " it if there is any
229
+ call go#list#Window (l: listtype , len (a: errors ))
216
230
endfunction
217
231
218
232
function ! go#fmt#ToggleFmtAutoSave () abort
@@ -225,4 +239,5 @@ function! go#fmt#ToggleFmtAutoSave() abort
225
239
let g: go_fmt_autosave = 1
226
240
call go#util#EchoProgress (" auto fmt enabled" )
227
241
endfunction
242
+
228
243
" vim: sw = 2 ts = 2 et
0 commit comments