Golang Formatter和Vim-如何销毁历史记录?

Go (Golang) programming language comes with a tool called go fmt. Its a code formatter, which formats your code automagically (alignments, alphabetic sorting, tabbing, spacing, idioms...). Its really awesome.

So I've found this little autocommand which utilizes it in Vim, each time buffer is saved to file. au FileType go au BufWritePre <buffer> Fmt Fmt is a function that comes with Go vim plugin.

This is really great, but it has 1 problem. Each time formatter writes to buffer, it creates a jump in undo/redo history. Which becomes very painful when trying to undo/redo changes, since every 2nd change is formatter (making cursor jump to line 1).

So I am wondering, is there any way to discard latest change from undo/redo history after triggering Fmt?

EDIT: Ok, so far I have: au FileType go au BufWritePre <buffer> undojoin | Fmt But its not all good yet. According to :h undojoin, undojoin is not allowed after undo. And sure enough, it fires an error when I try to :w after an undo.

So how do I achieve something like this pseudo-code:

if lastAction != undo then
    au FileType go au BufWritePre <buffer> undojoin | Fmt
end

If I get this last bit figured out, I think I have a solution.

I think this is almost there, accomplishes what you ask, but I see it's deleting one undo point (I think this is expected from undojoin):

function! GoFmt()
    try                
        exe "undojoin"
        exe "Fmt"
    catch              
    endtry
endfunction
au FileType go au BufWritePre <buffer> call GoFmt()

EDIT

Based on MattyW answer I recalled another alternative:

au FileType go au BufWritePre <buffer> %!gofmt

:%!<some command> executes a shell command over the buffer, so I do it before writing it to file. But also, it's gonna put the cursor at top of file...

I just have this in my .vimrc:

au BufWritePost *.go !gofmt -w %

Automatically runs gofmt on the file when I save. It doesn't actually reformat it in the buffer so it doesn't interrupt what I'm looking at, but it's correctly formatted on disk so all check ins are properly formatted. If you want to see the correctly formatted code looks like you can just do :e .

Doesn't do anything to my undo/redo history either

You can install the vim plugins from the default repository. Alternatively, a pathogen friendly mirror is here:

https://github.com/jnwhiteh/vim-golang

Then you can use the :Fmt command to safely do a go fmt!

I attempted to use @pepper_chino's answer but ran into issues where if fmt errors then vim would undo the last change prior to running GoFmt. I worked around this in a long and slightly convoluted way:

" Fmt calls 'go fmt' to convert the file to go's format standards. This being
" run often makes the undo buffer long and difficult to use. This function
" wraps the Fmt function causing it to join the format with the last action.
" This has to have a try/catch since you can't undojoin if the previous
" command was itself an undo.
function! GoFmt()
  " Save cursor/view info.
  let view = winsaveview()

  " Check if Fmt will succeed or not. If it will fail run again to populate location window. If it succeeds then we call it with an undojoin.

  " Copy the file to a temp file and attempt to run gofmt on it
  let TempFile = tempname()
  let SaveModified = &modified
  exe 'w ' . TempFile
  let &modified = SaveModified
  silent exe '! ' . g:gofmt_command . ' ' . TempFile
  call delete(TempFile)

  if v:shell_error
    " Execute Fmt to populate the location window
    silent Fmt
  else
    " Now that we know Fmt will succeed we can now run Fmt with its undo
    " joined to the previous edit in the current buffer
    try                
      silent undojoin | silent Fmt
    catch              
    endtry
  endif
  " Restore the saved cursor/view info.
  call winrestview(view)
endfunction
command! GoFmt call GoFmt()

Here is my go at this. It seems to be working well both with read/write autocmds and bound to a key. It puts the cursor back and doesn't include the top-of-file event in the undos.

function! GoFormatBuffer()
    if &modifiable == 1
        let l:curw=winsaveview()
        let l:tmpname=tempname()
        call writefile(getline(1,'$'), l:tmpname)
        call system("gofmt " . l:tmpname ." > /dev/null 2>&1")
        if v:shell_error == 0
            try | silent undojoin | catch | endtry
            silent %!gofmt -tabwidth=4
        endif
        call delete(l:tmpname)
        call winrestview(l:curw)
    endif
endfunction

I check modifiable because I use vim as my pager.