mirror of
https://github.com/airblade/vim-gitgutter.git
synced 2025-11-08 19:43:47 -05:00
Asynchronous diffs in Vim.
This commit is contained in:
@@ -5,7 +5,8 @@ A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows whe
|
|||||||
Features:
|
Features:
|
||||||
|
|
||||||
* Shows signs for added, modified, and removed lines.
|
* Shows signs for added, modified, and removed lines.
|
||||||
* Neovim: runs the diffs asynchronously.
|
* Runs the diffs asynchronously in Vim (7.4.1791+) and NeoVim.
|
||||||
|
- MacVim in GUI mode runs synchronously because it doesn't support Vim's async operations yet ([macvim#272](/macvim-dev/macvim/issues/272)).
|
||||||
* Ensures signs are always as up to date as possible (but without running more than necessary).
|
* Ensures signs are always as up to date as possible (but without running more than necessary).
|
||||||
* Quick jumping between blocks of changed lines ("hunks").
|
* Quick jumping between blocks of changed lines ("hunks").
|
||||||
* Stage/undo/preview individual hunks.
|
* Stage/undo/preview individual hunks.
|
||||||
@@ -202,7 +203,7 @@ You can customise:
|
|||||||
* Whether or not line highlighting is on initially (defaults to off)
|
* Whether or not line highlighting is on initially (defaults to off)
|
||||||
* Whether or not vim-gitgutter runs in "realtime" (defaults to yes)
|
* Whether or not vim-gitgutter runs in "realtime" (defaults to yes)
|
||||||
* Whether or not vim-gitgutter runs eagerly (defaults to yes)
|
* Whether or not vim-gitgutter runs eagerly (defaults to yes)
|
||||||
* Whether or not vim-gitgutter runs asynchronously in NeoVim (defaults to yes)
|
* Whether or not vim-gitgutter runs asynchronously (defaults to yes)
|
||||||
|
|
||||||
Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme.
|
Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme.
|
||||||
|
|
||||||
@@ -317,7 +318,7 @@ Add `let g:gitgutter_highlight_lines = 1` to your `~/.vimrc`.
|
|||||||
|
|
||||||
#### To turn off asynchronous updates
|
#### To turn off asynchronous updates
|
||||||
|
|
||||||
By default diffs are run asynchronously in NeoVim. To run diffs synchronously instead:
|
By default diffs are run asynchronously. To run diffs synchronously instead:
|
||||||
|
|
||||||
```viml
|
```viml
|
||||||
let g:gitgutter_async = 0
|
let g:gitgutter_async = 0
|
||||||
|
|||||||
@@ -34,30 +34,6 @@ function! gitgutter#process_buffer(bufnr, realtime)
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! gitgutter#handle_diff_job(job_id, data, event)
|
|
||||||
call gitgutter#debug#log('job_id: '.a:job_id.', event: '.a: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)
|
function! gitgutter#handle_diff(diff)
|
||||||
call gitgutter#debug#log(a:diff)
|
call gitgutter#debug#log(a:diff)
|
||||||
|
|
||||||
|
|||||||
109
autoload/gitgutter/async.vim
Normal file
109
autoload/gitgutter/async.vim
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
let s:jobs = {}
|
||||||
|
" Async broken on MacVim in GUI mode:
|
||||||
|
" https://github.com/macvim-dev/macvim/issues/272
|
||||||
|
let s:available = has('nvim') || (has('patch-7-4-1791') && !(has('gui_macvim') && has('gui_running')))
|
||||||
|
|
||||||
|
function! gitgutter#async#available()
|
||||||
|
return s:available
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! gitgutter#async#execute(cmd)
|
||||||
|
if has('nvim')
|
||||||
|
let job_id = jobstart([&shell, &shellcmdflag, a:cmd], {
|
||||||
|
\ 'on_stdout': function('gitgutter#async#handle_diff_job_nvim'),
|
||||||
|
\ 'on_stderr': function('gitgutter#async#handle_diff_job_nvim'),
|
||||||
|
\ 'on_exit': function('gitgutter#async#handle_diff_job_nvim')
|
||||||
|
\ })
|
||||||
|
call gitgutter#debug#log('[nvim job: '.job_id.'] '.a:cmd)
|
||||||
|
if job_id < 1
|
||||||
|
throw 'diff failed'
|
||||||
|
endif
|
||||||
|
|
||||||
|
" 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.
|
||||||
|
call s:job_started(job_id)
|
||||||
|
|
||||||
|
else
|
||||||
|
" Pass a handler for stdout but not for stderr so that errors are
|
||||||
|
" ignored (and thus signs are not updated; this assumes that an error
|
||||||
|
" only occurs when a file is not tracked by git).
|
||||||
|
let job = job_start([&shell, &shellcmdflag, a:cmd], {
|
||||||
|
\ 'out_cb': 'gitgutter#async#handle_diff_job_vim'
|
||||||
|
\ })
|
||||||
|
" \ 'close_cb': 'gitgutter#handle_diff_job_vim_close'
|
||||||
|
call gitgutter#debug#log('[vim job: '.string(job_info(job)).'] '.a:cmd)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! gitgutter#async#handle_diff_job_nvim(job_id, data, event)
|
||||||
|
call gitgutter#debug#log('job_id: '.a:job_id.', event: '.a:event)
|
||||||
|
|
||||||
|
if a:event == 'stdout'
|
||||||
|
" a:data is a list
|
||||||
|
call s:job_finished(a:job_id)
|
||||||
|
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 s:is_job_started(a:job_id)
|
||||||
|
call gitgutter#handle_diff("")
|
||||||
|
call s:job_finished(a:job_id)
|
||||||
|
endif
|
||||||
|
|
||||||
|
else " a:event is stderr
|
||||||
|
call gitgutter#hunk#reset()
|
||||||
|
call s:job_finished(a:job_id)
|
||||||
|
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
" Channel is in NL mode.
|
||||||
|
function! gitgutter#async#handle_diff_job_vim(channel, line)
|
||||||
|
call gitgutter#debug#log('channel: '.a:channel.', line: '.a:line)
|
||||||
|
|
||||||
|
" This seems to be the only way to get info about the channel once closed.
|
||||||
|
let channel_id = matchstr(a:channel, '\d\+')
|
||||||
|
|
||||||
|
if a:line ==# 'DETACH' " End of job output
|
||||||
|
call gitgutter#handle_diff(s:job_output(channel_id))
|
||||||
|
call s:job_finished(channel_id)
|
||||||
|
else
|
||||||
|
call s:accumulate_job_output(channel_id, a:line)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:job_started(id)
|
||||||
|
let s:jobs[a:id] = 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:is_job_started(id)
|
||||||
|
return has_key(s:jobs, a:id)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:accumulate_job_output(id, line)
|
||||||
|
if has_key(s:jobs, a:id)
|
||||||
|
let s:jobs[a:id] = add(s:jobs[a:id], a:line)
|
||||||
|
else
|
||||||
|
let s:jobs[a:id] = [a:line]
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Returns a string
|
||||||
|
function! s:job_output(id)
|
||||||
|
if has_key(s:jobs, a:id)
|
||||||
|
return join(s:jobs[a:id], "\n")."\n"
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:job_finished(id)
|
||||||
|
if has_key(s:jobs, a:id)
|
||||||
|
unlet s:jobs[a:id]
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
@@ -125,23 +125,10 @@ function! gitgutter#diff#run_diff(realtime, preserve_full_diff)
|
|||||||
|
|
||||||
let cmd = gitgutter#utility#command_in_directory_of_file(cmd)
|
let cmd = gitgutter#utility#command_in_directory_of_file(cmd)
|
||||||
|
|
||||||
if g:gitgutter_async && has('nvim') && !a:preserve_full_diff
|
if g:gitgutter_async && gitgutter#async#available() && !a:preserve_full_diff
|
||||||
let job_id = jobstart([&shell, '-c', cmd], {
|
call gitgutter#async#execute(cmd)
|
||||||
\ 'on_stdout': function('gitgutter#handle_diff_job'),
|
|
||||||
\ 'on_stderr': function('gitgutter#handle_diff_job'),
|
|
||||||
\ 'on_exit': function('gitgutter#handle_diff_job')
|
|
||||||
\ })
|
|
||||||
call gitgutter#debug#log('[job_id: '.job_id.'] '.cmd)
|
|
||||||
if job_id < 1
|
|
||||||
throw 'diff failed'
|
|
||||||
endif
|
|
||||||
|
|
||||||
" 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.
|
|
||||||
call gitgutter#utility#pending_job(job_id)
|
|
||||||
|
|
||||||
return 'async'
|
return 'async'
|
||||||
|
|
||||||
else
|
else
|
||||||
let diff = gitgutter#utility#system(cmd)
|
let diff = gitgutter#utility#system(cmd)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ let s:file = ''
|
|||||||
let s:using_xolox_shell = -1
|
let s:using_xolox_shell = -1
|
||||||
let s:exit_code = 0
|
let s:exit_code = 0
|
||||||
let s:fish = &shell =~# 'fish'
|
let s:fish = &shell =~# 'fish'
|
||||||
let s:jobs = {}
|
|
||||||
|
|
||||||
function! gitgutter#utility#warn(message)
|
function! gitgutter#utility#warn(message)
|
||||||
echohl WarningMsg
|
echohl WarningMsg
|
||||||
@@ -170,20 +169,6 @@ function! gitgutter#utility#strip_trailing_new_line(line)
|
|||||||
return substitute(a:line, '\n$', '', '')
|
return substitute(a:line, '\n$', '', '')
|
||||||
endfunction
|
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
|
|
||||||
|
|
||||||
function! gitgutter#utility#git_version()
|
function! gitgutter#utility#git_version()
|
||||||
return matchstr(system('git --version'), '[0-9.]\+')
|
return matchstr(system('git --version'), '[0-9.]\+')
|
||||||
endfunction
|
endfunction
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ You can customise:
|
|||||||
- Whether or not line highlighting is on initially (defaults to off)
|
- Whether or not line highlighting is on initially (defaults to off)
|
||||||
- Whether or not vim-gitgutter runs in realtime (defaults to yes)
|
- Whether or not vim-gitgutter runs in realtime (defaults to yes)
|
||||||
- Whether or not vim-gitgutter runs eagerly (defaults to yes)
|
- Whether or not vim-gitgutter runs eagerly (defaults to yes)
|
||||||
- Whether or not vim-gitgutter runs asynchronously in NeoVim (defaults to yes)
|
- Whether or not vim-gitgutter runs asynchronously (defaults to yes)
|
||||||
|
|
||||||
Please note that vim-gitgutter won't override any colours or highlights you've
|
Please note that vim-gitgutter won't override any colours or highlights you've
|
||||||
set in your colorscheme.
|
set in your colorscheme.
|
||||||
@@ -277,7 +277,7 @@ Add to your |vimrc|
|
|||||||
|
|
||||||
TO TURN OFF ASYNCHRONOUS UPDATES
|
TO TURN OFF ASYNCHRONOUS UPDATES
|
||||||
|
|
||||||
By default diffs are run asynchronously in NeoVim. To run diffs synchronously
|
By default diffs are run asynchronously. To run diffs synchronously
|
||||||
instead:
|
instead:
|
||||||
|
|
||||||
Add to your |vimrc|
|
Add to your |vimrc|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ VIM="/Applications/MacVim.app/Contents/MacOS/Vim -v"
|
|||||||
|
|
||||||
# Execute the tests.
|
# Execute the tests.
|
||||||
for testcase in test*.vim; do
|
for testcase in test*.vim; do
|
||||||
$VIM -N -u NONE -S $testcase -c 'quit!'
|
$VIM -N -u NONE --cmd 'let g:gitgutter_async=0' -S $testcase -c 'quit!'
|
||||||
|
|
||||||
git reset HEAD fixture.txt > /dev/null
|
git reset HEAD fixture.txt > /dev/null
|
||||||
git checkout fixture.txt
|
git checkout fixture.txt
|
||||||
|
|||||||
Reference in New Issue
Block a user