diff --git a/README.mkd b/README.mkd index 2a20e1e..f0d207f 100644 --- a/README.mkd +++ b/README.mkd @@ -5,6 +5,7 @@ A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows whe Features: * Shows signs for added, modified, and removed lines. +* Neovim: runs the diffs asynchronously. * Ensures signs are always as up to date as possible (but without running more than necessary). * Quick jumping between blocks of changed lines ("hunks"). * Stage/revert/preview individual hunks. diff --git a/autoload/gitgutter.vim b/autoload/gitgutter.vim index 825e067..e5d52d1 100644 --- a/autoload/gitgutter.vim +++ b/autoload/gitgutter.vim @@ -20,20 +20,9 @@ function! gitgutter#process_buffer(bufnr, realtime) try if !a:realtime || gitgutter#utility#has_fresh_changes() let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(), 1) - call gitgutter#hunk#set_hunks(gitgutter#diff#parse_diff(diff)) - let modified_lines = gitgutter#diff#process_hunks(gitgutter#hunk#hunks()) - - if len(modified_lines) > g:gitgutter_max_signs - call gitgutter#utility#warn_once('exceeded maximum number of signs (configured by g:gitgutter_max_signs).', 'max_signs') - call gitgutter#sign#clear_signs() - return + if diff != 'async' + call gitgutter#handle_diff(diff) endif - - if g:gitgutter_signs || g:gitgutter_highlight_lines - call gitgutter#sign#update_signs(modified_lines) - endif - - call gitgutter#utility#save_last_seen_change() endif catch /diff failed/ call gitgutter#hunk#reset() @@ -43,6 +32,46 @@ function! gitgutter#process_buffer(bufnr, realtime) endif endfunction + +function! gitgutter#handle_diff_job(job_id, data, event) + if a:event == 'stdout' + " a:data is a list + call gitgutter#utility#job_output_received(a:job_id, 'stdout') + call gitgutter#handle_diff(join(a:data,"\n")."\n") + + elseif a:event == 'exit' + " If the exit event is triggered without a preceding stdout event, + " the diff was empty. + if gitgutter#utility#is_pending_job(a:job_id) + call gitgutter#handle_diff("") + call gitgutter#utility#job_output_received(a:job_id, 'exit') + endif + + else + call gitgutter#hunk#reset() + call gitgutter#utility#job_output_received(a:job_id, 'stderr') + + endif +endfunction + + +function! gitgutter#handle_diff(diff) + call gitgutter#hunk#set_hunks(gitgutter#diff#parse_diff(a:diff)) + let modified_lines = gitgutter#diff#process_hunks(gitgutter#hunk#hunks()) + + if len(modified_lines) > g:gitgutter_max_signs + call gitgutter#utility#warn_once('exceeded maximum number of signs (configured by g:gitgutter_max_signs).', 'max_signs') + call gitgutter#sign#clear_signs() + return + endif + + if g:gitgutter_signs || g:gitgutter_highlight_lines + call gitgutter#sign#update_signs(modified_lines) + endif + + call gitgutter#utility#save_last_seen_change() +endfunction + function! gitgutter#disable() " get list of all buffers (across all tabs) let buflist = [] diff --git a/autoload/gitgutter/diff.vim b/autoload/gitgutter/diff.vim index 6883d9f..d8644b3 100644 --- a/autoload/gitgutter/diff.vim +++ b/autoload/gitgutter/diff.vim @@ -116,23 +116,35 @@ function! gitgutter#diff#run_diff(realtime, use_external_grep) endif end - let diff = gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(cmd)) - - if a:realtime - call delete(blob_file) - call delete(buff_file) - endif - - if gitgutter#utility#shell_error() - " A shell error indicates the file is not tracked by git (unless something bizarre is going on). - throw 'diff failed' - endif - if !tracked call setbufvar(bufnr, 'gitgutter_tracked', 1) endif - return diff + if has('nvim') && a:use_external_grep + let cmd = gitgutter#utility#command_in_directory_of_file(cmd) + " Note that when `cmd` doesn't produce any output, i.e. the diff is empty, + " the `stdout` event is not fired on the job handler. Therefore we keep + " track of the jobs ourselves so we can spot empty diffs. + let job_id = jobstart([&shell, '-c', cmd], { + \ 'on_stdout': function('gitgutter#handle_diff_job'), + \ 'on_stderr': function('gitgutter#handle_diff_job'), + \ 'on_exit': function('gitgutter#handle_diff_job') + \ }) + call gitgutter#utility#pending_job(job_id) + return 'async' + else + let diff = gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(cmd)) + if gitgutter#utility#shell_error() + " A shell error indicates the file is not tracked by git (unless something bizarre is going on). + throw 'diff failed' + endif + return diff + endif + + if a:realtime + " call delete(blob_file) + " call delete(buff_file) + endif endfunction function! gitgutter#diff#parse_diff(diff) diff --git a/autoload/gitgutter/utility.vim b/autoload/gitgutter/utility.vim index e1b2fce..b658119 100644 --- a/autoload/gitgutter/utility.vim +++ b/autoload/gitgutter/utility.vim @@ -2,6 +2,7 @@ let s:file = '' let s:using_xolox_shell = -1 let s:exit_code = 0 let s:fish = &shell =~# 'fish' +let s:jobs = {} function! gitgutter#utility#warn(message) echohl WarningMsg @@ -162,3 +163,17 @@ endfunction function! gitgutter#utility#strip_trailing_new_line(line) return substitute(a:line, '\n$', '', '') endfunction + +function! gitgutter#utility#pending_job(job_id) + let s:jobs[a:job_id] = 1 +endfunction + +function! gitgutter#utility#is_pending_job(job_id) + return has_key(s:jobs, a:job_id) +endfunction + +function! gitgutter#utility#job_output_received(job_id, event) + if has_key(s:jobs, a:job_id) + unlet s:jobs[a:job_id] + endif +endfunction