1
0
mirror of https://github.com/amix/vimrc synced 2025-06-23 06:35:01 +08:00

Updated plugins

This commit is contained in:
Amir Salihefendic
2018-03-31 11:56:26 -03:00
parent 7c643a2d9c
commit 02572caa95
84 changed files with 4588 additions and 1749 deletions

View File

@ -0,0 +1,8 @@
> What is the latest commit SHA in your installed vim-gitgutter?
> What vim/nvim version are you on?
> If no signs are showing at all, what does `:echo b:gitgutter.path` give?
> If no signs are showing at all, and the `path` value is a path and not `-2`, does it work with `let g:gitgutter_grep=''`?

View File

@ -1,12 +1,14 @@
## vim-gitgutter
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows whether each line has been added, modified, and where lines have been removed. You can also stage and undo individual hunks.
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows which lines have been added, modified, or removed. You can also preview, stage, and undo individual hunks. The plugin also provides a hunk text object.
The signs are always up to date and the plugin never saves your buffer.
Features:
* Shows signs for added, modified, and removed lines.
* Runs the diffs asynchronously in terminal Vim/MacVim (7.4.1826+), gVim (7.4.1850+), MacVim GUI (7.4.1832+), and NeoVim.
* Ensures signs are always as up to date as possible (but without running more than necessary).
* Runs the diffs asynchronously where possible.
* Ensures signs are always up to date.
* Quick jumping between blocks of changed lines ("hunks").
* Stage/undo/preview individual hunks.
* Provides a hunk text object.
@ -21,9 +23,8 @@ Features:
Constraints:
* Supports git only.
If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify).
* Supports git only. If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify).
* Relies on the `FocusGained` event. If your terminal doesn't report focus events, either use something like [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`.
### Screenshot
@ -39,7 +40,7 @@ In the screenshot above you can see:
### Installation
Before installation, please check your Vim supports signs by running `:echo has('signs')`. `1` means you're all set; `0` means you need to install a Vim with signs support. If you're compiling Vim yourself you need the 'big' or 'huge' feature set. [MacVim][] supports signs.
Before installation, please check your Vim supports signs by running `:echo has('signs')`. `1` means you're all set; `0` means you need to install a Vim with signs support. If you're compiling Vim yourself you need the 'big' or 'huge' feature set. MacVim supports signs.
You install vim-gitgutter like any other vim plugin.
@ -100,16 +101,13 @@ cp -r vim-gitgutter/* ~/.vim/
See `:help add-global-plugin`.
If you are on Windows you may find the command prompt pops up briefly every time vim-gitgutter runs. You can avoid this by installing both [vim-misc](https://github.com/xolox/vim-misc) and [vim-shell](https://github.com/xolox/vim-shell). If you have those two plugins but don't want vim-gitgutter to use them, you can opt out with `let g:gitgutter_avoid_cmd_prompt_on_windows = 0` in your `~/.vimrc`.
### Getting started
When you make a change to a file tracked by git, the diff markers should appear automatically. The delay is governed by vim's `updatetime` option; the default value is 4 seconds but I suggest reducing it to around 250ms (add `set updatetime=250` to your vimrc).
When you make a change to a file tracked by git, the diff markers should appear automatically. The delay is governed by vim's `updatetime` option; the default value is `4000`, i.e. 4 seconds, but I suggest reducing it to around 100ms (add `set updatetime=100` to your vimrc).
You can jump between hunks with `[c` and `]c`. You can preview, stage, and undo hunks with `<leader>hp`, `<leader>hs`, and `<leader>hu` respectively.
You cannot currently unstage a staged hunk.
You cannot unstage a staged hunk.
#### Activation
@ -136,7 +134,7 @@ Note that if you have line highlighting on and signs off, you will have an empty
If you switch off both line highlighting and signs, you won't see the sign column. That is unless you configure the sign column always to be there (see Sign Column section).
To keep your Vim snappy, vim-gitgutter will suppress itself when a file has more than 500 changes. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with:
To keep your Vim snappy, vim-gitgutter will suppress the signs when a file has more than 500 changes. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with:
```viml
let g:gitgutter_max_signs = 500 " default value
@ -205,33 +203,6 @@ Finally, you can force vim-gitgutter to update its signs across all visible buff
See the customisation section below for how to change the defaults.
### When are the signs updated?
By default the signs are updated as follows:
| Event | Reason for update | Configuration |
|---------------------------|--------------------------------------|------------------------|
| Stop typing | So the signs are real time | `g:gitgutter_realtime` |
| Switch buffer | To notice change to git index | `g:gitgutter_eager` |
| Switch tab | To notice change to git index | `g:gitgutter_eager` |
| Focus the GUI | To notice change to git index | `g:gitgutter_eager` (not gVim on Windows) |
| After shell command | To notice change to git index | `g:gitgutter_eager` |
| Read a file into a buffer | To display initial signs | [always] |
| Save a buffer | So non-realtime signs are up to date | [always] |
| Change a file outside Vim | To notice `git stash` | [always] |
The length of time Vim waits after you stop typing before it triggers the plugin is governed by the setting `updatetime`. This defaults to `4000` milliseconds which is rather too long. I recommend around `250` milliseconds but it depends on your system and your preferences. Note that in terminal Vim pre-7.4.427 an `updatetime` of less than approximately `1000` milliseconds can lead to random highlighting glitches; the lower the `updatetime`, the more glitches.
If you experience a lag, you can trade speed for accuracy:
```viml
let g:gitgutter_realtime = 0
let g:gitgutter_eager = 0
```
Note the realtime updating requires Vim 7.3.105 or higher.
### Customisation
You can customise:
@ -351,7 +322,7 @@ If you use an alternative to grep, you can tell vim-gitgutter to use it here.
```viml
" Default:
let g:gitgutter_grep_command = 'grep'
let g:gitgutter_grep = 'grep'
```
#### To turn off vim-gitgutter by default
@ -490,57 +461,53 @@ nmap <silent> [c :call PrevHunkAllBuffers()<CR>
### FAQ
> How can I turn off realtime updates?
Add this to your vim configuration (in an `/after/plugin` directory):
```viml
" .vim/after/plugin/gitgutter.vim
autocmd! gitgutter CursorHold,CursorHoldI
```
> Why can't I unstage staged changes?
Unstaging staged hunks is feasible but not quite as easy as it sounds. There are three relevant versions of a file at any one time:
1. The version at HEAD in the repo.
2. The version staged in the index.
3. The version in the working tree, in your vim buffer.
`git-diff` without arguments shows you how 3 and 2 differ; this is what vim-gitgutter shows too.
`git-diff --staged` shows you how 2 and 1 differ.
Let's say you are looking at a file in vim which has some unstaged changes. Now you stage a hunk, either via vim-gitgutter or another means. The hunk is no longer marked in vim-gitgutter because it is the same in 3 and 2.
Now you want to unstage that hunk. To see it, you need the difference between 2 and 1. For vim-gitgutter to show those differences, it would need to show you 2 instead of 3 in your vim buffer. But 2 is virtual so vim-gitgutter would need to handle it without touching 3.
I intend to implement this but I can't commit to any deadline.
This plugin is for showing changes between the working tree and the index (and staging/undoing those changes). Unstaging a staged hunk would require showing changes between the index and HEAD, which is out of scope.
> Why are the colours in the sign column weird?
Your colorscheme is configuring the `SignColumn` highlight group weirdly. Please see the section above on customising the sign column.
> There's a noticeable lag when vim-gitter runs; how can I avoid it?
By default vim-gitgutter runs often so the signs are as accurate as possible. The delay is governed by `updatetime`; see [above](#when-are-the-signs-updated) for more information.
If you don't want realtime updates and would like to trade a little accuracy for speed, add this to your `~/.vimrc`:
```viml
let g:gitgutter_realtime = 0
let g:gitgutter_eager = 0
```
> What happens if I also use another plugin which uses signs (e.g. Syntastic)?
Vim only allows one sign per line. Before adding a sign to a line, vim-gitgutter checks whether a sign has already been added by somebody else. If so it doesn't do anything. In other words vim-gitgutter won't overwrite another plugin's signs. It also won't remove another plugin's signs.
> Why aren't any signs showing at all?
### Troubleshooting
#### When no signs are showing at all
Here are some things you can check:
* `:echo system("git --version")` succeeds.
* Your git config is compatible with the version of git returned by the command above.
* Your Vim supports signs (`:echo has('signs')` should give `1`).
* Your file is being tracked by git and has unstaged changes.
* If you have aliased or configured `grep` to use any flags, add `let g:gitgutter_grep_command = 'grep'` to your `~/.vimrc`.
* Try adding `let g:gitgutter_grep=''` to your vimrc. If it works, the problem is grep producing non-plain output; e.g. ANSI escape codes or colours.
* Verify `:echo system("git --version")` succeeds.
* Verify your git config is compatible with the version of git returned by the command above.
* Verify your Vim supports signs (`:echo has('signs')` should give `1`).
* Verify your file is being tracked by git and has unstaged changes.
> Why is the whole file marked as added when I edit it?
#### When the whole file is marked as added
* If you use zsh, and you set `CDPATH`, make sure `CDPATH` doesn't include the current directory.
#### When signs take a few seconds to appear
* Try reducing `updatetime`, e.g. `set updatetime=100`.
#### When signs don't update after focusing Vim
* Your terminal probably isn't reporting focus events. Either try installing [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`.
### Shameless Plug
@ -561,4 +528,4 @@ Copyright Andrew Stewart, AirBlade Software Ltd. Released under the MIT licence
[pathogen]: https://github.com/tpope/vim-pathogen
[siv]: http://pluralsight.com/training/Courses/TableOfContents/smash-into-vim
[airblade]: http://airbladesoftware.com/peepcode-vim
[macvim]: http://code.google.com/p/macvim/
[terminus]: https://github.com/wincent/terminus

View File

@ -1,67 +1,54 @@
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
let s:t_string = type('')
" Primary functions {{{
function! gitgutter#all() abort
for buffer_id in gitgutter#utility#dedup(tabpagebuflist())
let file = expand('#' . buffer_id . ':p')
function! gitgutter#all(force) abort
for bufnr in s:uniq(tabpagebuflist())
let file = expand('#'.bufnr.':p')
if !empty(file)
call gitgutter#process_buffer(buffer_id, 0)
call gitgutter#init_buffer(bufnr)
call gitgutter#process_buffer(bufnr, a:force)
endif
endfor
endfunction
" bufnr: (integer) the buffer to process.
" realtime: (boolean) when truthy, do a realtime diff; otherwise do a disk-based diff.
function! gitgutter#process_buffer(bufnr, realtime) abort
call gitgutter#utility#use_known_shell()
call gitgutter#utility#set_buffer(a:bufnr)
if gitgutter#utility#is_active()
if g:gitgutter_sign_column_always
call gitgutter#sign#add_dummy_sign()
" Finds the file's path relative to the repo root.
function! gitgutter#init_buffer(bufnr)
if gitgutter#utility#is_active(a:bufnr)
let p = gitgutter#utility#repo_path(a:bufnr, 0)
if type(p) != s:t_string || empty(p)
call gitgutter#utility#set_repo_path(a:bufnr)
endif
try
if !a:realtime || gitgutter#utility#has_fresh_changes()
let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(), 0)
if diff != 'async'
call gitgutter#handle_diff(diff)
endif
endif
endfunction
function! gitgutter#process_buffer(bufnr, force) abort
" NOTE a:bufnr is not necessarily the current buffer.
if gitgutter#utility#is_active(a:bufnr)
if a:force || s:has_fresh_changes(a:bufnr)
let diff = ''
try
let diff = gitgutter#diff#run_diff(a:bufnr, 0)
catch /gitgutter not tracked/
call gitgutter#debug#log('Not tracked: '.gitgutter#utility#file(a:bufnr))
catch /gitgutter diff failed/
call gitgutter#debug#log('Diff failed: '.gitgutter#utility#file(a:bufnr))
call gitgutter#hunk#reset(a:bufnr)
endtry
if diff != 'async'
call gitgutter#diff#handler(a:bufnr, diff)
endif
catch /diff failed/
call gitgutter#debug#log('diff failed')
call gitgutter#hunk#reset()
endtry
execute "silent doautocmd" s:nomodeline "User GitGutter"
else
call gitgutter#hunk#reset()
endif
call gitgutter#utility#restore_shell()
endif
endif
endfunction
function! gitgutter#handle_diff(diff) abort
call gitgutter#debug#log(a:diff)
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'tracked', 1)
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() abort
" get list of all buffers (across all tabs)
let buflist = []
@ -69,13 +56,10 @@ function! gitgutter#disable() abort
call extend(buflist, tabpagebuflist(i + 1))
endfor
for buffer_id in gitgutter#utility#dedup(buflist)
let file = expand('#' . buffer_id . ':p')
for bufnr in s:uniq(buflist)
let file = expand('#'.bufnr.':p')
if !empty(file)
call gitgutter#utility#set_buffer(buffer_id)
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(1)
call gitgutter#hunk#reset()
call s:clear(bufnr)
endif
endfor
@ -84,7 +68,7 @@ endfunction
function! gitgutter#enable() abort
let g:gitgutter_enabled = 1
call gitgutter#all()
call gitgutter#all(1)
endfunction
function! gitgutter#toggle() abort
@ -97,163 +81,33 @@ endfunction
" }}}
" Line highlights {{{
function! gitgutter#line_highlights_disable() abort
let g:gitgutter_highlight_lines = 0
call gitgutter#highlight#define_sign_line_highlights()
if !g:gitgutter_signs
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(0)
endif
redraw!
function! s:has_fresh_changes(bufnr) abort
return getbufvar(a:bufnr, 'changedtick') != gitgutter#utility#getbufvar(a:bufnr, 'tick')
endfunction
function! gitgutter#line_highlights_enable() abort
let old_highlight_lines = g:gitgutter_highlight_lines
let g:gitgutter_highlight_lines = 1
call gitgutter#highlight#define_sign_line_highlights()
if !old_highlight_lines && !g:gitgutter_signs
call gitgutter#all()
endif
redraw!
function! s:reset_tick(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'tick', 0)
endfunction
function! gitgutter#line_highlights_toggle() abort
if g:gitgutter_highlight_lines
call gitgutter#line_highlights_disable()
else
call gitgutter#line_highlights_enable()
endif
function! s:clear(bufnr)
call gitgutter#sign#clear_signs(a:bufnr)
call gitgutter#sign#remove_dummy_sign(a:bufnr, 1)
call gitgutter#hunk#reset(a:bufnr)
call s:reset_tick(a:bufnr)
endfunction
" }}}
" Signs {{{
function! gitgutter#signs_enable() abort
let old_signs = g:gitgutter_signs
let g:gitgutter_signs = 1
call gitgutter#highlight#define_sign_text_highlights()
if !old_signs && !g:gitgutter_highlight_lines
call gitgutter#all()
endif
endfunction
function! gitgutter#signs_disable() abort
let g:gitgutter_signs = 0
call gitgutter#highlight#define_sign_text_highlights()
if !g:gitgutter_highlight_lines
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(0)
endif
endfunction
function! gitgutter#signs_toggle() abort
if g:gitgutter_signs
call gitgutter#signs_disable()
else
call gitgutter#signs_enable()
endif
endfunction
" }}}
" Hunks {{{
function! gitgutter#stage_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'stage')
call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' apply --cached --unidiff-zero - '), diff_for_hunk)
" refresh gitgutter's view of buffer
silent execute "GitGutter"
endif
silent! call repeat#set("\<Plug>GitGutterStageHunk", -1)<CR>
endif
call gitgutter#utility#restore_shell()
endfunction
function! gitgutter#undo_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'undo')
call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' apply --reverse --unidiff-zero - '), diff_for_hunk)
" reload file preserving screen line position
" CTRL-Y and CTRL-E treat negative counts as positive counts.
let x = line('w0')
silent edit
let y = line('w0')
let z = x - y
if z > 0
execute "normal! ".z."\<C-E>"
else
execute "normal! ".z."\<C-Y>"
if exists('*uniq') " Vim 7.4.218
function! s:uniq(list)
return uniq(sort(a:list))
endfunction
else
function! s:uniq(list)
let processed = []
for e in a:list
if index(processed, e) == -1
call add(processed, e)
endif
endif
silent! call repeat#set("\<Plug>GitGutterUndoHunk", -1)<CR>
endif
call gitgutter#utility#restore_shell()
endfunction
function! gitgutter#preview_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'preview')
silent! wincmd P
if !&previewwindow
noautocmd execute 'bo' &previewheight 'new'
set previewwindow
endif
setlocal noro modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile
execute "%delete_"
call append(0, split(diff_for_hunk, "\n"))
noautocmd wincmd p
endif
endif
call gitgutter#utility#restore_shell()
endfunction
" }}}
endfor
return processed
endfunction
endif

View File

@ -11,10 +11,13 @@ function! gitgutter#async#available()
endfunction
function! gitgutter#async#execute(cmd) abort
function! gitgutter#async#execute(cmd, bufnr, handler) abort
call gitgutter#debug#log('[async] '.a:cmd)
let options = {
\ 'stdoutbuffer': [],
\ 'buffer': gitgutter#utility#bufnr()
\ 'buffer': a:bufnr,
\ 'handler': a:handler
\ }
let command = s:build_command(a:cmd)
@ -58,33 +61,13 @@ function! s:on_stdout_nvim(_job_id, data, _event) dict abort
endfunction
function! s:on_stderr_nvim(_job_id, _data, _event) dict abort
" Backward compatibility for nvim < 0.2.0
if !has('nvim-0.2.0')
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(self.buffer)
if gitgutter#utility#is_active()
call gitgutter#hunk#reset()
endif
call gitgutter#utility#set_buffer(current_buffer)
return
endif
call s:buffer_exec(self.buffer, function('gitgutter#hunk#reset'))
call self.handler.err(self.buffer)
endfunction
function! s:on_exit_nvim(_job_id, _data, _event) dict abort
" Backward compatibility for nvim < 0.2.0
if !has('nvim-0.2.0')
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(self.buffer)
if gitgutter#utility#is_active()
call gitgutter#handle_diff(gitgutter#utility#stringify(self.stdoutbuffer))
endif
call gitgutter#utility#set_buffer(current_buffer)
return
function! s:on_exit_nvim(_job_id, exit_code, _event) dict abort
if !a:exit_code
call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
endif
call s:buffer_exec(self.buffer, function('gitgutter#handle_diff', [gitgutter#utility#stringify(self.stdoutbuffer)]))
endfunction
@ -92,22 +75,15 @@ function! s:on_stdout_vim(_channel, data) dict abort
call add(self.stdoutbuffer, a:data)
endfunction
function! s:on_stderr_vim(_channel, _data) dict abort
call s:buffer_exec(self.buffer, function('gitgutter#hunk#reset'))
function! s:on_stderr_vim(channel, _data) dict abort
call self.handler.err(self.buffer)
try
call ch_close(a:channel) " so close_cb and its 'out' handler are not triggered
catch /E906/
" noop
endtry
endfunction
function! s:on_exit_vim(_channel) dict abort
call s:buffer_exec(self.buffer, function('gitgutter#handle_diff', [gitgutter#utility#stringify(self.stdoutbuffer)]))
endfunction
function! s:buffer_exec(buffer, fn)
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(a:buffer)
if gitgutter#utility#is_active()
call a:fn()
endif
call gitgutter#utility#set_buffer(current_buffer)
call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
endfunction

View File

@ -12,67 +12,67 @@ function! gitgutter#debug#debug()
setlocal bufhidden=delete
setlocal noswapfile
call gitgutter#debug#vim_version()
call gitgutter#debug#separator()
call s:vim_version()
call s:separator()
call gitgutter#debug#git_version()
call gitgutter#debug#separator()
call s:git_version()
call s:separator()
call gitgutter#debug#grep_version()
call gitgutter#debug#separator()
call s:grep_version()
call s:separator()
call gitgutter#debug#option('updatetime')
call gitgutter#debug#option('shell')
call gitgutter#debug#option('shellcmdflag')
call gitgutter#debug#option('shellpipe')
call gitgutter#debug#option('shellquote')
call gitgutter#debug#option('shellredir')
call gitgutter#debug#option('shellslash')
call gitgutter#debug#option('shelltemp')
call gitgutter#debug#option('shelltype')
call gitgutter#debug#option('shellxescape')
call gitgutter#debug#option('shellxquote')
call s:option('updatetime')
call s:option('shell')
call s:option('shellcmdflag')
call s:option('shellpipe')
call s:option('shellquote')
call s:option('shellredir')
call s:option('shellslash')
call s:option('shelltemp')
call s:option('shelltype')
call s:option('shellxescape')
call s:option('shellxquote')
endfunction
function! gitgutter#debug#separator()
call gitgutter#debug#output('')
function! s:separator()
call s:output('')
endfunction
function! gitgutter#debug#vim_version()
function! s:vim_version()
redir => version_info
silent execute 'version'
redir END
call gitgutter#debug#output(split(version_info, '\n')[0:2])
call s:output(split(version_info, '\n')[0:2])
endfunction
function! gitgutter#debug#git_version()
function! s:git_version()
let v = system(g:gitgutter_git_executable.' --version')
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
call s:output( substitute(v, '\n$', '', '') )
endfunction
function! gitgutter#debug#grep_version()
function! s:grep_version()
let v = system('grep --version')
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
call s:output( substitute(v, '\n$', '', '') )
let v = system('grep --help')
call gitgutter#debug#output( substitute(v, '\%x00', '', 'g') )
call s:output( substitute(v, '\%x00', '', 'g') )
endfunction
function! gitgutter#debug#option(name)
function! s:option(name)
if exists('+' . a:name)
let v = eval('&' . a:name)
call gitgutter#debug#output(a:name . '=' . v)
call s:output(a:name . '=' . v)
" redir => output
" silent execute "verbose set " . a:name . "?"
" redir END
" call gitgutter#debug#output(a:name . '=' . output)
" call s:output(a:name . '=' . output)
else
call gitgutter#debug#output(a:name . ' [n/a]')
call s:output(a:name . ' [n/a]')
end
endfunction
function! gitgutter#debug#output(text)
function! s:output(text)
call append(line('$'), a:text)
endfunction

View File

@ -1,152 +1,176 @@
if exists('g:gitgutter_grep_command')
let s:grep_available = 1
let s:grep_command = g:gitgutter_grep_command
else
let s:grep_available = executable('grep')
if s:grep_available
let s:grep_command = 'grep'
if $GREP_OPTIONS =~# '--color=always'
let s:grep_command .= ' --color=never'
endif
endif
endif
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
let s:c_flag = gitgutter#utility#git_supports_command_line_config_override()
" True for git v1.7.2+.
function! s:git_supports_command_line_config_override() abort
call system(g:gitgutter_git_executable.' -c foo.bar=baz --version')
return !v:shell_error
endfunction
let s:c_flag = s:git_supports_command_line_config_override()
let s:temp_index = tempname()
let s:temp_buffer = tempname()
" Returns a diff of the buffer.
"
" The way to get the diff depends on whether the buffer is saved or unsaved.
" The buffer contents is not the same as the file on disk so we need to pass
" two instances of the file to git-diff:
"
" * Saved: the buffer contents is the same as the file on disk in the working
" tree so we simply do:
" git diff myfileA myfileB
"
" git diff myfile
" where myfileA comes from
"
" * Unsaved: the buffer contents is not the same as the file on disk so we
" need to pass two instances of the file to git-diff:
" git show :myfile > myfileA
"
" git diff myfileA myfileB
"
" The first instance is the file in the index which we obtain with:
"
" git show :myfile > myfileA
"
" The second instance is the buffer contents. Ideally we would pass this to
" git-diff on stdin via the second argument to vim's system() function.
" Unfortunately git-diff does not do CRLF conversion for input received on
" stdin, and git-show never performs CRLF conversion, so repos with CRLF
" conversion report that every line is modified due to mismatching EOLs.
"
" Instead, we write the buffer contents to a temporary file - myfileB in this
" example. Note the file extension must be preserved for the CRLF
" conversion to work.
"
" Before diffing a buffer for the first time, we check whether git knows about
" the file:
"
" git ls-files --error-unmatch myfile
" and myfileB is the buffer contents.
"
" After running the diff we pass it through grep where available to reduce
" subsequent processing by the plugin. If grep is not available the plugin
" does the filtering instead.
function! gitgutter#diff#run_diff(realtime, preserve_full_diff) abort
"
"
" Regarding line endings:
"
" git-show does not convert line endings.
" git-diff FILE FILE does convert line endings for the given files.
"
" If a file has CRLF line endings and git's core.autocrlf is true,
" the file in git's object store will have LF line endings. Writing
" it out via git-show will produce a file with LF line endings.
"
" If this last file is one of the files passed to git-diff, git-diff will
" convert its line endings to CRLF before diffing -- which is what we want --
" but also by default output a warning on stderr.
"
" warning: LF will be replace by CRLF in <temp file>.
" The file will have its original line endings in your working directory.
"
" When running the diff asynchronously, the warning message triggers the stderr
" callbacks which assume the overall command has failed and reset all the
" signs. As this is not what we want, and we can safely ignore the warning,
" we turn it off by passing the '-c "core.safecrlf=false"' argument to
" git-diff.
"
" When writing the temporary files we preserve the original file's extension
" so that repos using .gitattributes to control EOL conversion continue to
" convert correctly.
function! gitgutter#diff#run_diff(bufnr, preserve_full_diff) abort
while gitgutter#utility#repo_path(a:bufnr, 0) == -1
sleep 5m
endwhile
if gitgutter#utility#repo_path(a:bufnr, 0) == -2
throw 'gitgutter not tracked'
endif
" Wrap compound commands in parentheses to make Windows happy.
" bash doesn't mind the parentheses.
let cmd = '('
let bufnr = gitgutter#utility#bufnr()
let tracked = gitgutter#utility#getbufvar(bufnr, 'tracked', 0) " i.e. tracked by git
if !tracked
" Don't bother trying to realtime-diff an untracked file.
" NOTE: perhaps we should pull this guard up to the caller?
if a:realtime
throw 'diff failed'
else
let cmd .= g:gitgutter_git_executable.' ls-files --error-unmatch '.gitgutter#utility#shellescape(gitgutter#utility#filename()).' && ('
endif
" Append buffer number to avoid race conditions between writing and reading
" the files when asynchronously processing multiple buffers.
"
" Without the buffer number, index_file would have a race in the shell
" between the second process writing it (with git-show) and the first
" reading it (with git-diff).
let index_file = s:temp_index.'.'.a:bufnr
" Without the buffer number, buff_file would have a race between the
" second gitgutter#process_buffer() writing the file (synchronously, below)
" and the first gitgutter#process_buffer()'s async job reading it (with
" git-diff).
let buff_file = s:temp_buffer.'.'.a:bufnr
let extension = gitgutter#utility#extension(a:bufnr)
if !empty(extension)
let index_file .= '.'.extension
let buff_file .= '.'.extension
endif
if a:realtime
let blob_name = g:gitgutter_diff_base.':'.gitgutter#utility#shellescape(gitgutter#utility#file_relative_to_repo_root())
let blob_file = s:temp_index
let buff_file = s:temp_buffer
let extension = gitgutter#utility#extension()
if !empty(extension)
let blob_file .= '.'.extension
let buff_file .= '.'.extension
endif
let cmd .= g:gitgutter_git_executable.' show '.blob_name.' > '.blob_file.' && '
" Write file from index to temporary file.
let index_name = g:gitgutter_diff_base.':'.gitgutter#utility#repo_path(a:bufnr, 1)
let cmd .= g:gitgutter_git_executable.' --no-pager show '.index_name.' > '.index_file.' && '
" Writing the whole buffer resets the '[ and '] marks and also the
" 'modified' flag (if &cpoptions includes '+'). These are unwanted
" side-effects so we save and restore the values ourselves.
let modified = getbufvar(bufnr, "&mod")
let op_mark_start = getpos("'[")
let op_mark_end = getpos("']")
" Write buffer to temporary file.
" Note: this is synchronous.
call s:write_buffer(a:bufnr, buff_file)
let current_buffer = bufnr('')
execute 'buffer '.bufnr
execute 'keepalt noautocmd silent write!' buff_file
execute 'buffer '.current_buffer
call setbufvar(bufnr, "&mod", modified)
call setpos("'[", op_mark_start)
call setpos("']", op_mark_end)
endif
let cmd .= g:gitgutter_git_executable
" Call git-diff with the temporary files.
let cmd .= g:gitgutter_git_executable.' --no-pager'
if s:c_flag
let cmd .= ' -c "diff.autorefreshindex=0"'
let cmd .= ' -c "diff.noprefix=false"'
let cmd .= ' -c "core.safecrlf=false"'
endif
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' '
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' -- '.index_file.' '.buff_file
if a:realtime
let cmd .= ' -- '.blob_file.' '.buff_file
else
let cmd .= g:gitgutter_diff_base.' -- '.gitgutter#utility#shellescape(gitgutter#utility#filename())
" Pipe git-diff output into grep.
if !a:preserve_full_diff && !empty(g:gitgutter_grep)
let cmd .= ' | '.g:gitgutter_grep.' '.gitgutter#utility#shellescape('^@@ ')
endif
if !a:preserve_full_diff && s:grep_available
let cmd .= ' | '.s:grep_command.' '.gitgutter#utility#shellescape('^@@ ')
endif
if (!a:preserve_full_diff && s:grep_available) || a:realtime
" grep exits with 1 when no matches are found; diff exits with 1 when
" differences are found. However we want to treat non-matches and
" differences as non-erroneous behaviour; so we OR the command with one
" which always exits with success (0).
let cmd .= ' || exit 0'
endif
" grep exits with 1 when no matches are found; git-diff exits with 1 when
" differences are found. However we want to treat non-matches and
" differences as non-erroneous behaviour; so we OR the command with one
" which always exits with success (0).
let cmd .= ' || exit 0'
let cmd .= ')'
if !tracked
let cmd .= ')'
endif
let cmd = gitgutter#utility#cd_cmd(a:bufnr, cmd)
let cmd = gitgutter#utility#command_in_directory_of_file(cmd)
if g:gitgutter_async && gitgutter#async#available() && !a:preserve_full_diff
call gitgutter#async#execute(cmd)
if g:gitgutter_async && gitgutter#async#available()
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': function('gitgutter#diff#handler'),
\ 'err': function('gitgutter#hunk#reset'),
\ })
return 'async'
else
let diff = gitgutter#utility#system(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'
if v:shell_error
call gitgutter#debug#log(diff)
throw 'gitgutter diff failed'
endif
return diff
endif
endfunction
function! gitgutter#diff#handler(bufnr, diff) abort
call gitgutter#debug#log(a:diff)
call gitgutter#hunk#set_hunks(a:bufnr, gitgutter#diff#parse_diff(a:diff))
let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr))
let signs_count = len(modified_lines)
if signs_count > g:gitgutter_max_signs
call gitgutter#utility#warn_once(a:bufnr, printf(
\ 'exceeded maximum number of signs (%d > %d, configured by g:gitgutter_max_signs).',
\ signs_count, g:gitgutter_max_signs), 'max_signs')
call gitgutter#sign#clear_signs(a:bufnr)
else
if g:gitgutter_signs || g:gitgutter_highlight_lines
call gitgutter#sign#update_signs(a:bufnr, modified_lines)
endif
endif
call s:save_last_seen_change(a:bufnr)
if exists('#User#GitGutter')
let g:gitgutter_hook_context = {'bufnr': a:bufnr}
execute 'doautocmd' s:nomodeline 'User GitGutter'
unlet g:gitgutter_hook_context
endif
endfunction
function! gitgutter#diff#parse_diff(diff) abort
let hunks = []
for line in split(a:diff, '\n')
@ -171,69 +195,71 @@ function! gitgutter#diff#parse_hunk(line) abort
end
endfunction
function! gitgutter#diff#process_hunks(hunks) abort
" This function is public so it may be used by other plugins
" e.g. vim-signature.
function! gitgutter#diff#process_hunks(bufnr, hunks) abort
let modified_lines = []
for hunk in a:hunks
call extend(modified_lines, gitgutter#diff#process_hunk(hunk))
call extend(modified_lines, s:process_hunk(a:bufnr, hunk))
endfor
return modified_lines
endfunction
" Returns [ [<line_number (number)>, <name (string)>], ...]
function! gitgutter#diff#process_hunk(hunk) abort
function! s:process_hunk(bufnr, hunk) abort
let modifications = []
let from_line = a:hunk[0]
let from_count = a:hunk[1]
let to_line = a:hunk[2]
let to_count = a:hunk[3]
if gitgutter#diff#is_added(from_count, to_count)
call gitgutter#diff#process_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(to_count)
if s:is_added(from_count, to_count)
call s:process_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(a:bufnr, to_count)
elseif gitgutter#diff#is_removed(from_count, to_count)
call gitgutter#diff#process_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_removed(from_count)
elseif s:is_removed(from_count, to_count)
call s:process_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count)
elseif gitgutter#diff#is_modified(from_count, to_count)
call gitgutter#diff#process_modified(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count)
elseif s:is_modified(from_count, to_count)
call s:process_modified(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)
elseif gitgutter#diff#is_modified_and_added(from_count, to_count)
call gitgutter#diff#process_modified_and_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(to_count - from_count)
call gitgutter#hunk#increment_lines_modified(from_count)
elseif s:is_modified_and_added(from_count, to_count)
call s:process_modified_and_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(a:bufnr, to_count - from_count)
call gitgutter#hunk#increment_lines_modified(a:bufnr, from_count)
elseif gitgutter#diff#is_modified_and_removed(from_count, to_count)
call gitgutter#diff#process_modified_and_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count)
call gitgutter#hunk#increment_lines_removed(from_count - to_count)
elseif s:is_modified_and_removed(from_count, to_count)
call s:process_modified_and_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)
call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count - to_count)
endif
return modifications
endfunction
function! gitgutter#diff#is_added(from_count, to_count) abort
function! s:is_added(from_count, to_count) abort
return a:from_count == 0 && a:to_count > 0
endfunction
function! gitgutter#diff#is_removed(from_count, to_count) abort
function! s:is_removed(from_count, to_count) abort
return a:from_count > 0 && a:to_count == 0
endfunction
function! gitgutter#diff#is_modified(from_count, to_count) abort
function! s:is_modified(from_count, to_count) abort
return a:from_count > 0 && a:to_count > 0 && a:from_count == a:to_count
endfunction
function! gitgutter#diff#is_modified_and_added(from_count, to_count) abort
function! s:is_modified_and_added(from_count, to_count) abort
return a:from_count > 0 && a:to_count > 0 && a:from_count < a:to_count
endfunction
function! gitgutter#diff#is_modified_and_removed(from_count, to_count) abort
function! s:is_modified_and_removed(from_count, to_count) abort
return a:from_count > 0 && a:to_count > 0 && a:from_count > a:to_count
endfunction
function! gitgutter#diff#process_added(modifications, from_count, to_count, to_line) abort
function! s:process_added(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:to_count
let line_number = a:to_line + offset
@ -242,7 +268,7 @@ function! gitgutter#diff#process_added(modifications, from_count, to_count, to_l
endwhile
endfunction
function! gitgutter#diff#process_removed(modifications, from_count, to_count, to_line) abort
function! s:process_removed(modifications, from_count, to_count, to_line) abort
if a:to_line == 0
call add(a:modifications, [1, 'removed_first_line'])
else
@ -250,7 +276,7 @@ function! gitgutter#diff#process_removed(modifications, from_count, to_count, to
endif
endfunction
function! gitgutter#diff#process_modified(modifications, from_count, to_count, to_line) abort
function! s:process_modified(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:to_count
let line_number = a:to_line + offset
@ -259,7 +285,7 @@ function! gitgutter#diff#process_modified(modifications, from_count, to_count, t
endwhile
endfunction
function! gitgutter#diff#process_modified_and_added(modifications, from_count, to_count, to_line) abort
function! s:process_modified_and_added(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:from_count
let line_number = a:to_line + offset
@ -273,7 +299,7 @@ function! gitgutter#diff#process_modified_and_added(modifications, from_count, t
endwhile
endfunction
function! gitgutter#diff#process_modified_and_removed(modifications, from_count, to_count, to_line) abort
function! s:process_modified_and_removed(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:to_count
let line_number = a:to_line + offset
@ -283,28 +309,14 @@ function! gitgutter#diff#process_modified_and_removed(modifications, from_count,
let a:modifications[-1] = [a:to_line + offset - 1, 'modified_removed']
endfunction
" Generates a zero-context diff for the current hunk.
"
" diff - the full diff for the buffer
" type - stage | undo | preview
function! gitgutter#diff#generate_diff_for_hunk(diff, type) abort
let diff_for_hunk = gitgutter#diff#discard_hunks(a:diff, a:type == 'stage' || a:type == 'undo')
if a:type == 'stage' || a:type == 'undo'
let diff_for_hunk = gitgutter#diff#adjust_hunk_summary(diff_for_hunk, a:type == 'stage')
endif
return diff_for_hunk
endfunction
" Returns the diff with all hunks discarded except the current.
"
" diff - the diff to process
" keep_header - truthy to keep the diff header and hunk summary, falsy to discard it
function! gitgutter#diff#discard_hunks(diff, keep_header) abort
" Returns a diff for the current hunk.
function! gitgutter#diff#hunk_diff(bufnr, full_diff)
let modified_diff = []
let keep_line = a:keep_header
for line in split(a:diff, '\n')
let keep_line = 1
" Don't keepempty when splitting because the diff we want may not be the
" final one. Instead add trailing NL at end of function.
for line in split(a:full_diff, '\n')
let hunk_info = gitgutter#diff#parse_hunk(line)
if len(hunk_info) == 4 " start of new hunk
let keep_line = gitgutter#hunk#cursor_in_hunk(hunk_info)
@ -313,36 +325,32 @@ function! gitgutter#diff#discard_hunks(diff, keep_header) abort
call add(modified_diff, line)
endif
endfor
return join(modified_diff, "\n")."\n"
endfunction
if a:keep_header
return gitgutter#utility#stringify(modified_diff)
else
" Discard hunk summary too.
return gitgutter#utility#stringify(modified_diff[1:])
function! s:write_buffer(bufnr, file)
let bufcontents = getbufline(a:bufnr, 1, '$')
if getbufvar(a:bufnr, '&fileformat') ==# 'dos'
call map(bufcontents, 'v:val."\r"')
endif
let fenc = getbufvar(a:bufnr, '&fileencoding')
if fenc !=# &encoding
call map(bufcontents, 'iconv(v:val, &encoding, "'.fenc.'")')
endif
if getbufvar(a:bufnr, '&bomb')
let bufcontents[0]=''.bufcontents[0]
endif
call writefile(bufcontents, a:file)
endfunction
" Adjust hunk summary (from's / to's line number) to ignore changes above/before this one.
"
" diff_for_hunk - a diff containing only the hunk of interest
" staging - truthy if the hunk is to be staged, falsy if it is to be undone
"
" TODO: push this down to #discard_hunks?
function! gitgutter#diff#adjust_hunk_summary(diff_for_hunk, staging) abort
let line_adjustment = gitgutter#hunk#line_adjustment_for_current_hunk()
let adj_diff = []
for line in split(a:diff_for_hunk, '\n')
if match(line, s:hunk_re) != -1
if a:staging
" increment 'to' line number
let line = substitute(line, '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '')
else
" decrement 'from' line number
let line = substitute(line, '-\@<=\(\d\+\)', '\=submatch(1)-line_adjustment', '')
endif
endif
call add(adj_diff, line)
endfor
return gitgutter#utility#stringify(adj_diff)
function! s:save_last_seen_change(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'tick', getbufvar(a:bufnr, 'changedtick'))
endfunction

View File

@ -1,3 +1,37 @@
function! gitgutter#highlight#line_disable() abort
let g:gitgutter_highlight_lines = 0
call s:define_sign_line_highlights()
if !g:gitgutter_signs
call gitgutter#sign#clear_signs(bufnr(''))
call gitgutter#sign#remove_dummy_sign(bufnr(''), 0)
endif
redraw!
endfunction
function! gitgutter#highlight#line_enable() abort
let old_highlight_lines = g:gitgutter_highlight_lines
let g:gitgutter_highlight_lines = 1
call s:define_sign_line_highlights()
if !old_highlight_lines && !g:gitgutter_signs
call gitgutter#all(1)
endif
redraw!
endfunction
function! gitgutter#highlight#line_toggle() abort
if g:gitgutter_highlight_lines
call gitgutter#highlight#line_disable()
else
call gitgutter#highlight#line_enable()
endif
endfunction
function! gitgutter#highlight#define_sign_column_highlight() abort
if g:gitgutter_override_sign_column_highlight
highlight! link SignColumn LineNr
@ -7,7 +41,7 @@ function! gitgutter#highlight#define_sign_column_highlight() abort
endfunction
function! gitgutter#highlight#define_highlights() abort
let [guibg, ctermbg] = gitgutter#highlight#get_background_colors('SignColumn')
let [guibg, ctermbg] = s:get_background_colors('SignColumn')
" Highlights used by the signs.
@ -42,12 +76,12 @@ function! gitgutter#highlight#define_signs() abort
sign define GitGutterLineModifiedRemoved
sign define GitGutterDummy
call gitgutter#highlight#define_sign_text()
call s:define_sign_text()
call gitgutter#highlight#define_sign_text_highlights()
call gitgutter#highlight#define_sign_line_highlights()
call s:define_sign_line_highlights()
endfunction
function! gitgutter#highlight#define_sign_text() abort
function! s:define_sign_text() abort
execute "sign define GitGutterLineAdded text=" . g:gitgutter_sign_added
execute "sign define GitGutterLineModified text=" . g:gitgutter_sign_modified
execute "sign define GitGutterLineRemoved text=" . g:gitgutter_sign_removed
@ -75,7 +109,7 @@ function! gitgutter#highlight#define_sign_text_highlights() abort
endif
endfunction
function! gitgutter#highlight#define_sign_line_highlights() abort
function! s:define_sign_line_highlights() abort
if g:gitgutter_highlight_lines
sign define GitGutterLineAdded linehl=GitGutterAddLine
sign define GitGutterLineModified linehl=GitGutterChangeLine
@ -91,22 +125,22 @@ function! gitgutter#highlight#define_sign_line_highlights() abort
endif
endfunction
function! gitgutter#highlight#get_background_colors(group) abort
function! s:get_background_colors(group) abort
redir => highlight
silent execute 'silent highlight ' . a:group
redir END
let link_matches = matchlist(highlight, 'links to \(\S\+\)')
if len(link_matches) > 0 " follow the link
return gitgutter#highlight#get_background_colors(link_matches[1])
return s:get_background_colors(link_matches[1])
endif
let ctermbg = gitgutter#highlight#match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)')
let guibg = gitgutter#highlight#match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)')
let ctermbg = s:match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)')
let guibg = s:match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)')
return [guibg, ctermbg]
endfunction
function! gitgutter#highlight#match_highlight(highlight, pattern) abort
function! s:match_highlight(highlight, pattern) abort
let matches = matchlist(a:highlight, a:pattern)
if len(matches) == 0
return 'NONE'

View File

@ -1,15 +1,15 @@
function! gitgutter#hunk#set_hunks(hunks) abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'hunks', a:hunks)
call s:reset_summary()
function! gitgutter#hunk#set_hunks(bufnr, hunks) abort
call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks)
call s:reset_summary(a:bufnr)
endfunction
function! gitgutter#hunk#hunks() abort
return gitgutter#utility#getbufvar(gitgutter#utility#bufnr(), 'hunks', [])
function! gitgutter#hunk#hunks(bufnr) abort
return gitgutter#utility#getbufvar(a:bufnr, 'hunks', [])
endfunction
function! gitgutter#hunk#reset() abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'hunks', [])
call s:reset_summary()
function! gitgutter#hunk#reset(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'hunks', [])
call s:reset_summary(a:bufnr)
endfunction
@ -17,37 +17,35 @@ function! gitgutter#hunk#summary(bufnr) abort
return gitgutter#utility#getbufvar(a:bufnr, 'summary', [0,0,0])
endfunction
function! s:reset_summary() abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'summary', [0,0,0])
function! s:reset_summary(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'summary', [0,0,0])
endfunction
function! gitgutter#hunk#increment_lines_added(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
function! gitgutter#hunk#increment_lines_added(bufnr, count) abort
let summary = gitgutter#hunk#summary(a:bufnr)
let summary[0] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary)
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction
function! gitgutter#hunk#increment_lines_modified(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
function! gitgutter#hunk#increment_lines_modified(bufnr, count) abort
let summary = gitgutter#hunk#summary(a:bufnr)
let summary[1] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary)
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction
function! gitgutter#hunk#increment_lines_removed(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
function! gitgutter#hunk#increment_lines_removed(bufnr, count) abort
let summary = gitgutter#hunk#summary(a:bufnr)
let summary[2] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary)
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction
function! gitgutter#hunk#next_hunk(count) abort
if gitgutter#utility#is_active()
let bufnr = bufnr('')
if gitgutter#utility#is_active(bufnr)
let current_line = line('.')
let hunk_count = 0
for hunk in gitgutter#hunk#hunks()
for hunk in gitgutter#hunk#hunks(bufnr)
if hunk[2] > current_line
let hunk_count += 1
if hunk_count == a:count
@ -61,10 +59,11 @@ function! gitgutter#hunk#next_hunk(count) abort
endfunction
function! gitgutter#hunk#prev_hunk(count) abort
if gitgutter#utility#is_active()
let bufnr = bufnr('')
if gitgutter#utility#is_active(bufnr)
let current_line = line('.')
let hunk_count = 0
for hunk in reverse(copy(gitgutter#hunk#hunks()))
for hunk in reverse(copy(gitgutter#hunk#hunks(bufnr)))
if hunk[2] < current_line
let hunk_count += 1
if hunk_count == a:count
@ -80,10 +79,11 @@ endfunction
" Returns the hunk the cursor is currently in or an empty list if the cursor
" isn't in a hunk.
function! gitgutter#hunk#current_hunk() abort
function! s:current_hunk() abort
let bufnr = bufnr('')
let current_hunk = []
for hunk in gitgutter#hunk#hunks()
for hunk in gitgutter#hunk#hunks(bufnr)
if gitgutter#hunk#cursor_in_hunk(hunk)
let current_hunk = hunk
break
@ -107,22 +107,8 @@ function! gitgutter#hunk#cursor_in_hunk(hunk) abort
return 0
endfunction
" Returns the number of lines the current hunk is offset from where it would
" be if any changes above it in the file didn't exist.
function! gitgutter#hunk#line_adjustment_for_current_hunk() abort
let adj = 0
for hunk in gitgutter#hunk#hunks()
if gitgutter#hunk#cursor_in_hunk(hunk)
break
else
let adj += hunk[1] - hunk[3]
endif
endfor
return adj
endfunction
function! gitgutter#hunk#text_object(inner) abort
let hunk = gitgutter#hunk#current_hunk()
let hunk = s:current_hunk()
if empty(hunk)
return
@ -141,3 +127,155 @@ function! gitgutter#hunk#text_object(inner) abort
execute 'normal! 'first_line.'GV'.last_line.'G'
endfunction
function! gitgutter#hunk#stage() abort
call s:hunk_op(function('s:stage'))
silent! call repeat#set("\<Plug>GitGutterStageHunk", -1)<CR>
endfunction
function! gitgutter#hunk#undo() abort
call s:hunk_op(function('s:undo'))
silent! call repeat#set("\<Plug>GitGutterUndoHunk", -1)<CR>
endfunction
function! gitgutter#hunk#preview() abort
call s:hunk_op(function('s:preview'))
silent! call repeat#set("\<Plug>GitGutterPreviewHunk", -1)<CR>
endfunction
function! s:hunk_op(op)
let bufnr = bufnr('')
if gitgutter#utility#is_active(bufnr)
" Get a (synchronous) diff.
let [async, g:gitgutter_async] = [g:gitgutter_async, 0]
let diff = gitgutter#diff#run_diff(bufnr, 1)
let g:gitgutter_async = async
call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff))
if empty(s:current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
call a:op(gitgutter#diff#hunk_diff(bufnr, diff))
endif
endif
endfunction
function! s:stage(hunk_diff)
let bufnr = bufnr('')
let diff = s:adjust_header(bufnr, a:hunk_diff)
" Apply patch to index.
call gitgutter#utility#system(
\ gitgutter#utility#cd_cmd(bufnr, g:gitgutter_git_executable.' apply --cached --unidiff-zero - '),
\ diff)
" Refresh gitgutter's view of buffer.
call gitgutter#process_buffer(bufnr, 1)
endfunction
function! s:undo(hunk_diff)
" Apply reverse patch to buffer.
let hunk = gitgutter#diff#parse_hunk(split(a:hunk_diff, '\n')[4])
let lines = map(split(a:hunk_diff, '\n')[5:], 'v:val[1:]')
let lnum = hunk[2]
let added_only = hunk[1] == 0 && hunk[3] > 0
let removed_only = hunk[1] > 0 && hunk[3] == 0
if removed_only
call append(lnum, lines)
elseif added_only
execute lnum .','. (lnum+len(lines)-1) .'d'
else
call append(lnum-1, lines[0:hunk[1]])
execute (lnum+hunk[1]) .','. (lnum+hunk[1]+hunk[3]) .'d'
endif
endfunction
function! s:preview(hunk_diff)
let hunk_lines = split(s:discard_header(a:hunk_diff), "\n")
let hunk_lines_length = len(hunk_lines)
let previewheight = min([hunk_lines_length, &previewheight])
silent! wincmd P
if !&previewwindow
noautocmd execute 'bo' previewheight 'new'
set previewwindow
else
execute 'resize' previewheight
endif
setlocal noreadonly modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile
execute "%delete_"
call append(0, hunk_lines)
normal! gg
setlocal readonly nomodifiable
noautocmd wincmd p
endfunction
function! s:adjust_header(bufnr, hunk_diff)
let filepath = gitgutter#utility#repo_path(a:bufnr, 0)
return s:adjust_hunk_summary(s:fix_file_references(filepath, a:hunk_diff))
endfunction
" Replaces references to temp files with the actual file.
function! s:fix_file_references(filepath, hunk_diff)
let lines = split(a:hunk_diff, '\n')
let left_prefix = matchstr(lines[2], '[abciow12]').'/'
let right_prefix = matchstr(lines[3], '[abciow12]').'/'
let quote = lines[0][11] == '"' ? '"' : ''
let left_file = quote.left_prefix.a:filepath.quote
let right_file = quote.right_prefix.a:filepath.quote
let lines[0] = 'diff --git '.left_file.' '.right_file
let lines[2] = '--- '.left_file
let lines[3] = '+++ '.right_file
return join(lines, "\n")."\n"
endfunction
if $VIM_GITGUTTER_TEST
function! gitgutter#hunk#fix_file_references(filepath, hunk_diff)
return s:fix_file_references(a:filepath, a:hunk_diff)
endfunction
endif
function! s:adjust_hunk_summary(hunk_diff) abort
let line_adjustment = s:line_adjustment_for_current_hunk()
let diff = split(a:hunk_diff, '\n', 1)
let diff[4] = substitute(diff[4], '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '')
return join(diff, "\n")
endfunction
function! s:discard_header(hunk_diff)
return join(split(a:hunk_diff, '\n', 1)[5:], "\n")
endfunction
" Returns the number of lines the current hunk is offset from where it would
" be if any changes above it in the file didn't exist.
function! s:line_adjustment_for_current_hunk() abort
let bufnr = bufnr('')
let adj = 0
for hunk in gitgutter#hunk#hunks(bufnr)
if gitgutter#hunk#cursor_in_hunk(hunk)
break
else
let adj += hunk[1] - hunk[3]
endif
endfor
return adj
endfunction

View File

@ -10,14 +10,43 @@ let s:dummy_sign_id = s:first_sign_id - 1
let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596"))
" Removes gitgutter's signs (excluding dummy sign) from the buffer being processed.
function! gitgutter#sign#clear_signs() abort
let bufnr = gitgutter#utility#bufnr()
call gitgutter#sign#find_current_signs()
function! gitgutter#sign#enable() abort
let old_signs = g:gitgutter_signs
let sign_ids = map(values(gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')), 'v:val.id')
call gitgutter#sign#remove_signs(sign_ids, 1)
call gitgutter#utility#setbufvar(bufnr, 'gitgutter_signs', {})
let g:gitgutter_signs = 1
call gitgutter#highlight#define_sign_text_highlights()
if !old_signs && !g:gitgutter_highlight_lines
call gitgutter#all(1)
endif
endfunction
function! gitgutter#sign#disable() abort
let g:gitgutter_signs = 0
call gitgutter#highlight#define_sign_text_highlights()
if !g:gitgutter_highlight_lines
call gitgutter#sign#clear_signs(bufnr(''))
call gitgutter#sign#remove_dummy_sign(bufnr(''), 0)
endif
endfunction
function! gitgutter#sign#toggle() abort
if g:gitgutter_signs
call gitgutter#sign#disable()
else
call gitgutter#sign#enable()
endif
endfunction
" Removes gitgutter's signs (excluding dummy sign) from the buffer being processed.
function! gitgutter#sign#clear_signs(bufnr) abort
call s:find_current_signs(a:bufnr)
let sign_ids = map(values(gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')), 'v:val.id')
call s:remove_signs(a:bufnr, sign_ids, 1)
call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', {})
endfunction
@ -25,39 +54,37 @@ endfunction
"
" modified_lines: list of [<line_number (number)>, <name (string)>]
" where name = 'added|removed|modified|modified_removed'
function! gitgutter#sign#update_signs(modified_lines) abort
call gitgutter#sign#find_current_signs()
function! gitgutter#sign#update_signs(bufnr, modified_lines) abort
call s:find_current_signs(a:bufnr)
let new_gitgutter_signs_line_numbers = map(copy(a:modified_lines), 'v:val[0]')
let obsolete_signs = gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_line_numbers)
let obsolete_signs = s:obsolete_gitgutter_signs_to_remove(a:bufnr, new_gitgutter_signs_line_numbers)
let flicker_possible = s:remove_all_old_signs && !empty(a:modified_lines)
if flicker_possible
call gitgutter#sign#add_dummy_sign()
call s:add_dummy_sign(a:bufnr)
endif
call gitgutter#sign#remove_signs(obsolete_signs, s:remove_all_old_signs)
call gitgutter#sign#upsert_new_gitgutter_signs(a:modified_lines)
call s:remove_signs(a:bufnr, obsolete_signs, s:remove_all_old_signs)
call s:upsert_new_gitgutter_signs(a:bufnr, a:modified_lines)
if flicker_possible
call gitgutter#sign#remove_dummy_sign(0)
call gitgutter#sign#remove_dummy_sign(a:bufnr, 0)
endif
endfunction
function! gitgutter#sign#add_dummy_sign() abort
let bufnr = gitgutter#utility#bufnr()
if !gitgutter#utility#getbufvar(bufnr, 'dummy_sign')
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', 1)
function! s:add_dummy_sign(bufnr) abort
if !gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign')
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . a:bufnr
call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 1)
endif
endfunction
function! gitgutter#sign#remove_dummy_sign(force) abort
let bufnr = gitgutter#utility#bufnr()
if gitgutter#utility#getbufvar(bufnr, 'dummy_sign') && (a:force || !g:gitgutter_sign_column_always)
execute "sign unplace" s:dummy_sign_id "buffer=" . bufnr
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', 0)
function! gitgutter#sign#remove_dummy_sign(bufnr, force) abort
if gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign') && (a:force || !g:gitgutter_sign_column_always)
execute "sign unplace" s:dummy_sign_id "buffer=" . a:bufnr
call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 0)
endif
endfunction
@ -67,14 +94,13 @@ endfunction
"
function! gitgutter#sign#find_current_signs() abort
let bufnr = gitgutter#utility#bufnr()
function! s:find_current_signs(bufnr) abort
let gitgutter_signs = {} " <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>}
let other_signs = [] " [<line_number (number),...]
let dummy_sign_placed = 0
redir => signs
silent execute "sign place buffer=" . bufnr
silent execute "sign place buffer=" . a:bufnr
redir END
for sign_line in filter(split(signs, '\n')[2:], 'v:val =~# "="')
@ -101,19 +127,18 @@ function! gitgutter#sign#find_current_signs() abort
end
endfor
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', dummy_sign_placed)
call gitgutter#utility#setbufvar(bufnr, 'gitgutter_signs', gitgutter_signs)
call gitgutter#utility#setbufvar(bufnr, 'other_signs', other_signs)
call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', dummy_sign_placed)
call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', gitgutter_signs)
call gitgutter#utility#setbufvar(a:bufnr, 'other_signs', other_signs)
endfunction
" Returns a list of [<id (number)>, ...]
" Sets `s:remove_all_old_signs` as a side-effect.
function! gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_line_numbers) abort
let bufnr = gitgutter#utility#bufnr()
function! s:obsolete_gitgutter_signs_to_remove(bufnr, new_gitgutter_signs_line_numbers) abort
let signs_to_remove = [] " list of [<id (number)>, ...]
let remove_all_signs = 1
let old_gitgutter_signs = gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')
let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')
for line_number in keys(old_gitgutter_signs)
if index(a:new_gitgutter_signs_line_numbers, str2nr(line_number)) == -1
call add(signs_to_remove, old_gitgutter_signs[line_number].id)
@ -126,13 +151,12 @@ function! gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_
endfunction
function! gitgutter#sign#remove_signs(sign_ids, all_signs) abort
let bufnr = gitgutter#utility#bufnr()
if a:all_signs && s:supports_star && empty(gitgutter#utility#getbufvar(bufnr, 'other_signs'))
let dummy_sign_present = gitgutter#utility#getbufvar(bufnr, 'dummy_sign')
execute "sign unplace * buffer=" . bufnr
function! s:remove_signs(bufnr, sign_ids, all_signs) abort
if a:all_signs && s:supports_star && empty(gitgutter#utility#getbufvar(a:bufnr, 'other_signs'))
let dummy_sign_present = gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign')
execute "sign unplace * buffer=" . a:bufnr
if dummy_sign_present
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . a:bufnr
endif
else
for id in a:sign_ids
@ -142,22 +166,21 @@ function! gitgutter#sign#remove_signs(sign_ids, all_signs) abort
endfunction
function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort
let bufnr = gitgutter#utility#bufnr()
let other_signs = gitgutter#utility#getbufvar(bufnr, 'other_signs')
let old_gitgutter_signs = gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')
function! s:upsert_new_gitgutter_signs(bufnr, modified_lines) abort
let other_signs = gitgutter#utility#getbufvar(a:bufnr, 'other_signs')
let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')
for line in a:modified_lines
let line_number = line[0] " <number>
if index(other_signs, line_number) == -1 " don't clobber others' signs
let name = gitgutter#utility#highlight_name_for_change(line[1])
let name = s:highlight_name_for_change(line[1])
if !has_key(old_gitgutter_signs, line_number) " insert
let id = gitgutter#sign#next_sign_id()
execute "sign place" id "line=" . line_number "name=" . name "buffer=" . bufnr
let id = s:next_sign_id()
execute "sign place" id "line=" . line_number "name=" . name "buffer=" . a:bufnr
else " update if sign has changed
let old_sign = old_gitgutter_signs[line_number]
if old_sign.name !=# name
execute "sign place" old_sign.id "name=" . name "buffer=" . bufnr
execute "sign place" old_sign.id "name=" . name "buffer=" . a:bufnr
end
endif
endif
@ -166,7 +189,7 @@ function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort
endfunction
function! gitgutter#sign#next_sign_id() abort
function! s:next_sign_id() abort
let next_id = s:next_sign_id
let s:next_sign_id += 1
return next_id
@ -177,3 +200,20 @@ endfunction
function! gitgutter#sign#reset()
let s:next_sign_id = s:first_sign_id
endfunction
function! s:highlight_name_for_change(text) abort
if a:text ==# 'added'
return 'GitGutterLineAdded'
elseif a:text ==# 'removed'
return 'GitGutterLineRemoved'
elseif a:text ==# 'removed_first_line'
return 'GitGutterLineRemovedFirstLine'
elseif a:text ==# 'modified'
return 'GitGutterLineModified'
elseif a:text ==# 'modified_removed'
return 'GitGutterLineModifiedRemoved'
endif
endfunction

View File

@ -1,11 +1,18 @@
let s:file = ''
let s:using_xolox_shell = -1
let s:exit_code = 0
function! gitgutter#utility#supports_overscore_sign()
if s:windows()
return &encoding ==? 'utf-8'
else
return &termencoding ==? &encoding || &termencoding == ''
endif
endfunction
function! gitgutter#utility#setbufvar(buffer, varname, val)
let dict = get(getbufvar(a:buffer, ''), 'gitgutter', {})
let needs_setting = empty(dict)
let dict[a:varname] = a:val
call setbufvar(a:buffer, 'gitgutter', dict)
if needs_setting
call setbufvar(a:buffer, 'gitgutter', dict)
endif
endfunction
function! gitgutter#utility#getbufvar(buffer, varname, ...)
@ -26,11 +33,11 @@ function! gitgutter#utility#warn(message) abort
let v:warningmsg = a:message
endfunction
function! gitgutter#utility#warn_once(message, key) abort
if empty(gitgutter#utility#getbufvar(s:bufnr, a:key))
call gitgutter#utility#setbufvar(s:bufnr, a:key, '1')
function! gitgutter#utility#warn_once(bufnr, message, key) abort
if empty(gitgutter#utility#getbufvar(a:bufnr, a:key))
call gitgutter#utility#setbufvar(a:bufnr, a:key, '1')
echohl WarningMsg
redraw | echo 'vim-gitgutter: ' . a:message
redraw | echom 'vim-gitgutter: ' . a:message
echohl None
let v:warningmsg = a:message
endif
@ -38,188 +45,166 @@ endfunction
" Returns truthy when the buffer's file should be processed; and falsey when it shouldn't.
" This function does not and should not make any system calls.
function! gitgutter#utility#is_active() abort
function! gitgutter#utility#is_active(bufnr) abort
return g:gitgutter_enabled &&
\ !pumvisible() &&
\ gitgutter#utility#is_file_buffer() &&
\ gitgutter#utility#exists_file() &&
\ gitgutter#utility#not_git_dir()
\ s:is_file_buffer(a:bufnr) &&
\ s:exists_file(a:bufnr) &&
\ s:not_git_dir(a:bufnr)
endfunction
function! gitgutter#utility#not_git_dir() abort
return gitgutter#utility#full_path_to_directory_of_file() !~ '[/\\]\.git\($\|[/\\]\)'
function! s:not_git_dir(bufnr) abort
return s:dir(a:bufnr) !~ '[/\\]\.git\($\|[/\\]\)'
endfunction
function! gitgutter#utility#is_file_buffer() abort
return empty(getbufvar(s:bufnr, '&buftype'))
function! s:is_file_buffer(bufnr) abort
return empty(getbufvar(a:bufnr, '&buftype'))
endfunction
" A replacement for the built-in `shellescape(arg)`.
"
" Recent versions of Vim handle shell escaping pretty well. However older
" versions aren't as good. This attempts to do the right thing.
"
" See:
" https://github.com/tpope/vim-fugitive/blob/8f0b8edfbd246c0026b7a2388e1d883d579ac7f6/plugin/fugitive.vim#L29-L37
" From tpope/vim-fugitive
function! s:winshell()
return &shell =~? 'cmd' || exists('+shellslash') && !&shellslash
endfunction
" From tpope/vim-fugitive
function! gitgutter#utility#shellescape(arg) abort
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
return a:arg
elseif &shell =~# 'cmd' || gitgutter#utility#using_xolox_shell()
elseif s:winshell()
return '"' . substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g') . '"'
else
return shellescape(a:arg)
endif
endfunction
function! gitgutter#utility#set_buffer(bufnr) abort
let s:bufnr = a:bufnr
let s:file = resolve(bufname(a:bufnr))
function! gitgutter#utility#file(bufnr)
return s:abs_path(a:bufnr, 1)
endfunction
function! gitgutter#utility#bufnr()
return s:bufnr
endfunction
function! gitgutter#utility#file()
return s:file
endfunction
function! gitgutter#utility#filename() abort
return fnamemodify(s:file, ':t')
endfunction
function! gitgutter#utility#extension() abort
return fnamemodify(s:file, ':e')
endfunction
function! gitgutter#utility#full_path_to_directory_of_file() abort
return fnamemodify(s:file, ':p:h')
endfunction
function! gitgutter#utility#directory_of_file() abort
return fnamemodify(s:file, ':h')
endfunction
function! gitgutter#utility#exists_file() abort
return filereadable(s:file)
endfunction
function! gitgutter#utility#has_unsaved_changes() abort
return getbufvar(s:bufnr, "&mod")
endfunction
function! gitgutter#utility#has_fresh_changes() abort
return getbufvar(s:bufnr, 'changedtick') != gitgutter#utility#getbufvar(s:bufnr, 'last_tick')
endfunction
function! gitgutter#utility#save_last_seen_change() abort
call gitgutter#utility#setbufvar(s:bufnr, 'last_tick', getbufvar(s:bufnr, 'changedtick'))
endfunction
function! gitgutter#utility#shell_error() abort
return gitgutter#utility#using_xolox_shell() ? s:exit_code : v:shell_error
endfunction
function! gitgutter#utility#using_xolox_shell() abort
if s:using_xolox_shell == -1
if !g:gitgutter_avoid_cmd_prompt_on_windows
let s:using_xolox_shell = 0
" Although xolox/vim-shell works on both windows and unix we only want to use
" it on windows.
elseif has('win32') || has('win64') || has('win32unix')
let s:using_xolox_shell = exists('g:xolox#misc#version') && exists('g:xolox#shell#version')
else
let s:using_xolox_shell = 0
endif
endif
return s:using_xolox_shell
" Not shellescaped
function! gitgutter#utility#extension(bufnr) abort
return fnamemodify(s:abs_path(a:bufnr, 0), ':e')
endfunction
function! gitgutter#utility#system(cmd, ...) abort
call gitgutter#debug#log(a:cmd, a:000)
if gitgutter#utility#using_xolox_shell()
let options = {'command': a:cmd, 'check': 0}
if a:0 > 0
let options['stdin'] = a:1
endif
let ret = xolox#misc#os#exec(options)
let output = join(ret.stdout, "\n")
let s:exit_code = ret.exit_code
else
silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1)
endif
call s:use_known_shell()
silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1)
call s:restore_shell()
return output
endfunction
function! gitgutter#utility#file_relative_to_repo_root() abort
let file_path_relative_to_repo_root = gitgutter#utility#getbufvar(s:bufnr, 'repo_relative_path')
if empty(file_path_relative_to_repo_root)
let dir_path_relative_to_repo_root = gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' rev-parse --show-prefix'))
let dir_path_relative_to_repo_root = gitgutter#utility#strip_trailing_new_line(dir_path_relative_to_repo_root)
let file_path_relative_to_repo_root = dir_path_relative_to_repo_root . gitgutter#utility#filename()
call gitgutter#utility#setbufvar(s:bufnr, 'repo_relative_path', file_path_relative_to_repo_root)
endif
return file_path_relative_to_repo_root
" Path of file relative to repo root.
"
" * empty string - not set
" * non-empty string - path
" * -1 - pending
" * -2 - not tracked by git
function! gitgutter#utility#repo_path(bufnr, shellesc) abort
let p = gitgutter#utility#getbufvar(a:bufnr, 'path')
return a:shellesc ? gitgutter#utility#shellescape(p) : p
endfunction
function! gitgutter#utility#command_in_directory_of_file(cmd) abort
return 'cd '.gitgutter#utility#shellescape(gitgutter#utility#directory_of_file()).' && '.a:cmd
endfunction
function! gitgutter#utility#set_repo_path(bufnr) abort
" Values of path:
" * non-empty string - path
" * -1 - pending
" * -2 - not tracked by git
function! gitgutter#utility#highlight_name_for_change(text) abort
if a:text ==# 'added'
return 'GitGutterLineAdded'
elseif a:text ==# 'removed'
return 'GitGutterLineRemoved'
elseif a:text ==# 'removed_first_line'
return 'GitGutterLineRemovedFirstLine'
elseif a:text ==# 'modified'
return 'GitGutterLineModified'
elseif a:text ==# 'modified_removed'
return 'GitGutterLineModifiedRemoved'
call gitgutter#utility#setbufvar(a:bufnr, 'path', -1)
let cmd = gitgutter#utility#cd_cmd(a:bufnr, g:gitgutter_git_executable.' ls-files --error-unmatch --full-name '.gitgutter#utility#shellescape(s:filename(a:bufnr)))
if g:gitgutter_async && gitgutter#async#available()
if has('lambda')
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': {bufnr, path -> gitgutter#utility#setbufvar(bufnr, 'path', s:strip_trailing_new_line(path))},
\ 'err': {bufnr -> gitgutter#utility#setbufvar(bufnr, 'path', -2)},
\ })
else
if has('nvim') && !has('nvim-0.2.0')
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': function('s:set_path'),
\ 'err': function('s:not_tracked_by_git')
\ })
else
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': function('s:set_path'),
\ 'err': function('s:set_path', [-2])
\ })
endif
endif
else
let path = gitgutter#utility#system(cmd)
if v:shell_error
call gitgutter#utility#setbufvar(a:bufnr, 'path', -2)
else
call gitgutter#utility#setbufvar(a:bufnr, 'path', s:strip_trailing_new_line(path))
endif
endif
endfunction
" Dedups list in-place.
" Assumes list has no empty entries.
function! gitgutter#utility#dedup(list)
return filter(sort(a:list), 'index(a:list, v:val, v:key + 1) == -1')
if has('nvim') && !has('nvim-0.2.0')
function! s:not_tracked_by_git(bufnr)
call s:set_path(a:bufnr, -2)
endfunction
endif
function! s:set_path(bufnr, path)
if a:bufnr == -2
let [bufnr, path] = [a:path, a:bufnr]
call gitgutter#utility#setbufvar(bufnr, 'path', path)
else
call gitgutter#utility#setbufvar(a:bufnr, 'path', s:strip_trailing_new_line(a:path))
endif
endfunction
function! gitgutter#utility#strip_trailing_new_line(line) abort
function! gitgutter#utility#cd_cmd(bufnr, cmd) abort
let cd = s:unc_path(a:bufnr) ? 'pushd' : (s:windows() ? 'cd /d' : 'cd')
return cd.' '.s:dir(a:bufnr).' && '.a:cmd
endfunction
function! s:unc_path(bufnr)
return s:abs_path(a:bufnr, 0) =~ '^\\\\'
endfunction
function! s:use_known_shell() abort
if has('unix') && &shell !=# 'sh'
let [s:shell, s:shellcmdflag, s:shellredir] = [&shell, &shellcmdflag, &shellredir]
let &shell = 'sh'
set shellcmdflag=-c shellredir=>%s\ 2>&1
endif
endfunction
function! s:restore_shell() abort
if has('unix') && exists('s:shell')
let [&shell, &shellcmdflag, &shellredir] = [s:shell, s:shellcmdflag, s:shellredir]
endif
endfunction
function! s:abs_path(bufnr, shellesc)
let p = resolve(expand('#'.a:bufnr.':p'))
return a:shellesc ? gitgutter#utility#shellescape(p) : p
endfunction
function! s:dir(bufnr) abort
return gitgutter#utility#shellescape(fnamemodify(s:abs_path(a:bufnr, 0), ':h'))
endfunction
" Not shellescaped.
function! s:filename(bufnr) abort
return fnamemodify(s:abs_path(a:bufnr, 0), ':t')
endfunction
function! s:exists_file(bufnr) abort
return filereadable(s:abs_path(a:bufnr, 0))
endfunction
function! s:strip_trailing_new_line(line) abort
return substitute(a:line, '\n$', '', '')
endfunction
" True for git v1.7.2+.
function! gitgutter#utility#git_supports_command_line_config_override() abort
call system(g:gitgutter_git_executable.' -c foo.bar=baz --version')
return !v:shell_error
endfunction
function! gitgutter#utility#stringify(list) abort
return join(a:list, "\n")."\n"
endfunction
function! gitgutter#utility#use_known_shell() abort
if has('unix')
if &shell !=# 'sh'
let s:shell = &shell
let s:shellcmdflag = &shellcmdflag
let s:shellredir = &shellredir
let &shell = 'sh'
set shellcmdflag=-c
set shellredir=>%s\ 2>&1
endif
endif
endfunction
function! gitgutter#utility#restore_shell() abort
if has('unix')
if exists('s:shell')
let &shell = s:shell
let &shellcmdflag = s:shellcmdflag
let &shellredir = s:shellredir
endif
endif
function! s:windows()
return has('win64') || has('win32') || has('win16')
endfunction

View File

@ -4,45 +4,50 @@
Vim Git Gutter
Author: Andy Stewart <http://airbladesoftware.com/>
Author: Andy Stewart <https://airbladesoftware.com/>
Plugin Homepage: <https://github.com/airblade/vim-gitgutter>
===============================================================================
CONTENTS *GitGutterContents*
1. Introduction ................. |GitGutterIntroduction|
2. Installation ................. |GitGutterInstallation|
3. Usage ........................ |GitGutterUsage|
4. Commands ..................... |GitGutterCommands|
5. Autocommand .................. |GitGutterAutocmd|
6. CUSTOMISATION................. |GitGutterCustomisation|
7. FAQ .......................... |GitGutterFAQ|
===============================================================================
1. INTRODUCTION *GitGutterIntroduction*
*GitGutter*
CONTENTS *gitgutter*
Vim Git Gutter is a Vim plugin which shows a git diff in the 'gutter' (sign
column). It shows whether each line has been added, modified, and where lines
have been removed.
Introduction ................. |gitgutter-introduction|
Installation ................. |gitgutter-installation|
Commands ..................... |gitgutter-commands|
Mappings ..................... |gitgutter-mappings|
Autocommand .................. |gitgutter-autocommand|
Options ...................... |gitgutter-options|
Highlights ................... |gitgutter-highlights|
FAQ .......................... |gitgutter-faq|
TROUBLESHOOTING .............. |gitgutter-troubleshooting|
This is a port of the Git Gutter plugin for Sublime Text 2.
===============================================================================
2. INSTALLATION *GitGutterInstallation*
INTRODUCTION *gitgutter-introduction*
* Pathogen:
GitGutter is a Vim plugin which shows a git diff in the 'gutter' (sign column).
It shows which lines have been added, modified, or removed. You can also
preview, stage, and undo individual hunks. The plugin also provides a hunk
text object.
The signs are always up to date and the plugin never saves your buffer.
===============================================================================
INSTALLATION *gitgutter-installation*
Pathogen:~
>
cd ~/.vim/bundle
git clone git://github.com/airblade/vim-gitgutter.git
<
* Voom:
Voom:~
Edit your plugin manifest (`voom edit`) and add:
>
airblade/vim-gitgutter
<
* VimPlug:
VimPlug:~
Place this in your .vimrc:
>
@ -53,7 +58,7 @@ Then run the following in Vim:
:source %
:PlugInstall
<
* NeoBundle:
NeoBundle:~
Place this in your .vimrc:
>
@ -64,7 +69,7 @@ Then run the following in Vim:
:source %
:NeoBundleInstall
<
* No plugin manager:
No plugin manager:~
Copy vim-gitgutter's subdirectories into your vim configuration directory:
>
@ -73,133 +78,346 @@ Copy vim-gitgutter's subdirectories into your vim configuration directory:
<
See |add-global-plugin|.
===============================================================================
3. USAGE *GitGutterUsage*
You don't have to do anything: it just works.
===============================================================================
4. COMMANDS *GitGutterCommands*
COMMANDS *gitgutter-commands*
Commands for turning Git Gutter on and off:
Commands for turning vim-gitgutter on and off:~
:GitGutterDisable *:GitGutterDisable*
Explicitly turn Git Gutter off.
*gitgutter-:GitGutterDisable*
:GitGutterDisable Turn vim-gitgutter off for all buffers.
:GitGutterEnable *:GitGutterEnable*
Explicitly turn Git Gutter on.
*gitgutter-:GitGutterEnable*
:GitGutterEnable Turn vim-gitgutter on for all buffers.
:GitGutterToggle *:GitGutterToggle*
Explicitly turn Git Gutter on if it was off and vice versa.
*gitgutter-:GitGutterToggle*
:GitGutterToggle Toggle vim-gitgutter on or off for all buffers.
:GitGutter *:GitGutter*
Update signs for the current buffer.
*gitgutter-:GitGutter*
:GitGutter Update signs for the current buffer. You shouldn't
need to run this.
:GitGutterAll *:GitGutterAll*
Update signs across all buffers.
*gitgutter-:GitGutterAll*
:GitGutterAll Update signs for all buffers. You shouldn't need to
run this.
Commands for turning signs on and off (defaults to on):
:GitGutterSignsEnable *:GitGutterSignsEnable*
Explicitly turn line signs on.
Commands for turning signs on and off (defaults to on):~
:GitGutterSignsDisable *:GitGutterSignsDisable*
Explicitly turn line signs off.
*gitgutter-:GitGutterSignsEnable*
:GitGutterSignsEnable Show signs for the diff.
:GitGutterSignsToggle *:GitGutterSignsToggle*
Explicitly turn line signs on if it was off and vice versa.
*gitgutter-:GitGutterSignsDisable*
:GitGutterSignsDisable Do not show signs for the diff.
Commands for turning line highlighting on and off (defaults to off):
*gitgutter-:GitGutterSignsToggle*
:GitGutterSignsToggle Toggle signs on or off.
:GitGutterLineHighlightsEnable *:GitGutterLineHighlightsEnable*
Explicitly turn line highlighting on.
:GitGutterLineHighlightsDisable *:GitGutterLineHighlightsDisable*
Explicitly turn line highlighting off.
Commands for turning line highlighting on and off (defaults to off):~
:GitGutterLineHighlightsToggle *:GitGutterLineHighlightsToggle*
Explicitly turn line highlighting on if it was off and vice versa.
*gitgutter-:GitGutterLineHighlightsEnable*
:GitGutterLineHighlightsEnable Turn on line highlighting.
Commands for jumping between marked hunks:
*gitgutter-:GitGutterLineHighlightsDisable*
:GitGutterLineHighlightsDisable Turn off line highlighting.
:GitGutterNextHunk *:GitGutterNextHunk*
Jump to the next marked hunk. Takes a count.
*gitgutter-:GitGutterLineHighlightsToggle*
:GitGutterLineHighlightsToggle Turn line highlighting on or off.
:GitGutterPrevHunk *:GitGutterPrevHunk*
Jump to the previous marked hunk. Takes a count.
Commands for staging or undoing individual hunks:
Commands for jumping between hunks:~
:GitGutterStageHunk *:GitGutterStageHunk*
Stage the hunk the cursor is in.
*gitgutter-:GitGutterNextHunk*
:GitGutterNextHunk Jump to the next [count] hunk.
:GitGutterUndoHunk *:GitGutterUndoHunk*
Undo the hunk the cursor is in.
*gitgutter-:GitGutterPrevHunk*
:GitGutterPrevHunk Jump to the previous [count] hunk.
Commands for operating on a hunk:~
*gitgutter-:GitGutterStageHunk*
:GitGutterStageHunk Stage the hunk the cursor is in.
*gitgutter-:GitGutterUndoHunk*
:GitGutterUndoHunk Undo the hunk the cursor is in.
*gitgutter-:GitGutterPreviewHunk*
:GitGutterPreviewHunk Preview the hunk the cursor is in.
Use |:pclose| or |CTRL-W_CTRL-Z| to close the preview
window.
:GitGutterPreviewHunk *:GitGutterPreviewHunk*
Preview the hunk the cursor is in.
Use |:pclose| or |CTRL-W_CTRL-Z| to close the preview window.
===============================================================================
5. AUTOCOMMAND *GitGutterAutocmd*
AUTOCOMMAND *gitgutter-autocommand*
User GitGutter~
After updating a buffer's signs vim-gitgutter fires a |User| |autocmd| with the
event GitGutter. You can listen for this event, for example:
>
autocmd User GitGutter call updateMyStatusLine()
<
A dictionary `g:gitgutter_hook_context` is made available during its execution,
which contains an entry `bufnr` that contains the buffer number being updated.
===============================================================================
6. CUSTOMISATION *GitGutterCustomisation*
MAPPINGS *gitgutter-mappings*
You can customise:
You can disable all these mappings with:
>
let g:gitgutter_map_keys = 0
<
- The sign column's colours
- The signs' colours and symbols
- Line highlights
- The base of the diff
- Extra arguments for git-diff
- Key mappings
- The grep executable used
- Whether or not vim-gitgutter is on initially (defaults to on)
- Whether or not signs are shown (defaults to yes)
- 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 eagerly (defaults to yes)
- Whether or not vim-gitgutter runs asynchronously (defaults to yes)
Hunk operations:~
Please note that vim-gitgutter won't override any colours or highlights you've
set in your colorscheme.
These can be repeated with `.` if you have vim-repeat installed.
SIGN COLUMN
*gitgutter-<Leader>hp*
<Leader>hp Preview the hunk under the cursor.
By default vim-gitgutter will make the sign column look like the line number
column (i.e. the |hl-LineNr| highlight group).
*gitgutter-<Leader>hs*
<Leader>hs Stage the hunk under the cursor.
*gitgutter-<Leader>hu*
<Leader>hu Undo the hunk under the cursor.
You can change these mappings like this:
>
nmap ghp <Plug>GitGutterPreviewHunk
nmap ghs <Plug>GitGutterStageHunk
nmap ghu <Plug>GitGutterUndoHunk
<
Hunk jumping:~
*gitgutter-]c*
]c Jump to the next [count] hunk.
*gitgutter-[c*
[c Jump to the previous [count] hunk.
You can change these mappings like this:
>
nmap [c <Plug>GitGutterPrevHunk
nmap ]c <Plug>GitGutterNextHunk
<
Hunk text object:~
*gitgutter-ic* *gitgutter-ac* *gitgutter-text-object*
"ic" operates on the current hunk's lines. "ac" does the same but also includes
trailing empty lines.
>
omap ic <Plug>GitGutterTextObjectInnerPending
omap ac <Plug>GitGutterTextObjectOuterPending
xmap ic <Plug>GitGutterTextObjectInnerVisual
xmap ac <Plug>GitGutterTextObjectOuterVisual
<
===============================================================================
OPTIONS *gitgutter-options*
The most important option is 'updatetime' which determines how long (in
milliseconds) the plugin will wait after you stop typing before it updates the
signs. Vim's default is 4000. I recommend 100.
Most important option:~
'updatetime'
Git:~
|g:gitgutter_git_executable|
|g:gitgutter_diff_args|
|g:gitgutter_diff_base|
Grep:~
|g:gitgutter_grep|
Signs:~
|g:gitgutter_signs|
|g:gitgutter_highlight_lines|
|g:gitgutter_max_signs|
|g:gitgutter_sign_added|
|g:gitgutter_sign_modified|
|g:gitgutter_sign_removed|
|g:gitgutter_sign_removed_first_line|
|g:gitgutter_sign_modified_removed|
|g:gitgutter_sign_column_always|
|g:gitgutter_override_sign_column_highlight|
Terminal:~
|g:gitgutter_terminal_reports_focus|
General:~
|g:gitgutter_enabled|
|g:gitgutter_map_keys|
|g:gitgutter_async|
|g:gitgutter_log|
*g:gitgutter_git_executable*
Default: 'git'
This option determines what git binary to use. Set this if git is not on your
path.
*g:gitgutter_diff_args*
Default: empty
Use this option to pass any extra arguments to git-diff. For example:
>
let g:gitgutter_diff_args = '-w'
<
*g:gitgutter_diff_base*
Default: empty
By default buffers are diffed against the index. Use this option to diff against
a revision instead. For example:
>
let g:gitgutter_diff_base = '<some commit SHA>'
<
*g:gitgutter_grep*
Default: 'grep'
The plugin pipes the output of git-diff into grep to minimise the amount of data
vim has to process. Set this option if grep is not on your path.
grep must produce plain-text output without any ANSI escape codes or colours.
Use this option to turn off colours if necessary.
>
let g:gitgutter_grep = 'grep --color=never'
<
If you do not want to use grep at all (perhaps to debug why signs are not
showing), set this option to an empty string:
>
let g:gitgutter_grep = ''
<
*g:gitgutter_signs*
Default: 1
Determines whether or not to show signs.
*g:gitgutter_highlight_lines*
Default: 0
Determines whether or not to show line highlights.
*g:gitgutter_max_signs*
Default: 500
Sets the maximum number of signs to show in a buffer. Vim is slow at updating
signs, so to avoid slowing down the GUI the number of signs is capped. When
the number of changed lines exceeds this value, the plugin removes all signs
and displays a warning message.
*g:gitgutter_sign_added*
*g:gitgutter_sign_modified*
*g:gitgutter_sign_removed*
*g:gitgutter_sign_removed_first_line*
*g:gitgutter_sign_modified_removed*
Defaults:
>
let g:gitgutter_sign_added = '+'
let g:gitgutter_sign_modified = '~'
let g:gitgutter_sign_removed = '_'
let g:gitgutter_sign_removed_first_line = '‾'
let g:gitgutter_sign_modified_removed = '~_'
<
You can use unicode characters but not images. Signs must not take up more than
2 columns.
*g:gitgutter_sign_column_always*
Default: 0
This legacy option controls whether the sign column should always be shown, even
if there are no signs to display.
From Vim 7.4.2201, use 'signcolumn' instead:
>
set signcolumn=yes
<
*g:gitgutter_override_sign_column_highlight*
Default: 1
Controls whether to make the sign column look like the line-number column (i.e.
the |hl-LineNr| highlight group).
To customise your sign column's background color, first tell vim-gitgutter to
leave it alone:
>
let g:gitgutter_override_sign_column_highlight = 0
let g:gitgutter_override_sign_column_highlight = 0
<
And then either update your colorscheme's |hlSignColumn| highlight group or set
it in your |vimrc|:
Desired appearance Command ~
Same as line number column highlight clear SignColumn
Same as line-number column highlight clear SignColumn
User-defined (terminal Vim) highlight SignColumn ctermbg={whatever}
User-defined (graphical Vim) highlight SignColumn guibg={whatever}
SIGNS' COLOURS AND SYMBOLS
To customise the colours, set up the following highlight groups in your
*g:gitgutter_terminal_reports_focus*
Default: 1
Normally the plugin uses |FocusGained| to force-update all buffers when Vim
receives focus. However some terminals do not report focus events and so the
|FocusGained| autocommand never fires.
If this applies to you, either install something like Terminus
(https://github.com/wincent/terminus) to make |FocusGained| work or set this
option to 0.
When this option is 0, the plugin force-updates the buffer on |BufEnter|
(instead of only updating if the buffer's contents has changed since the last
update).
*g:gitgutter_enabled*
Default: 1
Controls whether or not the plugin is on at startup.
*g:gitgutter_map_keys*
Default: 1
Controls whether or not the plugin provides mappings. See |gitgutter-mapppings|.
*g:gitgutter_async*
Default: 1
Controls whether or not diffs are run in the background. This has no effect if
your Vim does not support background jobs.
*g:gitgutter_log*
Default: 0
When switched on, the plugin logs to gitgutter.log in the directory where it is
installed. Additionally it logs channel activity to channel.log.
===============================================================================
HIGHLIGHTS *gitgutter-highlights*
To change the signs' colours, set up the following highlight groups in your
colorscheme or |vimrc|:
>
GitGutterAdd " an added line
GitGutterChange " a changed line
GitGutterDelete " at least one removed line
GitGutterChangeDelete " a changed line followed by at least one removed line
GitGutterAdd " an added line
GitGutterChange " a changed line
GitGutterDelete " at least one removed line
GitGutterChangeDelete " a changed line followed by at least one removed line
<
You can either set these with `highlight GitGutterAdd {key}={arg}...` or link
@ -208,134 +426,94 @@ them to existing highlight groups with, say:
highlight link GitGutterAdd DiffAdd
<
To customise the symbols, add the following to your |vimrc|:
>
let g:gitgutter_sign_added = 'xx'
let g:gitgutter_sign_modified = 'yy'
let g:gitgutter_sign_removed = 'zz'
let g:gitgutter_sign_modified_removed = 'ww'
<
LINE HIGHLIGHTS
Similarly to the signs' colours, set up the following highlight groups in your
To change the line highlights, set up the following highlight groups in your
colorscheme or |vimrc|:
>
GitGutterAddLine " default: links to DiffAdd
GitGutterChangeLine " default: links to DiffChange
GitGutterDeleteLine " default: links to DiffDelete
GitGutterChangeDeleteLine " default: links to GitGutterChangeLineDefault
GitGutterAddLine " default: links to DiffAdd
GitGutterChangeLine " default: links to DiffChange
GitGutterDeleteLine " default: links to DiffDelete
GitGutterChangeDeleteLine " default: links to GitGutterChangeLineDefault
<
THE BASE OF THE DIFF
By default buffers are diffed against the index. To diff against a commit
instead:
>
let g:gitgutter_diff_base = '<commit SHA>'
<
EXTRA ARGUMENTS FOR GIT-DIFF
To pass extra arguments to git-diff, add this to your |vimrc|:
>
let g:gitgutter_diff_args = '-w'
<
KEY MAPPINGS
To disable all key maps:
>
let g:gitgutter_map_keys = 0
<
To change the hunk-jumping maps (defaults shown):
>
nmap [c <Plug>GitGutterPrevHunk
nmap ]c <Plug>GitGutterNextHunk
<
To change the hunk-staging/undoing/previewing maps (defaults shown):
>
nmap <Leader>hs <Plug>GitGutterStageHunk
nmap <Leader>hu <Plug>GitGutterUndoHunk
nmap <Leader>hp <Plug>GitGutterPreviewHunk
<
To change the hunk text object maps (defaults shown):
>
omap ic <Plug>GitGutterTextObjectInnerPending
omap ac <Plug>GitGutterTextObjectOuterPending
xmap ic <Plug>GitGutterTextObjectInnerVisual
xmap ac <Plug>GitGutterTextObjectOuterVisual
<
TO USE A CUSTOM GREP COMMAND
To use a custom invocation for grep, use this:
>
let g:gitgutter_grep_command = 'grep'
<
TO TURN OFF VIM-GITGUTTER BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_enabled = 0
<
TO TURN OFF SIGNS BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_signs = 0
<
Note that the sign column will still be present if you have line highlighting
switched on.
TO TURN ON LINE HIGHLIGHTING BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_highlight_lines = 1
<
TO STOP VIM-GITGUTTER RUNNING IN REALTIME
Add to your |vimrc|
>
let g:gitgutter_realtime = 0
<
TO STOP VIM-GITGUTTER RUNNING EAGERLY
Add to your |vimrc|
>
let g:gitgutter_eager = 0
<
TO TURN OFF ASYNCHRONOUS UPDATES
By default diffs are run asynchronously. To run diffs synchronously
instead:
Add to your |vimrc|
>
let g:gitgutter_async = 0
<
===============================================================================
7. FAQ *GitGutterFAQ*
FAQ *gitgutter-faq*
a. Why are the colours in the sign column weird?
a. How do I turn off realtime updates?
Add this to your vim configuration in an |after-directory|:
>
autocmd! gitgutter CursorHold,CursorHoldI
<
b. Why can't I unstage staged changes?
This plugin is for showing changes between the working tree and the index
(and staging/undoing those changes). Unstaging a staged hunk would require
showing changes between the index and HEAD, which is out of scope.
c. Why are the colours in the sign column weird?
Your colorscheme is configuring the |hl-SignColumn| highlight group weirdly.
Please see |GitGutterCustomisation| on customising the sign column.
Please see |g:gitgutter_override_sign_column_highlight| on customising the
sign column.
b. What happens if I also use another plugin which uses signs (e.g. Syntastic)?
d. What happens if I also use another plugin which uses signs (e.g. Syntastic)?
Vim only allows one sign per line. Vim-gitgutter will not interfere with
signs it did not add.
===============================================================================
TROUBLESHOOTING *gitgutter-troubleshooting*
When no signs are showing at all:~
1. Try bypassing grep with:
>
let g:gitgutter_grep = ''
<
If it works, the problem is grep outputting ANSI escape codes. Use this
option to pass arguments to grep to turn off the escape codes.
2. Verify git is on your path:
>
:echo system('git --version')
<
3. Verify your git config is compatible with the version of git return by the
command above.
4. Verify your Vim supports signs. The following should give 1:
>
:echo has('signs')
<
5. Check whether the plugin thinks git knows about your file:
>
:echo getbufvar('','gitgutter').path
<
If the result is -2, the plugin thinks your file is not tracked by git.
When the whole file is marked as added:~
If you use zsh, and you set "CDPATH", make sure "CDPATH" does not include the
current directory.
When signs take a few seconds to appear:~
Try reducing 'updatetime':
>
set updatetime=100
<
When signs don't update after focusing Vim:~
Your terminal probably isn't reporting focus events. Either try installing
Terminus (https://github.com/wincent/terminus) or set:
>
let g:gitgutter_terminal_reports_focus = 0
<
Vim only allows one sign per line. Before adding a sign to a line,
vim-gitgutter checks whether a sign has already been added by somebody else.
If so it doesn't do anything. In other words vim-gitgutter won't overwrite
another plugin's signs. It also won't remove another plugin's signs.

View File

@ -7,14 +7,9 @@ let g:loaded_gitgutter = 1
" Initialisation {{{
" Realtime sign updates require Vim 7.3.105+.
if v:version < 703 || (v:version == 703 && !has("patch105"))
let g:gitgutter_realtime = 0
endif
" Eager updates require gettabvar()/settabvar().
if !exists("*gettabvar")
let g:gitgutter_eager = 0
call gitgutter#utility#warn('requires Vim 7.3.105')
finish
endif
function! s:set(var, default) abort
@ -33,35 +28,50 @@ call s:set('g:gitgutter_signs', 1)
call s:set('g:gitgutter_highlight_lines', 0)
call s:set('g:gitgutter_sign_column_always', 0)
if g:gitgutter_sign_column_always && exists('&signcolumn')
" Vim 7.4.2201.
set signcolumn=yes
let g:gitgutter_sign_column_always = 0
call gitgutter#utility#warn('please replace "let g:gitgutter_sign_column_always=1" with "set signcolumn=yes"')
endif
call s:set('g:gitgutter_override_sign_column_highlight', 1)
call s:set('g:gitgutter_realtime', 1)
call s:set('g:gitgutter_eager', 1)
call s:set('g:gitgutter_sign_added', '+')
call s:set('g:gitgutter_sign_modified', '~')
call s:set('g:gitgutter_sign_removed', '_')
try
if gitgutter#utility#supports_overscore_sign()
call s:set('g:gitgutter_sign_removed_first_line', '‾')
catch /E239/
let g:gitgutter_sign_removed_first_line = '_^'
endtry
else
call s:set('g:gitgutter_sign_removed_first_line', '_^')
endif
call s:set('g:gitgutter_sign_modified_removed', '~_')
call s:set('g:gitgutter_diff_args', '')
call s:set('g:gitgutter_diff_base', '')
call s:set('g:gitgutter_map_keys', 1)
call s:set('g:gitgutter_avoid_cmd_prompt_on_windows', 1)
call s:set('g:gitgutter_terminal_reports_focus', 1)
call s:set('g:gitgutter_async', 1)
call s:set('g:gitgutter_log', 0)
call s:set('g:gitgutter_git_executable', 'git')
call s:set('g:gitgutter_git_executable', 'git')
if !executable(g:gitgutter_git_executable)
call gitgutter#utility#warn('cannot find git. Please set g:gitgutter_git_executable.')
endif
let default_grep = 'grep'
call s:set('g:gitgutter_grep', default_grep)
if !empty(g:gitgutter_grep)
if executable(split(g:gitgutter_grep)[0])
if $GREP_OPTIONS =~# '--color=always'
let g:gitgutter_grep .= ' --color=never'
endif
else
if g:gitgutter_grep !=# default_grep
call gitgutter#utility#warn('cannot find '.g:gitgutter_grep.'. Please check g:gitgutter_grep.')
endif
let g:gitgutter_grep = ''
endif
endif
call gitgutter#highlight#define_sign_column_highlight()
call gitgutter#highlight#define_highlights()
call gitgutter#highlight#define_signs()
@ -70,40 +80,39 @@ call gitgutter#highlight#define_signs()
" Primary functions {{{
command -bar GitGutterAll call gitgutter#all()
command -bar GitGutter call gitgutter#process_buffer(bufnr(''), 0)
command! -bar GitGutterAll call gitgutter#all(1)
command! -bar GitGutter call gitgutter#process_buffer(bufnr(''), 1)
command -bar GitGutterDisable call gitgutter#disable()
command -bar GitGutterEnable call gitgutter#enable()
command -bar GitGutterToggle call gitgutter#toggle()
command! -bar GitGutterDisable call gitgutter#disable()
command! -bar GitGutterEnable call gitgutter#enable()
command! -bar GitGutterToggle call gitgutter#toggle()
" }}}
" Line highlights {{{
command -bar GitGutterLineHighlightsDisable call gitgutter#line_highlights_disable()
command -bar GitGutterLineHighlightsEnable call gitgutter#line_highlights_enable()
command -bar GitGutterLineHighlightsToggle call gitgutter#line_highlights_toggle()
command! -bar GitGutterLineHighlightsDisable call gitgutter#highlight#line_disable()
command! -bar GitGutterLineHighlightsEnable call gitgutter#highlight#line_enable()
command! -bar GitGutterLineHighlightsToggle call gitgutter#highlight#line_toggle()
" }}}
" Signs {{{
command -bar GitGutterSignsEnable call gitgutter#signs_enable()
command -bar GitGutterSignsDisable call gitgutter#signs_disable()
command -bar GitGutterSignsToggle call gitgutter#signs_toggle()
command! -bar GitGutterSignsEnable call gitgutter#sign#enable()
command! -bar GitGutterSignsDisable call gitgutter#sign#disable()
command! -bar GitGutterSignsToggle call gitgutter#sign#toggle()
" }}}
" Hunks {{{
command -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_hunk(<count>)
command -bar -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk(<count>)
command! -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_hunk(<count>)
command! -bar -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk(<count>)
command -bar GitGutterStageHunk call gitgutter#stage_hunk()
command -bar GitGutterUndoHunk call gitgutter#undo_hunk()
command -bar GitGutterRevertHunk echomsg 'GitGutterRevertHunk is deprecated. Use GitGutterUndoHunk'<Bar>call gitgutter#undo_hunk()
command -bar GitGutterPreviewHunk call gitgutter#preview_hunk()
command! -bar GitGutterStageHunk call gitgutter#hunk#stage()
command! -bar GitGutterUndoHunk call gitgutter#hunk#undo()
command! -bar GitGutterPreviewHunk call gitgutter#hunk#preview()
" Hunk text object
onoremap <silent> <Plug>GitGutterTextObjectInnerPending :<C-U>call gitgutter#hunk#text_object(1)<CR>
@ -130,7 +139,8 @@ xnoremap <silent> <Plug>GitGutterTextObjectOuterVisual :<C-U>call gitgutter#hun
" `line` - refers to the line number where the change starts
" `count` - refers to the number of lines the change covers
function! GitGutterGetHunks()
return gitgutter#utility#is_active() ? gitgutter#hunk#hunks() : []
let bufnr = bufnr('')
return gitgutter#utility#is_active(bufnr) ? gitgutter#hunk#hunks(bufnr) : []
endfunction
" Returns an array that contains a summary of the hunk status for the current
@ -142,7 +152,7 @@ endfunction
" }}}
command -bar GitGutterDebug call gitgutter#debug#debug()
command! -bar GitGutterDebug call gitgutter#debug#debug()
" Maps {{{
@ -169,7 +179,6 @@ if g:gitgutter_map_keys
endif
if !hasmapto('<Plug>GitGutterUndoHunk') && maparg('<Leader>hu', 'n') ==# ''
nmap <Leader>hu <Plug>GitGutterUndoHunk
nmap <Leader>hr <Plug>GitGutterUndoHunk:echomsg '<Leader>hr is deprecated. Use <Leader>hu'<CR>
endif
if !hasmapto('<Plug>GitGutterPreviewHunk') && maparg('<Leader>hp', 'n') ==# ''
nmap <Leader>hp <Plug>GitGutterPreviewHunk
@ -196,35 +205,26 @@ endif
augroup gitgutter
autocmd!
if g:gitgutter_realtime
autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 1)
endif
autocmd TabEnter * let t:gitgutter_didtabenter = 1
if g:gitgutter_eager
autocmd BufWritePost,FileChangedShellPost,ShellCmdPost * call gitgutter#process_buffer(bufnr(''), 0)
autocmd BufEnter *
\ if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter |
\ let t:gitgutter_didtabenter = 0 |
\ call gitgutter#all(!g:gitgutter_terminal_reports_focus) |
\ else |
\ call gitgutter#init_buffer(bufnr('')) |
\ call gitgutter#process_buffer(bufnr(''), !g:gitgutter_terminal_reports_focus) |
\ endif
autocmd BufEnter *
\ if gettabvar(tabpagenr(), 'gitgutter_didtabenter') |
\ call settabvar(tabpagenr(), 'gitgutter_didtabenter', 0) |
\ call gitgutter#all() |
\ else |
\ call gitgutter#process_buffer(bufnr(''), 0) |
\ endif
autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 0)
autocmd FileChangedShellPost,ShellCmdPost * call gitgutter#process_buffer(bufnr(''), 1)
autocmd TabEnter * call settabvar(tabpagenr(), 'gitgutter_didtabenter', 1)
" Ensure that all buffers are processed when opening vim with multiple files, e.g.:
"
" vim -o file1 file2
autocmd VimEnter * if winnr() != winnr('$') | call gitgutter#all(0) | endif
" Ensure that all buffers are processed when opening vim with multiple files, e.g.:
"
" vim -o file1 file2
autocmd VimEnter * if winnr() != winnr('$') | :GitGutterAll | endif
if !has('gui_win32')
autocmd FocusGained * call gitgutter#all()
endif
else
autocmd BufRead,BufWritePost,FileChangedShellPost * call gitgutter#process_buffer(bufnr(''), 0)
endif
autocmd FocusGained * call gitgutter#all(1)
autocmd ColorScheme * call gitgutter#highlight#define_sign_column_highlight() | call gitgutter#highlight#define_highlights()

View File

@ -0,0 +1,8 @@
The quick brown fox jumps
over the lazy dog
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>͂ɂقւƂ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʂ<EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>‚˂Ȃ<EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̂<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>܂<EFBFBD><EFBFBD>ӂ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߂݂<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ђ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

View File

@ -2,6 +2,8 @@
VIM="/Applications/MacVim.app/Contents/MacOS/Vim -v"
export VIM_GITGUTTER_TEST=1
$VIM -u NONE -U NONE -N \
--cmd 'set rtp+=../' \
--cmd 'let g:gitgutter_async=0' \

View File

@ -31,6 +31,10 @@ function s:git_diff_staged()
return split(system('git diff -U0 --staged fixture.txt'), '\n')
endfunction
function s:trigger_gitgutter()
doautocmd CursorHold
endfunction
"
" SetUp / TearDown
@ -40,7 +44,8 @@ function SetUp()
call system("git init ".s:test_repo.
\ " && cd ".s:test_repo.
\ " && cp ../fixture.txt .".
\ " && git add . && git commit -m 'initial'")
\ " && git add . && git commit -m 'initial'".
\ " && git config diff.mnemonicPrefix false")
execute ':cd' s:test_repo
edit! fixture.txt
call gitgutter#sign#reset()
@ -64,7 +69,7 @@ endfunction
function Test_add_lines()
normal ggo*
write
call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -76,7 +81,7 @@ function Test_add_lines_fish()
set shell=/usr/local/bin/fish
normal ggo*
write
call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -87,7 +92,7 @@ endfunction
function Test_modify_lines()
normal ggi*
write
call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -96,7 +101,7 @@ endfunction
function Test_remove_lines()
execute '5d'
write
call s:trigger_gitgutter()
let expected = ["line=4 id=3000 name=GitGutterLineRemoved"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -105,7 +110,7 @@ endfunction
function Test_remove_first_lines()
execute '1d'
write
call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineRemovedFirstLine"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -115,7 +120,7 @@ endfunction
function Test_edit_file_with_same_name_as_a_branch()
normal 5Gi*
call system('git checkout -b fixture.txt')
write
call s:trigger_gitgutter()
let expected = ["line=5 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -127,7 +132,7 @@ function Test_file_added_to_git()
call system('touch '.tmpfile.' && git add '.tmpfile)
execute 'edit '.tmpfile
normal ihello
write
call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fileAddedToGit.tmp'))
@ -138,7 +143,7 @@ function Test_filename_with_equals()
call system('touch =fixture=.txt && git add =fixture=.txt')
edit =fixture=.txt
normal ggo*
write
call s:trigger_gitgutter()
let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded',
@ -152,7 +157,7 @@ function Test_filename_with_square_brackets()
call system('touch fix[tu]re.txt && git add fix[tu]re.txt')
edit fix[tu]re.txt
normal ggo*
write
call s:trigger_gitgutter()
let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded',
@ -168,7 +173,7 @@ function Test_follow_symlink()
call system('ln -nfs fixture.txt '.tmp)
execute 'edit '.tmp
6d
write
call s:trigger_gitgutter()
let expected = ['line=5 id=3000 name=GitGutterLineRemoved']
call assert_equal(expected, s:signs('symlink'))
@ -183,7 +188,7 @@ function Test_keep_alt()
call assert_equal('', bufname('#'))
normal ggx
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal('', bufname('#'))
endfunction
@ -193,7 +198,7 @@ function Test_keep_modified()
normal 5Go*
call assert_equal(1, getbufvar('', '&modified'))
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal(1, getbufvar('', '&modified'))
endfunction
@ -204,7 +209,7 @@ function Test_keep_op_marks()
call assert_equal([0,6,1,0], getpos("'["))
call assert_equal([0,6,2,0], getpos("']"))
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal([0,6,1,0], getpos("'["))
call assert_equal([0,6,2,0], getpos("']"))
@ -218,26 +223,15 @@ endfunction
function Test_orphaned_signs()
execute "normal 5GoX\<CR>Y"
write
call s:trigger_gitgutter()
6d
write
call s:trigger_gitgutter()
let expected = ['line=6 id=3001 name=GitGutterLineAdded']
call assert_equal(expected, s:signs('fixture.txt'))
endfunction
function Test_sign_column_always()
let g:gitgutter_sign_column_always=1
write
let expected = ['line=9999 id=2999 name=GitGutterDummy']
call assert_equal(expected, s:signs('fixture.txt'))
let g:gitgutter_sign_column_always=0
endfunction
function Test_untracked_file_outside_repo()
let tmp = tempname()
call system('touch '.tmp)
@ -252,7 +246,7 @@ function Test_untracked_file_within_repo()
call system('touch '.tmp)
execute 'edit '.tmp
normal ggo*
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal([], s:signs(tmp))
@ -265,7 +259,7 @@ function Test_untracked_file_square_brackets_within_repo()
call system('touch '.tmp)
execute 'edit '.tmp
normal ggo*
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal([], s:signs(tmp))
@ -301,8 +295,19 @@ function Test_hunk_stage()
call assert_equal([], s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
" Buffer is unsaved
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index ae8e546..f5c6aff 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -5 +5 @@ d',
\ '-*e',
\ '+e'
\ ]
call assert_equal(expected, s:git_diff())
" Index has been updated
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..ae8e546 100644',
@ -313,6 +318,11 @@ function Test_hunk_stage()
\ '+*e'
\ ]
call assert_equal(expected, s:git_diff_staged())
" Save the buffer
write
call assert_equal([], s:git_diff())
endfunction
@ -329,6 +339,31 @@ function Test_hunk_stage_nearby_hunk()
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
" Buffer is unsaved
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index 53b13df..f5c6aff 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -3,0 +4 @@ c',
\ '+d',
\ ]
call assert_equal(expected, s:git_diff())
" Index has been updated
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..53b13df 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -4 +3,0 @@ c',
\ '-d',
\ ]
call assert_equal(expected, s:git_diff_staged())
" Save the buffer
write
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index 53b13df..8fdfda7 100644',
@ -340,16 +375,6 @@ function Test_hunk_stage_nearby_hunk()
\ '+z',
\ ]
call assert_equal(expected, s:git_diff())
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..53b13df 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -4 +3,0 @@ c',
\ '-d',
\ ]
call assert_equal(expected, s:git_diff_staged())
endfunction
@ -359,7 +384,6 @@ function Test_hunk_undo()
normal 5Gi*
GitGutterUndoHunk
write " write file so we can verify git diff (--staged)
call assert_equal('foo', &shell)
let &shell = _shell
@ -374,8 +398,9 @@ function Test_undo_nearby_hunk()
execute "normal! 2Gox\<CR>y\<CR>z"
normal 2jdd
normal k
call s:trigger_gitgutter()
GitGutterUndoHunk
write " write file so we can verify git diff (--staged)
call s:trigger_gitgutter()
let expected = [
\ 'line=3 id=3000 name=GitGutterLineAdded',
@ -384,6 +409,13 @@ function Test_undo_nearby_hunk()
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
call assert_equal([], s:git_diff_staged())
" Save the buffer
write
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..3fbde56 100644',
@ -396,5 +428,148 @@ function Test_undo_nearby_hunk()
\ ]
call assert_equal(expected, s:git_diff())
call assert_equal([], s:git_diff_staged())
endfunction
function Test_write_option()
set nowrite
normal ggo*
call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
set write
endfunction
function Test_inner_text_object()
execute "normal! 2Gox\<CR>y\<CR>z\<CR>\<CR>"
call s:trigger_gitgutter()
normal dic
call s:trigger_gitgutter()
call assert_equal([], s:signs('fixture.txt'))
call assert_equal(readfile('fixture.txt'), getline(1,'$'))
" Excludes trailing lines
normal 9Gi*
normal 10Gi*
call s:trigger_gitgutter()
execute "normal vic\<Esc>"
call assert_equal([9, 10], [line("'<"), line("'>")])
endfunction
function Test_around_text_object()
execute "normal! 2Gox\<CR>y\<CR>z\<CR>\<CR>"
call s:trigger_gitgutter()
normal dac
call s:trigger_gitgutter()
call assert_equal([], s:signs('fixture.txt'))
call assert_equal(readfile('fixture.txt'), getline(1,'$'))
" Includes trailing lines
normal 9Gi*
normal 10Gi*
call s:trigger_gitgutter()
execute "normal vac\<Esc>"
call assert_equal([9, 11], [line("'<"), line("'>")])
endfunction
function Test_user_autocmd()
autocmd User GitGutter let s:autocmd_user = g:gitgutter_hook_context.bufnr
" Verify not fired when nothing changed.
let s:autocmd_user = 0
call s:trigger_gitgutter()
call assert_equal(0, s:autocmd_user)
" Verify fired when there was a change.
normal ggo*
let bufnr = bufnr('')
call s:trigger_gitgutter()
call assert_equal(bufnr, s:autocmd_user)
endfunction
function Test_fix_file_references()
" No special characters
let hunk_diff = join([
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..3fbde56 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
let filepath = 'blah.txt'
let expected = join([
\ 'diff --git a/blah.txt b/blah.txt',
\ 'index f5c6aff..3fbde56 100644',
\ '--- a/blah.txt',
\ '+++ b/blah.txt',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff))
" diff.mnemonicPrefix; spaces in filename
let hunk_diff = join([
\ 'diff --git i/x/cat dog w/x/cat dog',
\ 'index f5c6aff..3fbde56 100644',
\ '--- i/x/cat dog',
\ '+++ w/x/cat dog',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
let filepath = 'blah.txt'
let expected = join([
\ 'diff --git i/blah.txt w/blah.txt',
\ 'index f5c6aff..3fbde56 100644',
\ '--- i/blah.txt',
\ '+++ w/blah.txt',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff))
" Backslashes in filename; quotation marks
let hunk_diff = join([
\ 'diff --git "a/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\11.1.vim" "b/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\12.1.vim"',
\ 'index f42aeb0..4930403 100644',
\ '--- "a/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\11.1.vim"',
\ '+++ "b/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\12.1.vim"',
\ '@@ -172,0 +173 @@ stuff',
\ '+x'
\ ], "\n")."\n"
let filepath = 'init.vim'
let expected = join([
\ 'diff --git "a/init.vim" "b/init.vim"',
\ 'index f42aeb0..4930403 100644',
\ '--- "a/init.vim"',
\ '+++ "b/init.vim"',
\ '@@ -172,0 +173 @@ stuff',
\ '+x'
\ ], "\n")."\n"
call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff))
endfunction
function Test_encoding()
call system('cp ../cp932.txt . && git add cp932.txt')
edit ++enc=cp932 cp932.txt
call s:trigger_gitgutter()
call assert_equal([], s:signs('cp932.txt'))
endfunction

View File

@ -0,0 +1,27 @@
" Measure how long it takes to unplace signs.
"
" Source this file with `:source %` or `vim -S unplace.vim`
let num = 500
sign define Foo text=*
new
call append(0, range(1, num))
for i in range(1, num)
execute "sign place ".i." line=".i." name=Foo buffer=".bufnr('')
endfor
let start = reltime()
for i in range(1, num)
execute "sign unplace ".i
endfor
let elapsed = reltime(start)
bdelete!
echom split(reltimestr(elapsed))[0]."s to remove ".num." signs"
echom string(reltimefloat(elapsed) * 1000 / num).' ms/sign'
echom string(float2nr(num / reltimefloat(elapsed))).' sign/s'