From 843ee5aed667283e635a7530f97ebb17795da942 Mon Sep 17 00:00:00 2001 From: Andy Stewart Date: Wed, 20 Feb 2013 10:53:23 +0100 Subject: [PATCH] First draft. --- README.mkd | 50 +++++++++++++++ plugin/gitgutter.vim | 147 +++++++++++++++++++++++++++++++++++++++++++ screenshot.png | Bin 0 -> 16191 bytes 3 files changed, 197 insertions(+) create mode 100644 README.mkd create mode 100644 plugin/gitgutter.vim create mode 100644 screenshot.png diff --git a/README.mkd b/README.mkd new file mode 100644 index 0000000..cd45b66 --- /dev/null +++ b/README.mkd @@ -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 diff --git a/plugin/gitgutter.vim b/plugin/gitgutter.vim new file mode 100644 index 0000000..bcb0fad --- /dev/null +++ b/plugin/gitgutter.vim @@ -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: diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6b50f839ec91fd623e1191f8ada8c823740be041 GIT binary patch literal 16191 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 P9zj7?MW#y1H01vSr(8@| literal 0 HcmV?d00001