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.