First draft.

This commit is contained in:
Andy Stewart
2013-02-20 10:53:23 +01:00
commit 843ee5aed6
3 changed files with 197 additions and 0 deletions

50
README.mkd Normal file
View File

@@ -0,0 +1,50 @@
## Vim Git Gutter
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.
This is a port of the [Git Gutter][st2gg] plugin for Sublime Text 2.
### Screenshot
![screenshot](https://raw.github.com/airblade/vim-gitgutter/screenshot.png)
In the screenshot above you can see:
* Line 15 has been modified.
* Lines 21-24 are new.
* A line or lines were removed between lines 25 and 26.
### Installation
If you don't have a preferred installation method, I recommend installing [pathogen.vim][pathogen], and then simply copy and paste:
```
cd ~/.vim/bundle
git clone git://github.com/airblade/vim-gitgutter.git
```
### FAQ
> The colours in the sign column are weird.
The syntax highlighting for your sign column is probably set strangely. Either modify your colorscheme or add this to your `~.vimrc`:
```
highlight clear SignColumn
```
> Lines removed below a modified line are not shown.
True. This plugin uses Vim's signs which require a sign to be on a line (not between two lines) and only permit one sign per line. Removed lines are signed with an underscore on the line above. If that line has also been modified, the plugin has to choose whether to show the removed-lines sign or the modified-line sign. It prefers the latter.
### Intellectual Property
Copyright Andrew Stewart, AirBlade Software Ltd. Released under the MIT licence.
[st2gg]: https://github.com/jisaacks/GitGutter
[pathogen]: https://github.com/tpope/vim-pathogen

147
plugin/gitgutter.vim Normal file
View File

@@ -0,0 +1,147 @@
if exists('g:loaded_gitgutter') || !executable('git') || &cp
finish
endif
let g:loaded_gitgutter = 1
" Initialisation {{{
function! s:init()
if !exists('g:gitgutter_initialised')
call s:define_highlights()
call s:define_signs()
let g:gitgutter_initialised = 1
endif
endfunction
function! s:define_highlights()
highlight lineAdded guifg=#009900 guibg=NONE ctermfg=2 ctermbg=NONE
highlight lineModified guifg=#bbbb00 guibg=NONE ctermfg=3 ctermbg=NONE
highlight lineRemoved guifg=#ff2222 guibg=NONE ctermfg=1 ctermbg=NONE
endfunction
function! s:define_signs()
sign define line_added text=+ texthl=lineAdded
sign define line_modified text=~ texthl=lineModified
sign define line_removed text=_ texthl=lineRemoved
endfunction
" }}}
" Utility {{{
function! s:current_file()
return expand('%')
endfunction
function! s:is_in_a_git_repo()
call system('git rev-parse > /dev/null 2>&1')
return !v:shell_error
endfunction
function! s:is_tracked_by_git()
call system('git ls-files --error-unmatch > /dev/null 2>&1 ' . shellescape(s:current_file()))
return !v:shell_error
endfunction
" }}}
" Core logic {{{
function! s:run_diff()
let cmd = 'git diff --no-ext-diff -U0 ' . shellescape(s:current_file())
let diff = system(cmd)
return diff
endfunction
function! s:parse_diff(diff)
let hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
let hunks = []
for line in split(a:diff, '\n')
if line =~ '^@@\s'
let matches = matchlist(line, hunk_re)
let from_line = str2nr(matches[1])
let from_count = (matches[2] == '') ? 1 : str2nr(matches[2])
let to_line = str2nr(matches[3])
let to_count = (matches[4] == '') ? 1 : str2nr(matches[4])
call add(hunks, [from_line, from_count, to_line, to_count])
endif
endfor
return hunks
endfunction
function! s:process_hunks(hunks)
let modified_lines = []
for hunk in a:hunks
let from_line = hunk[0]
let from_count = hunk[1]
let to_line = hunk[2]
let to_count = hunk[3]
" added
if from_count == 0 && to_count > 0
let offset = 0
while offset < to_count
let line_number = to_line + offset
call add(modified_lines, [line_number, 'added'])
let offset += 1
endwhile
" removed
elseif from_count > 0 && to_count == 0
" removed lines came after `to_line`.
call add(modified_lines, [to_line, 'removed'])
" modified
else
let offset = 0
while offset < to_count
let line_number = to_line + offset
call add(modified_lines, [line_number, 'modified'])
let offset += 1
endwhile
endif
endfor
return modified_lines
endfunction
function! s:clear_signs()
sign unplace *
endfunction
function! s:show_signs(modified_lines)
let file_name = s:current_file()
for line in a:modified_lines
let line_number = line[0]
let type = line[1]
" TODO: eugh
if type ==? 'added'
let name = 'line_added'
elseif type ==? 'removed'
let name = 'line_removed'
elseif type ==? 'modified'
let name = 'line_modified'
endif
exe ":sign place " . line_number . " line=" . line_number . " name=" . name . " file=" . file_name
endfor
endfunction
" }}}
" Public interface {{{
function! GitGutter()
if s:is_in_a_git_repo() && s:is_tracked_by_git()
call s:init()
let diff = s:run_diff()
let hunks = s:parse_diff(diff)
let modified_lines = s:process_hunks(hunks)
call s:clear_signs()
call s:show_signs(modified_lines)
endif
endfunction
" }}}
augroup gitgutter
autocmd!
autocmd BufReadPost,BufWritePost,FileReadPost,FileWritePost,BufEnter * call GitGutter()
augroup END
" vim:set et sw=2:

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB