DanTheMan
DanTheMan

Reputation: 33

vim DIY statusline + git diff --numstat

I'm trying to add the result of the following command to my diy vim status line

git diff --numstat <filename>

I've tried a bunch of methods, all of which have failed, almost the same way. Example:

function! GetGitDiffSummary()
  let l:adds = "+".system("git diff --numstat -- % | awk '{print($1)}'")
  let l:subs = "-".system("git diff --numstat -- % | awk '{print($2)}'")
  return l:adds."/".l:subs
endfunction

And then try to use it like this:

set statusline+=\ \ %{GetGitDiffSummary()}\ \ 

Problem 1: the values themselves don't really show up, I just see +/- without any values. Problem 2: when I press and hold keys that move the cursor (arrow keys & hjkl) I get the respective characters printed out. They don't change the contents of the file, but I still see them.

Quitting and reopening the file doesn't display the weird chars anymore, but if I press and hold up/down/j/k again, I see them again in my file.

Upvotes: 0

Views: 178

Answers (1)

anon
anon

Reputation:

One problem is that the % character only turns into the current buffer in Vim's command-line, but not when evaluated through system. Instead, you should put expand('%') and ideally wrap it in shellescape() to ensure that any special characters are escaped for use in the shell.

Provided you have a recent enough Vim, you should also have the trim( function to clean up some whitespace, so the function might look something like this:

function! GetGitDiffSummary()
  let l:git_command = "git diff --numstat -- " . shellescape(expand('%'))

  let l:adds = "+".trim(system(l:git_command . " | awk '{print($1)}'"))
  let l:subs = "-".trim(system(l:git_command . " | awk '{print($2)}'"))

  return l:adds."/".l:subs
endfunction

Problem 2: when I press and hold keys that move the cursor (arrow keys & hjkl) I get the respective characters printed out. They don't change the contents of the file, but I still see them.

This is a problem with performance. The system call could potentially take a while, and it blocks Vim. It gets invoked every time you move the cursor, which is unnecessary for this particular check -- git will only see a change in lines when you write the buffer. So maybe something like this would work better:

augroup UpdateDiffSummary
  autocmd!

  autocmd BufReadPost * let b:git_diff_summary = GetGitDiffSummary()
  autocmd BufWritePost * let b:git_diff_summary = GetGitDiffSummary()
augroup END

set statusline+=%{get(b:,'git_diff_summary')}

These autocommands should trigger when opening a file and when writing the buffer, which are the two moments when this data would change. The reason to use get(b:, 'git_diff_summary') instead of b:git_diff_summary is to avoid an error if that variable isn't set.

There's other optimizations that could be done -- you could make a single system call instead of two, and it might make sense to check if l:adds and l:subs are the empty string to replace them with 0. But this should work as a start, I think.

Upvotes: 1

Related Questions