This is actually pretty tricky to do. I've been using the vim-go plugin for a while*, which has the same problems as described in the question.
However, looking at the source I found that I can set a g:go_fmt_experimental
to enable some code which attempts to fix this.
Here is a modified version which removes some of the Go specific stuff and vim-go utility functions, so it can be reused for other formatters and such. It also avoids having to use a temporary file for the unsaved buffer by using stdin
.
See the comments for details as to how this works. It's also not perfect, since not everything is always restored (again, see the comments). But it looks like this is the best that's possible at the moment.
" Adapted from vim-go: https://github.com/fatih/vim-go/blob/master/LICENSE
"
" we have those problems :
" http://stackoverflow.com/questions/12741977/prevent-vim-from-updating-its-undo-tree
" http://stackoverflow.com/questions/18532692/golang-formatter-and-vim-how-to-destroy-history-record?rq=1
"
" The below function is an improved version that aims to fix all problems.
" it doesn't undo changes and break undo history. If you are here reading
" this and have VimL experience, please look at the function for
" improvements, patches are welcome :)
function! s:write_cmd(cmd) abort
" Using winsaveview to save/restore cursor state has the problem of
" closing folds on save:
" https://github.com/fatih/vim-go/issues/502
" One fix is to use mkview instead. Unfortunately, this sometimes causes
" other bad side effects:
" https://github.com/fatih/vim-go/issues/728
" and still closes all folds if foldlevel>0:
" https://github.com/fatih/vim-go/issues/732
let l:curw = {}
try
mkview!
catch
let l:curw = winsaveview()
endtry
" save our undo file to be restored after we are done. This is needed to
" prevent an additional undo jump due to BufWritePre auto command and also
" restore 'redo' history because it's getting being destroyed every
" BufWritePre
let tmpundofile = tempname()
exe 'wundo! ' . tmpundofile
" Write current unsaved buffer to a temp file
let l:tmpname = tempname()
call writefile(getline(1, '$'), l:tmpname)
if s:isWindows()
let l:tmpname = tr(l:tmpname, '\', '/')
endif
let out = system(a:cmd, getline(1, '$'))[:-2]
if v:shell_error > 0
echoerr out
return
end
try
silent undojoin
catch
" Silently ignore
endtry
" set buffer text
silent 0,$delete
call setline(1, split(out, '\n'))
" restore our undo history
silent! exe 'rundo ' . tmpundofile
call delete(tmpundofile)
" Restore our cursor/windows positions, folds, etc.
if empty(l:curw)
silent! loadview
else
call winrestview(l:curw)
endif
endfunction
" IsWin returns 1 if current OS is Windows or 0 otherwise
function! s:isWindows() abort
let win = ['win16', 'win32', 'win64', 'win95']
for w in win
if (has(w))
return 1
endif
endfor
return 0
endfunction
augroup write_Cmd
autocmd BufWritePre *.go call s:write_cmd('goimports')
augroup end
*: I was apprehensive about using it at first due to bad experiences with some other language plugins, but vim-go is much more sane.
u
didn't undo the last action, then that action had a very undesirable side effect (irreversible change). But it seems that you thinku
being able to undo your change is a side effect...?:set fileencoding=utf16
,:w
, and then pressu
, then I don't "undo" the encoding change, I will undo the last change I made to the buffer. This is similar. To me, thegoimports
command is an unimportant detail in the process of writing the buffer to disk. It's not an "action". I don't care about it. I never need to see it, care about it, or undo it, just like I don't need to see the fileencoding and many other details.:GoImports
which will automatically discards/add import path based on the code. You can set it sogoimports
is ran on save by addinglet g:go_fmt_command = "goimports"
to yourvimrc
file.