'Run gofmt on vim without plugin
I want to run gofmt on save on vim without installing any plugin. This is what I have tried (inspired by https://gist.github.com/tbrisbout/a91ac3419440cde40c5f54dc32c94427):
function! GoFmt()
let file = expand('%')
silent execute "!gofmt -w " . file
edit!
endfunction
command! GoFmt call GoFmt()
augroup go_autocmd
autocmd BufWritePost *.go GoFmt
augroup END
This just works when there is no format error. However, if the code contains error, it shows error message at the bottom of the screen and (it seems like) it appears on the buffer as text, so the whole code gets broken. Is there an simple way to handle this kind of work on vim?
Solution 1:[1]
Gofmt is both a code formatter and a linter, a design decision that makes it harder than necessary to integrate it with Vim. In practice, it means that things can go three ways:
- your code is perfect, nothing happens,
- your code has formatting issues, it is reformatted,
- your code has syntax issues, a list of errors is output.
For example, we could naively try to use gofmt
for gq
and friends by way of :help 'formatprg'
but the chances of eventually overwritting our code with crap like:
<standard input>:4:2: expected statement, found '.'
<standard input>:5:3: expected '}', found 'EOF'
are too high. Like in your case, we can do u
to undo but that's not fun. I guess we will have to work around gofmt
's bad design.
First step: switch to :help BufWritePre
. We have seen that gofmt
can handle stdin
, which allows us to format the buffer as well as the file. That is handy because formatting the file after it was written writes the file a second time for no good reason and forces us to reload it in Vim… and all that seems wasteful. :help BufWritePost
is best kept for things that don't affect Vim's state.
function! GoFmt()
echomsg "hello"
endfunction
command! GoFmt call GoFmt()
augroup go_autocmd
autocmd BufWritePre *.go GoFmt
augroup END
Second step: filter the whole buffer through gofmt
.
function! GoFmt()
silent %!gofmt
endfunction
- Best case scenario: nothing happens or the buffer is overwritten with the formatted content.
- Worst case scenario: the whole buffer is replaced with an error report.
Third step: "handle" the worst case scenario with a basic undo. If the external command returns an error, we can get it via :help v:shell_error
and do what needs to be done.
function! GoFmt()
silent %!gofmt
if v:shell_error > 0
silent undo
endif
endfunction
Fourth step: try to keep the cursor in place.
function! GoFmt()
let saved_view = winsaveview()
silent %!gofmt
if v:shell_error > 0
silent undo
endif
call winrestview(saved_view)
endfunction
See :help winsaveview()
and :help winrestview()
.
Fifth step: if applicable, create a quickfix list with the errors reported by gofmt
. :help getline()
gives us all the lines of the buffer—therefore all the errors—in a list of which we modify each item so that the file name is the current file name instead of the useless <standard input>
. We give that list to :help :cexpr
to create a quickfix list before undoing the filter.
function! GoFmt()
let saved_view = winsaveview()
silent %!gofmt
if v:shell_error > 0
cexpr getline(1, '$')->map({ idx, val -> val->substitute('<standard input>', expand('%'), '') })
silent undo
endif
call winrestview(saved_view)
endfunction
This step has a bit of a "draw the rest of the * howl" vibe but it really is just a simple :help substitute()
in a simple :help map()
. For the { foo, bar -> baz }
syntax, see :help lambda
.
Sixth and last step: open the quickfix window if there are any valid errors, with :help :cwindow
.
function! GoFmt()
let saved_view = winsaveview()
silent %!gofmt
if v:shell_error > 0
cexpr getline(1, '$')->map({ idx, val -> val->substitute('<standard input>', expand('%'), '') })
silent undo
cwindow
endif
call winrestview(saved_view)
endfunction
Solution 2:[2]
You can update the script as below:
function! GoFmt()
system('gofmt -e -w ' . expand('%'))
edit!
endfunction
Gofmt
is a tool that automatically formats Go source code. Gofmt
is both a code formatter and a linter.
From doc
-e
Print all (including spurious) errors.
-w
Do not print reformatted sources to standard output. If a file's formatting is different from gofmt's, overwrite it with gofmt's version. If an error occurred during overwriting, the original file is restored from an automatic backup.
The above script will update the file if there are no errors
If applicable, create a quickfix list with the errors reported by gofmt.
function! GoFmt()
cexpr system('gofmt -e -w ' . expand('%'))
edit!
endfunction
cexpr
to create a quickfix list read more about it using vim help doc help :cexpr
, system
to run system command read more about it using vim help doc help :system
, expand
Expand wildcards and special keywords into string read more about it using vim help doc help :expand
, %
is a special keyword which refer to current file.
edit!
is used to reload file contents.
Then you can quickly jump over the errors using quickfix list you can learn more about it from this blog
post.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Chandan |