Z Space Blog

your source for Game Z

Easy diffing with Vim

Vim has good support for showing the differences between two (or three) files with either the vimdiff command or setting up a diff session manually. This often requires opening the files in their own window panes, then using :diffthis in all the relevant files. If you're in the middle of an editing session, stopping to set up this diff session can cause you to lose focus. I wrote a function in my vimrc that automates this process, so the user can focus on making decisions instead of building their UI or some other flow-breaking process.

" Function: BufDiff
" Author: zlg <zlg+viml@zlg.space>
" Date: 2018-10-12
" License: GNU GPL, version 3 <https://gnu.org/licenses/gpl-3.0.html>
" Description: Open a new tab page with two or three buffers and enter diff mode.
" Long Description:
" BufDiff accepts two or three buffer arguments and opens a new tab page to
" work with them in diff mode. This ensures that your existing window and
" buffer layout is preserved while providing an easy way to enter a diff
" session. The arguments can be any argument that can also be passed to
" the ":buffer" Ex-command, including partial filenames (if quoted).
function! BufDiff(buf1, buf2, ...)
    let fname = substitute(expand("<sfile>"), "^function ", "", "")
    if a:0 > 1
        echohl ErrorMsg
        echomsg l:fname . ": This function accepts a maximum of three arguments."
        echohl None
        return
    endif

    " Check for a third buffer, which will default to zero if not present
    let a:buf3 = get(a:, 1, 0)

    if !bufexists(a:buf1) || !bufexists(a:buf2) || (a:0 == 1 && !bufexists(a:buf3))
        echohl ErrorMsg
        echomsg l:fname . ": One or more buffer number(s) not found!"
        echohl None
        return
    else
        tabnew
        exe ":buffer " . a:buf1
        diffthis
        vsplit
        exe ":buffer " . a:buf2
        diffthis
        if a:buf3
            vsplit
            exe ":buffer " . a:buf3
            diffthis
        endif
        return
    endif
endfunction

BufDiff() allows me to reference two or three open buffers and put them into a separate diff session via a tab page. When I'm done working with the diff session, I can :tabc to close the tabpage and my original session (from the prior tabpage) is still intact, ready for me to continue work.

Using it is simple. Let's say you invoke vim with vim foo.txt bar.txt. You'll end up in Vim with the contents of foo.txt (or an empty buffer ready for writing to said file), with bar.txt in a second buffer. If I do :call BufDiff(1,2), the new tabpage will come up and compare the two files. I could also use :call BufDiff("foo", "bar"), since the arguments get sent to the :buffer Ex-command.

The primary limitation of BufDiff is you need the files you want to compare to be present in the buffer list already. Many people begin working on their projects with a one-liner that opens all the relevant source files, so it's a limitation that I think is bearable.

If you use this function, let me know how it worked out for you! Improvements and suggestions are also welcome.

Shoutout to tockitj from Freenode's #vim channel for the inspiration.