How to display the total number of changes in Vim’s diff mode?

Okay, here's the best I could come up with myself. This function starts at the top of the current buffer and using the ]c motion it moves through the changes until ]c no longer has an effect. It returns the number of changes (or 0 if the cursor if it is not a diff buffer).

function! CountDiffs()
    let winview = winsaveview() 
    let num_diffs = 0
    if &diff
        let pos = getpos(".")
        keepj sil exe 'normal! G'
        let lnum = 1
        let moved = 1
        while moved
            let startl = line(".")
            keepj sil exe 'normal! [c'
            let moved = line(".") - startl
            if moved
                let num_diffs+=1
            endif
        endwhile
        call winrestview(winview)
        call setpos(".",pos)
    endif
    return num_diffs
endfunction

It seems to work okay, and isn't a noticeable performance hit when included in my statusline.

As for finding the "number" of the current change, here is a function that uses the backward [c motion to count the number of changes before the position of the cursor. The returned value isn't quite right... I think maybe it should only return a number if the cursor is "within" the changed text, rather than after the first line of the change.

function! CurrentDiff()
    if &diff
        let num_diff = 0
        let winview = winsaveview() 
        let pos = getpos(".")
        let moved = 1
        while moved
            let startl = line(".")
            keepj sil exe 'normal! [c'
            let moved = line(".") - startl
            if moved
                let num_diff+=1
            endif
        endwhile
        call winrestview(winview)
        call setpos(".",pos)
        return num_diff
    endif
endfunction

Again, it seems to behave itself in my statusline and doesn't affect the movement of the cursor. The numbers update properly as changes are copied too/from the buffer.

Once the problems have been ironed out I might consider uploading this as a plugin on the Vim website.


Here is a slightly more refined solution. It uses the same technique as my previous answer to count the diffs, but it stores the first line of each hunk in a list asigned to a global variable g:diff_hunks. Then the number of hunks below the cursor can be found by finding the position of the line number in the list. Also notice that I set nocursorbind and noscrollbind and reset them at the end to ensure we don't break mouse scrolling in the diff windows.

function! UpdateDiffHunks()
    setlocal nocursorbind
    setlocal noscrollbind
    let winview = winsaveview() 
    let pos = getpos(".")
    sil exe 'normal! gg'
    let moved = 1
    let hunks = []
    while moved
        let startl = line(".")
        keepjumps sil exe 'normal! ]c'
        let moved = line(".") - startl
        if moved
            call add(hunks,line("."))
        endif
    endwhile
    call winrestview(winview)
    call setpos(".",pos)
    setlocal cursorbind
    setlocal scrollbind
    let g:diff_hunks = hunks
endfunction

The function UpdateDiffHunks() should be updated whenever a diff buffer is modified, but I find it sufficient to map it to CursorMoved and BufEnter.

function! DiffCount()
    if !exists("g:diff_hunks") 
        call UpdateDiffHunks()
    endif
    let n_hunks = 0
    let curline = line(".")
    for hunkline in g:diff_hunks
        if curline < hunkline
            break
        endif
        let n_hunks += 1
    endfor
    return n_hunks . '/' . len(g:diff_hunks)
endfunction

The output of DiffCount() can be used in the statusline, or tied to a command.

Tags:

Vim

Diff