mirror of
https://github.com/airblade/vim-gitgutter.git
synced 2025-11-08 11:33:48 -05:00
148 lines
3.6 KiB
VimL
148 lines
3.6 KiB
VimL
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:
|