mirror of
https://github.com/airblade/vim-gitgutter.git
synced 2025-11-08 11:33:48 -05:00
Autoload.
This commit is contained in:
155
autoload/diff.vim
Normal file
155
autoload/diff.vim
Normal file
@@ -0,0 +1,155 @@
|
||||
let s:grep_available = executable('grep')
|
||||
let s:grep_command = ' | ' . (g:gitgutter_escape_grep ? '\grep' : 'grep') . ' -e "^@@ "'
|
||||
|
||||
|
||||
function! diff#run_diff(realtime)
|
||||
if a:realtime
|
||||
let blob_name = ':./' . fnamemodify(utility#file(),':t')
|
||||
let blob_file = tempname()
|
||||
let cmd = 'git show ' . blob_name . ' > ' . blob_file . ' && diff -U0 ' . g:gitgutter_diff_args . ' ' . blob_file . ' - '
|
||||
else
|
||||
let cmd = 'git diff --no-ext-diff --no-color -U0 ' . g:gitgutter_diff_args . ' ' . shellescape(utility#file())
|
||||
endif
|
||||
if s:grep_available
|
||||
let cmd .= s:grep_command
|
||||
endif
|
||||
let cmd = utility#escape(cmd)
|
||||
if a:realtime
|
||||
if &fileformat ==# "dos"
|
||||
let eol = "\r\n"
|
||||
elseif &fileformat ==# "mac"
|
||||
let eol = "\r"
|
||||
else
|
||||
let eol = "\n"
|
||||
endif
|
||||
let buffer_contents = join(getline(1, '$'), eol) . eol
|
||||
let diff = system(utility#command_in_directory_of_file(cmd), buffer_contents)
|
||||
else
|
||||
let diff = system(utility#command_in_directory_of_file(cmd))
|
||||
endif
|
||||
return diff
|
||||
endfunction
|
||||
|
||||
function! diff#parse_diff(diff)
|
||||
let hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
|
||||
let hunks = []
|
||||
for line in split(a:diff, '\n')
|
||||
let matches = matchlist(line, hunk_re)
|
||||
if len(matches) > 0
|
||||
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! diff#process_hunks(hunks)
|
||||
call hunk#reset()
|
||||
let modified_lines = []
|
||||
for hunk in a:hunks
|
||||
call extend(modified_lines, diff#process_hunk(hunk))
|
||||
endfor
|
||||
return modified_lines
|
||||
endfunction
|
||||
|
||||
function! diff#process_hunk(hunk)
|
||||
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 diff#is_added(from_count, to_count)
|
||||
call diff#process_added(modifications, from_count, to_count, to_line)
|
||||
call hunk#increment_lines_added(to_count)
|
||||
|
||||
elseif diff#is_removed(from_count, to_count)
|
||||
call diff#process_removed(modifications, from_count, to_count, to_line)
|
||||
call hunk#increment_lines_removed(from_count)
|
||||
|
||||
elseif diff#is_modified(from_count, to_count)
|
||||
call diff#process_modified(modifications, from_count, to_count, to_line)
|
||||
call hunk#increment_lines_modified(to_count)
|
||||
|
||||
elseif diff#is_modified_and_added(from_count, to_count)
|
||||
call diff#process_modified_and_added(modifications, from_count, to_count, to_line)
|
||||
call hunk#increment_lines_added(to_count - from_count)
|
||||
call hunk#increment_lines_modified(from_count)
|
||||
|
||||
elseif diff#is_modified_and_removed(from_count, to_count)
|
||||
call diff#process_modified_and_removed(modifications, from_count, to_count, to_line)
|
||||
call hunk#increment_lines_modified(to_count)
|
||||
call hunk#increment_lines_removed(from_count - to_count)
|
||||
|
||||
endif
|
||||
return modifications
|
||||
endfunction
|
||||
|
||||
function! diff#is_added(from_count, to_count)
|
||||
return a:from_count == 0 && a:to_count > 0
|
||||
endfunction
|
||||
|
||||
function! diff#is_removed(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count == 0
|
||||
endfunction
|
||||
|
||||
function! diff#is_modified(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count > 0 && a:from_count == a:to_count
|
||||
endfunction
|
||||
|
||||
function! diff#is_modified_and_added(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count > 0 && a:from_count < a:to_count
|
||||
endfunction
|
||||
|
||||
function! diff#is_modified_and_removed(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count > 0 && a:from_count > a:to_count
|
||||
endfunction
|
||||
|
||||
function! diff#process_added(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'added'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! diff#process_removed(modifications, from_count, to_count, to_line)
|
||||
call add(a:modifications, [a:to_line, 'removed'])
|
||||
endfunction
|
||||
|
||||
function! diff#process_modified(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! diff#process_modified_and_added(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:from_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'added'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! diff#process_modified_and_removed(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
call add(a:modifications, [a:to_line + offset - 1, 'modified_removed'])
|
||||
endfunction
|
||||
65
autoload/highlight.vim
Normal file
65
autoload/highlight.vim
Normal file
@@ -0,0 +1,65 @@
|
||||
function! highlight#define_sign_column_highlight()
|
||||
highlight default link SignColumn LineNr
|
||||
endfunction
|
||||
|
||||
function! highlight#define_highlights()
|
||||
" Highlights used by the signs.
|
||||
highlight GitGutterAddDefault guifg=#009900 guibg=NONE ctermfg=2 ctermbg=NONE
|
||||
highlight GitGutterChangeDefault guifg=#bbbb00 guibg=NONE ctermfg=3 ctermbg=NONE
|
||||
highlight GitGutterDeleteDefault guifg=#ff2222 guibg=NONE ctermfg=1 ctermbg=NONE
|
||||
highlight default link GitGutterChangeDeleteDefault GitGutterChangeDefault
|
||||
|
||||
highlight default link GitGutterAdd GitGutterAddDefault
|
||||
highlight default link GitGutterChange GitGutterChangeDefault
|
||||
highlight default link GitGutterDelete GitGutterDeleteDefault
|
||||
highlight default link GitGutterChangeDelete GitGutterChangeDeleteDefault
|
||||
|
||||
" Highlights used for the whole line.
|
||||
highlight default link GitGutterAddLine DiffAdd
|
||||
highlight default link GitGutterChangeLine DiffChange
|
||||
highlight default link GitGutterDeleteLine DiffDelete
|
||||
highlight default link GitGutterChangeDeleteLine GitGutterChangeLineDefault
|
||||
endfunction
|
||||
|
||||
function! highlight#define_signs()
|
||||
sign define GitGutterLineAdded
|
||||
sign define GitGutterLineModified
|
||||
sign define GitGutterLineRemoved
|
||||
sign define GitGutterLineModifiedRemoved
|
||||
sign define GitGutterDummy
|
||||
|
||||
if g:gitgutter_signs
|
||||
call highlight#define_sign_symbols()
|
||||
call highlight#define_sign_text_highlights()
|
||||
endif
|
||||
call highlight#define_sign_line_highlights()
|
||||
endfunction
|
||||
|
||||
function! highlight#define_sign_symbols()
|
||||
exe "sign define GitGutterLineAdded text=" . g:gitgutter_sign_added
|
||||
exe "sign define GitGutterLineModified text=" . g:gitgutter_sign_modified
|
||||
exe "sign define GitGutterLineRemoved text=" . g:gitgutter_sign_removed
|
||||
exe "sign define GitGutterLineModifiedRemoved text=" . g:gitgutter_sign_modified_removed
|
||||
endfunction
|
||||
|
||||
function! highlight#define_sign_text_highlights()
|
||||
sign define GitGutterLineAdded texthl=GitGutterAdd
|
||||
sign define GitGutterLineModified texthl=GitGutterChange
|
||||
sign define GitGutterLineRemoved texthl=GitGutterDelete
|
||||
sign define GitGutterLineModifiedRemoved texthl=GitGutterChangeDelete
|
||||
endfunction
|
||||
|
||||
function! highlight#define_sign_line_highlights()
|
||||
if g:gitgutter_highlight_lines
|
||||
sign define GitGutterLineAdded linehl=GitGutterAddLine
|
||||
sign define GitGutterLineModified linehl=GitGutterChangeLine
|
||||
sign define GitGutterLineRemoved linehl=GitGutterDeleteLine
|
||||
sign define GitGutterLineModifiedRemoved linehl=GitGutterChangeDeleteLine
|
||||
else
|
||||
sign define GitGutterLineAdded linehl=
|
||||
sign define GitGutterLineModified linehl=
|
||||
sign define GitGutterLineRemoved linehl=
|
||||
sign define GitGutterLineModifiedRemoved linehl=
|
||||
endif
|
||||
redraw!
|
||||
endfunction
|
||||
24
autoload/hunk.vim
Normal file
24
autoload/hunk.vim
Normal file
@@ -0,0 +1,24 @@
|
||||
" number of lines [added, modified, removed]
|
||||
let s:summary = [0, 0, 0]
|
||||
|
||||
function! hunk#summary()
|
||||
return s:summary
|
||||
endfunction
|
||||
|
||||
function! hunk#reset()
|
||||
let s:summary = [0, 0, 0] " TODO: is bling/airline expecting [-1, -1, -1]?
|
||||
endfunction
|
||||
|
||||
function! hunk#increment_lines_added(count)
|
||||
let s:summary[0] += a:count
|
||||
endfunction
|
||||
|
||||
function! hunk#increment_lines_modified(count)
|
||||
let s:summary[1] += a:count
|
||||
endfunction
|
||||
|
||||
function! hunk#increment_lines_removed(count)
|
||||
let s:summary[2] += a:count
|
||||
endfunction
|
||||
|
||||
|
||||
84
autoload/sign.vim
Normal file
84
autoload/sign.vim
Normal file
@@ -0,0 +1,84 @@
|
||||
" Vim doesn't namespace sign ids so every plugin shares the same
|
||||
" namespace. Sign ids are simply integers so to avoid clashes with other
|
||||
" signs we guess at a clear run.
|
||||
"
|
||||
" Note also we currently never reset s:next_sign_id.
|
||||
let s:first_sign_id = 3000
|
||||
let s:next_sign_id = s:first_sign_id
|
||||
let s:sign_ids = {} " key: filename, value: list of sign ids
|
||||
let s:other_signs = []
|
||||
let s:dummy_sign_id = 153
|
||||
|
||||
|
||||
function! sign#clear_signs(file_name)
|
||||
if exists('s:sign_ids') && has_key(s:sign_ids, a:file_name)
|
||||
for id in s:sign_ids[a:file_name]
|
||||
exe ":sign unplace" id "file=" . a:file_name
|
||||
endfor
|
||||
let s:sign_ids[a:file_name] = []
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" This assumes there are no GitGutter signs in the file.
|
||||
" If this is untenable we could change the regexp to exclude GitGutter's
|
||||
" signs.
|
||||
function! sign#find_other_signs(file_name)
|
||||
redir => signs
|
||||
silent exe ":sign place file=" . a:file_name
|
||||
redir END
|
||||
let s:other_signs = []
|
||||
for sign_line in split(signs, '\n')
|
||||
if sign_line =~ '^\s\+\w\+='
|
||||
let matches = matchlist(sign_line, '^\s\+\w\+=\(\d\+\)')
|
||||
let line_number = str2nr(matches[1])
|
||||
call add(s:other_signs, line_number)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! sign#show_signs(file_name, modified_lines)
|
||||
for line in a:modified_lines
|
||||
let line_number = line[0]
|
||||
let type = 'GitGutterLine' . utility#snake_case_to_camel_case(line[1])
|
||||
call sign#add_sign(line_number, type, a:file_name)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! sign#add_sign(line_number, name, file_name)
|
||||
let id = sign#next_sign_id()
|
||||
if !sign#is_other_sign(a:line_number) " Don't clobber other people's signs.
|
||||
exe ":sign place" id "line=" . a:line_number "name=" . a:name "file=" . a:file_name
|
||||
call sign#remember_sign(id, a:file_name)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! sign#next_sign_id()
|
||||
let next_id = s:next_sign_id
|
||||
let s:next_sign_id += 1
|
||||
return next_id
|
||||
endfunction
|
||||
|
||||
function! sign#remember_sign(id, file_name)
|
||||
if has_key(s:sign_ids, a:file_name)
|
||||
let sign_ids_for_file = s:sign_ids[a:file_name]
|
||||
call add(sign_ids_for_file, a:id)
|
||||
else
|
||||
let sign_ids_for_file = [a:id]
|
||||
endif
|
||||
let s:sign_ids[a:file_name] = sign_ids_for_file
|
||||
endfunction
|
||||
|
||||
function! sign#is_other_sign(line_number)
|
||||
return index(s:other_signs, a:line_number) == -1 ? 0 : 1
|
||||
endfunction
|
||||
|
||||
function! sign#add_dummy_sign()
|
||||
let last_line = line('$')
|
||||
exe ":sign place" s:dummy_sign_id "line=" . (last_line + 1) "name=GitGutterDummy file=" . utility#file()
|
||||
endfunction
|
||||
|
||||
function! sign#remove_dummy_sign()
|
||||
if exists('s:dummy_sign_id')
|
||||
exe ":sign unplace" s:dummy_sign_id "file=" . utility#file()
|
||||
endif
|
||||
endfunction
|
||||
77
autoload/utility.vim
Normal file
77
autoload/utility.vim
Normal file
@@ -0,0 +1,77 @@
|
||||
let s:file = ''
|
||||
|
||||
function! utility#is_active()
|
||||
return g:gitgutter_enabled && utility#exists_file() && utility#is_in_a_git_repo() && utility#is_tracked_by_git()
|
||||
endfunction
|
||||
|
||||
function! utility#current_file()
|
||||
return expand('%:p')
|
||||
endfunction
|
||||
|
||||
function! utility#set_file(file)
|
||||
let s:file = a:file
|
||||
endfunction
|
||||
|
||||
function! utility#file()
|
||||
return s:file
|
||||
endfunction
|
||||
|
||||
function! utility#exists_file()
|
||||
return filereadable(utility#file())
|
||||
endfunction
|
||||
|
||||
function! utility#directory_of_file()
|
||||
return shellescape(fnamemodify(utility#file(), ':h'))
|
||||
endfunction
|
||||
|
||||
function! utility#has_unsaved_changes(file)
|
||||
return getbufvar(a:file, "&mod")
|
||||
endfunction
|
||||
|
||||
" https://github.com/tpope/vim-dispatch/blob/9cdd05a87f8a47120335be03dfcd8358544221cd/autoload/dispatch/windows.vim#L8-L17
|
||||
function! utility#escape(str)
|
||||
if &shellxquote ==# '"'
|
||||
return '"' . substitute(a:str, '"', '""', 'g') . '"'
|
||||
else
|
||||
let esc = exists('+shellxescape') ? &shellxescape : '"&|<>()@^'
|
||||
return &shellquote .
|
||||
\ substitute(a:str, '['.esc.']', '&', 'g') .
|
||||
\ get({'(': ')', '"(': ')"'}, &shellquote, &shellquote)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! utility#discard_stdout_and_stderr()
|
||||
if !exists('utility#discard')
|
||||
if &shellredir ==? '>%s 2>&1'
|
||||
let utility#discard = ' > /dev/null 2>&1'
|
||||
else
|
||||
let utility#discard = ' >& /dev/null'
|
||||
endif
|
||||
endif
|
||||
return utility#discard
|
||||
endfunction
|
||||
|
||||
function! utility#command_in_directory_of_file(cmd)
|
||||
let utility#cmd_in_dir = 'cd ' . utility#directory_of_file() . ' && ' . a:cmd
|
||||
return substitute(utility#cmd_in_dir, "'", '"', 'g')
|
||||
endfunction
|
||||
|
||||
function! utility#is_in_a_git_repo()
|
||||
let cmd = utility#escape('git rev-parse' . utility#discard_stdout_and_stderr())
|
||||
call system(utility#command_in_directory_of_file(cmd))
|
||||
return !v:shell_error
|
||||
endfunction
|
||||
|
||||
function! utility#is_tracked_by_git()
|
||||
let cmd = utility#escape('git ls-files --error-unmatch' . utility#discard_stdout_and_stderr() . ' ' . shellescape(utility#file()))
|
||||
call system(utility#command_in_directory_of_file(cmd))
|
||||
return !v:shell_error
|
||||
endfunction
|
||||
|
||||
function! utility#differences(hunks)
|
||||
return len(a:hunks) != 0
|
||||
endfunction
|
||||
|
||||
function! utility#snake_case_to_camel_case(text)
|
||||
return substitute(a:text, '\v(.)(\a+)(_(.)(.+))?', '\u\1\l\2\u\4\l\5', '')
|
||||
endfunction
|
||||
@@ -18,7 +18,7 @@ endfunction
|
||||
call s:set('g:gitgutter_enabled', 1)
|
||||
call s:set('g:gitgutter_signs', 1)
|
||||
call s:set('g:gitgutter_highlight_lines', 0)
|
||||
let s:highlight_lines = g:gitgutter_highlight_lines
|
||||
" let s:highlight_lines = g:gitgutter_highlight_lines
|
||||
call s:set('g:gitgutter_sign_column_always', 0)
|
||||
call s:set('g:gitgutter_realtime', 1)
|
||||
call s:set('g:gitgutter_eager', 1)
|
||||
@@ -29,425 +29,17 @@ call s:set('g:gitgutter_sign_modified_removed', '~_')
|
||||
call s:set('g:gitgutter_diff_args', '')
|
||||
call s:set('g:gitgutter_escape_grep', 0)
|
||||
|
||||
let s:file = ''
|
||||
let s:hunk_summary = [0, 0, 0]
|
||||
|
||||
function! s:init()
|
||||
if !exists('g:gitgutter_initialised')
|
||||
call s:define_sign_column_highlight()
|
||||
call s:define_highlights()
|
||||
call s:define_signs()
|
||||
|
||||
" Vim doesn't namespace sign ids so every plugin shares the same
|
||||
" namespace. Sign ids are simply integers so to avoid clashes with other
|
||||
" signs we guess at a clear run.
|
||||
"
|
||||
" Note also we currently never reset s:next_sign_id.
|
||||
let s:first_sign_id = 3000
|
||||
let s:next_sign_id = s:first_sign_id
|
||||
let s:sign_ids = {} " key: filename, value: list of sign ids
|
||||
let s:other_signs = []
|
||||
let s:dummy_sign_id = 153
|
||||
|
||||
let s:grep_available = executable('grep')
|
||||
let s:grep_command = ' | ' . (g:gitgutter_escape_grep ? '\grep' : 'grep') . ' -e "^@@ "'
|
||||
|
||||
call highlight#define_sign_column_highlight()
|
||||
call highlight#define_highlights()
|
||||
call highlight#define_signs()
|
||||
let g:gitgutter_initialised = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Utility {{{
|
||||
|
||||
function! s:is_active()
|
||||
return g:gitgutter_enabled && s:exists_file() && s:is_in_a_git_repo() && s:is_tracked_by_git()
|
||||
endfunction
|
||||
|
||||
function! s:current_file()
|
||||
return expand('%:p')
|
||||
endfunction
|
||||
|
||||
function! s:set_file(file)
|
||||
let s:file = a:file
|
||||
endfunction
|
||||
|
||||
function! s:file()
|
||||
return s:file
|
||||
endfunction
|
||||
|
||||
function! s:exists_file()
|
||||
return filereadable(s:file())
|
||||
endfunction
|
||||
|
||||
function! s:directory_of_file()
|
||||
return shellescape(fnamemodify(s:file(), ':h'))
|
||||
endfunction
|
||||
|
||||
function! s:has_unsaved_changes(file)
|
||||
return getbufvar(a:file, "&mod")
|
||||
endfunction
|
||||
|
||||
function! s:reset_hunk_summary()
|
||||
let s:hunk_summary = [-1, -1, -1]
|
||||
endfunction
|
||||
|
||||
" https://github.com/tpope/vim-dispatch/blob/9cdd05a87f8a47120335be03dfcd8358544221cd/autoload/dispatch/windows.vim#L8-L17
|
||||
function! s:escape(str)
|
||||
if &shellxquote ==# '"'
|
||||
return '"' . substitute(a:str, '"', '""', 'g') . '"'
|
||||
else
|
||||
let esc = exists('+shellxescape') ? &shellxescape : '"&|<>()@^'
|
||||
return &shellquote .
|
||||
\ substitute(a:str, '['.esc.']', '&', 'g') .
|
||||
\ get({'(': ')', '"(': ')"'}, &shellquote, &shellquote)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:discard_stdout_and_stderr()
|
||||
if !exists('s:discard')
|
||||
if &shellredir ==? '>%s 2>&1'
|
||||
let s:discard = ' > /dev/null 2>&1'
|
||||
else
|
||||
let s:discard = ' >& /dev/null'
|
||||
endif
|
||||
endif
|
||||
return s:discard
|
||||
endfunction
|
||||
|
||||
function! s:command_in_directory_of_file(cmd)
|
||||
let s:cmd_in_dir = 'cd ' . s:directory_of_file() . ' && ' . a:cmd
|
||||
return substitute(s:cmd_in_dir, "'", '"', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:is_in_a_git_repo()
|
||||
let cmd = s:escape('git rev-parse' . s:discard_stdout_and_stderr())
|
||||
call system(s:command_in_directory_of_file(cmd))
|
||||
return !v:shell_error
|
||||
endfunction
|
||||
|
||||
function! s:is_tracked_by_git()
|
||||
let cmd = s:escape('git ls-files --error-unmatch' . s:discard_stdout_and_stderr() . ' ' . shellescape(s:file()))
|
||||
call system(s:command_in_directory_of_file(cmd))
|
||||
return !v:shell_error
|
||||
endfunction
|
||||
|
||||
function! s:differences(hunks)
|
||||
return len(a:hunks) != 0
|
||||
endfunction
|
||||
|
||||
function! s:snake_case_to_camel_case(text)
|
||||
return substitute(a:text, '\v(.)(\a+)(_(.)(.+))?', '\u\1\l\2\u\4\l\5', '')
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Highlights and signs {{{
|
||||
|
||||
function! s:define_sign_column_highlight()
|
||||
highlight default link SignColumn LineNr
|
||||
endfunction
|
||||
|
||||
function! s:define_highlights()
|
||||
" Highlights used by the signs.
|
||||
highlight GitGutterAddDefault guifg=#009900 guibg=NONE ctermfg=2 ctermbg=NONE
|
||||
highlight GitGutterChangeDefault guifg=#bbbb00 guibg=NONE ctermfg=3 ctermbg=NONE
|
||||
highlight GitGutterDeleteDefault guifg=#ff2222 guibg=NONE ctermfg=1 ctermbg=NONE
|
||||
highlight default link GitGutterChangeDeleteDefault GitGutterChangeDefault
|
||||
|
||||
highlight default link GitGutterAdd GitGutterAddDefault
|
||||
highlight default link GitGutterChange GitGutterChangeDefault
|
||||
highlight default link GitGutterDelete GitGutterDeleteDefault
|
||||
highlight default link GitGutterChangeDelete GitGutterChangeDeleteDefault
|
||||
|
||||
" Highlights used for the whole line.
|
||||
highlight default link GitGutterAddLine DiffAdd
|
||||
highlight default link GitGutterChangeLine DiffChange
|
||||
highlight default link GitGutterDeleteLine DiffDelete
|
||||
highlight default link GitGutterChangeDeleteLine GitGutterChangeLineDefault
|
||||
endfunction
|
||||
|
||||
function! s:define_signs()
|
||||
sign define GitGutterLineAdded
|
||||
sign define GitGutterLineModified
|
||||
sign define GitGutterLineRemoved
|
||||
sign define GitGutterLineModifiedRemoved
|
||||
sign define GitGutterDummy
|
||||
|
||||
if g:gitgutter_signs
|
||||
call s:define_sign_symbols()
|
||||
call s:define_sign_text_highlights()
|
||||
endif
|
||||
call s:define_sign_line_highlights()
|
||||
endfunction
|
||||
|
||||
function! s:define_sign_symbols()
|
||||
exe "sign define GitGutterLineAdded text=" . g:gitgutter_sign_added
|
||||
exe "sign define GitGutterLineModified text=" . g:gitgutter_sign_modified
|
||||
exe "sign define GitGutterLineRemoved text=" . g:gitgutter_sign_removed
|
||||
exe "sign define GitGutterLineModifiedRemoved text=" . g:gitgutter_sign_modified_removed
|
||||
endfunction
|
||||
|
||||
function! s:define_sign_text_highlights()
|
||||
sign define GitGutterLineAdded texthl=GitGutterAdd
|
||||
sign define GitGutterLineModified texthl=GitGutterChange
|
||||
sign define GitGutterLineRemoved texthl=GitGutterDelete
|
||||
sign define GitGutterLineModifiedRemoved texthl=GitGutterChangeDelete
|
||||
endfunction
|
||||
|
||||
function! s:define_sign_line_highlights()
|
||||
if s:highlight_lines
|
||||
sign define GitGutterLineAdded linehl=GitGutterAddLine
|
||||
sign define GitGutterLineModified linehl=GitGutterChangeLine
|
||||
sign define GitGutterLineRemoved linehl=GitGutterDeleteLine
|
||||
sign define GitGutterLineModifiedRemoved linehl=GitGutterChangeDeleteLine
|
||||
else
|
||||
sign define GitGutterLineAdded linehl=
|
||||
sign define GitGutterLineModified linehl=
|
||||
sign define GitGutterLineRemoved linehl=
|
||||
sign define GitGutterLineModifiedRemoved linehl=
|
||||
endif
|
||||
redraw!
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Diff processing {{{
|
||||
|
||||
function! s:run_diff(realtime)
|
||||
if a:realtime
|
||||
let blob_name = ':./' . fnamemodify(s:file(),':t')
|
||||
let blob_file = tempname()
|
||||
let cmd = 'git show ' . blob_name . ' > ' . blob_file . ' && diff -U0 ' . g:gitgutter_diff_args . ' ' . blob_file . ' - '
|
||||
else
|
||||
let cmd = 'git diff --no-ext-diff --no-color -U0 ' . g:gitgutter_diff_args . ' ' . shellescape(s:file())
|
||||
endif
|
||||
if s:grep_available
|
||||
let cmd .= s:grep_command
|
||||
endif
|
||||
let cmd = s:escape(cmd)
|
||||
if a:realtime
|
||||
if &fileformat ==# "dos"
|
||||
let eol = "\r\n"
|
||||
elseif &fileformat ==# "mac"
|
||||
let eol = "\r"
|
||||
else
|
||||
let eol = "\n"
|
||||
endif
|
||||
let buffer_contents = join(getline(1, '$'), eol) . eol
|
||||
let diff = system(s:command_in_directory_of_file(cmd), buffer_contents)
|
||||
else
|
||||
let diff = system(s:command_in_directory_of_file(cmd))
|
||||
endif
|
||||
return diff
|
||||
endfunction
|
||||
|
||||
function! s:parse_diff(diff)
|
||||
let hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
|
||||
let hunks = []
|
||||
for line in split(a:diff, '\n')
|
||||
let matches = matchlist(line, hunk_re)
|
||||
if len(matches) > 0
|
||||
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 s:hunk_summary = [0, 0, 0]
|
||||
let modified_lines = []
|
||||
for hunk in a:hunks
|
||||
call extend(modified_lines, s:process_hunk(hunk))
|
||||
endfor
|
||||
return modified_lines
|
||||
endfunction
|
||||
|
||||
function! s:process_hunk(hunk)
|
||||
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 s:is_added(from_count, to_count)
|
||||
call s:process_added(modifications, from_count, to_count, to_line)
|
||||
let s:hunk_summary[0] += to_count
|
||||
|
||||
elseif s:is_removed(from_count, to_count)
|
||||
call s:process_removed(modifications, from_count, to_count, to_line)
|
||||
let s:hunk_summary[2] += from_count
|
||||
|
||||
elseif s:is_modified(from_count, to_count)
|
||||
call s:process_modified(modifications, from_count, to_count, to_line)
|
||||
let s:hunk_summary[1] += to_count
|
||||
|
||||
elseif s:is_modified_and_added(from_count, to_count)
|
||||
call s:process_modified_and_added(modifications, from_count, to_count, to_line)
|
||||
let s:hunk_summary[0] += to_count - from_count
|
||||
let s:hunk_summary[1] += from_count
|
||||
|
||||
elseif s:is_modified_and_removed(from_count, to_count)
|
||||
call s:process_modified_and_removed(modifications, from_count, to_count, to_line)
|
||||
let s:hunk_summary[1] += to_count
|
||||
let s:hunk_summary[2] += from_count - to_count
|
||||
|
||||
endif
|
||||
return modifications
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Diff utility {{{
|
||||
|
||||
function! s:is_added(from_count, to_count)
|
||||
return a:from_count == 0 && a:to_count > 0
|
||||
endfunction
|
||||
|
||||
function! s:is_removed(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count == 0
|
||||
endfunction
|
||||
|
||||
function! s:is_modified(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count > 0 && a:from_count == a:to_count
|
||||
endfunction
|
||||
|
||||
function! s:is_modified_and_added(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count > 0 && a:from_count < a:to_count
|
||||
endfunction
|
||||
|
||||
function! s:is_modified_and_removed(from_count, to_count)
|
||||
return a:from_count > 0 && a:to_count > 0 && a:from_count > a:to_count
|
||||
endfunction
|
||||
|
||||
function! s:process_added(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'added'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:process_removed(modifications, from_count, to_count, to_line)
|
||||
call add(a:modifications, [a:to_line, 'removed'])
|
||||
endfunction
|
||||
|
||||
function! s:process_modified(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:process_modified_and_added(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:from_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'added'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:process_modified_and_removed(modifications, from_count, to_count, to_line)
|
||||
let offset = 0
|
||||
while offset < a:to_count
|
||||
let line_number = a:to_line + offset
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
call add(a:modifications, [a:to_line + offset - 1, 'modified_removed'])
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Sign processing {{{
|
||||
|
||||
function! s:clear_signs(file_name)
|
||||
if exists('s:sign_ids') && has_key(s:sign_ids, a:file_name)
|
||||
for id in s:sign_ids[a:file_name]
|
||||
exe ":sign unplace" id "file=" . a:file_name
|
||||
endfor
|
||||
let s:sign_ids[a:file_name] = []
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" This assumes there are no GitGutter signs in the file.
|
||||
" If this is untenable we could change the regexp to exclude GitGutter's
|
||||
" signs.
|
||||
function! s:find_other_signs(file_name)
|
||||
redir => signs
|
||||
silent exe ":sign place file=" . a:file_name
|
||||
redir END
|
||||
let s:other_signs = []
|
||||
for sign_line in split(signs, '\n')
|
||||
if sign_line =~ '^\s\+\w\+='
|
||||
let matches = matchlist(sign_line, '^\s\+\w\+=\(\d\+\)')
|
||||
let line_number = str2nr(matches[1])
|
||||
call add(s:other_signs, line_number)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:show_signs(file_name, modified_lines)
|
||||
for line in a:modified_lines
|
||||
let line_number = line[0]
|
||||
let type = 'GitGutterLine' . s:snake_case_to_camel_case(line[1])
|
||||
call s:add_sign(line_number, type, a:file_name)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:add_sign(line_number, name, file_name)
|
||||
let id = s:next_sign_id()
|
||||
if !s:is_other_sign(a:line_number) " Don't clobber other people's signs.
|
||||
exe ":sign place" id "line=" . a:line_number "name=" . a:name "file=" . a:file_name
|
||||
call s:remember_sign(id, a:file_name)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:next_sign_id()
|
||||
let next_id = s:next_sign_id
|
||||
let s:next_sign_id += 1
|
||||
return next_id
|
||||
endfunction
|
||||
|
||||
function! s:remember_sign(id, file_name)
|
||||
if has_key(s:sign_ids, a:file_name)
|
||||
let sign_ids_for_file = s:sign_ids[a:file_name]
|
||||
call add(sign_ids_for_file, a:id)
|
||||
else
|
||||
let sign_ids_for_file = [a:id]
|
||||
endif
|
||||
let s:sign_ids[a:file_name] = sign_ids_for_file
|
||||
endfunction
|
||||
|
||||
function! s:is_other_sign(line_number)
|
||||
return index(s:other_signs, a:line_number) == -1 ? 0 : 1
|
||||
endfunction
|
||||
|
||||
function! s:add_dummy_sign()
|
||||
let last_line = line('$')
|
||||
exe ":sign place" s:dummy_sign_id "line=" . (last_line + 1) "name=GitGutterDummy file=" . s:file()
|
||||
endfunction
|
||||
|
||||
function! s:remove_dummy_sign()
|
||||
if exists('s:dummy_sign_id')
|
||||
exe ":sign unplace" s:dummy_sign_id "file=" . s:file()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Public interface {{{
|
||||
|
||||
@@ -460,45 +52,45 @@ command GitGutterAll call GitGutterAll()
|
||||
|
||||
" Supply optional argument to use realtime mode.
|
||||
function! GitGutter(file, ...)
|
||||
call s:set_file(a:file)
|
||||
if s:is_active()
|
||||
call utility#set_file(a:file)
|
||||
if utility#is_active()
|
||||
call s:init()
|
||||
if (a:0 == 1) || s:has_unsaved_changes(a:file)
|
||||
let diff = s:run_diff(1)
|
||||
if (a:0 == 1) || utility#has_unsaved_changes(a:file)
|
||||
let diff = diff#run_diff(1)
|
||||
else
|
||||
let diff = s:run_diff(0)
|
||||
let diff = diff#run_diff(0)
|
||||
endif
|
||||
let s:hunks = s:parse_diff(diff)
|
||||
let modified_lines = s:process_hunks(s:hunks)
|
||||
let s:hunks = diff#parse_diff(diff)
|
||||
let modified_lines = diff#process_hunks(s:hunks)
|
||||
if g:gitgutter_sign_column_always
|
||||
call s:add_dummy_sign()
|
||||
call sign#add_dummy_sign()
|
||||
else
|
||||
if s:differences(s:hunks)
|
||||
call s:add_dummy_sign() " prevent flicker
|
||||
if utility#differences(s:hunks)
|
||||
call sign#add_dummy_sign() " prevent flicker
|
||||
else
|
||||
call s:remove_dummy_sign()
|
||||
call sign#remove_dummy_sign()
|
||||
endif
|
||||
endif
|
||||
call s:clear_signs(a:file)
|
||||
call s:find_other_signs(a:file)
|
||||
call s:show_signs(a:file, modified_lines)
|
||||
call sign#clear_signs(a:file)
|
||||
call sign#find_other_signs(a:file)
|
||||
call sign#show_signs(a:file, modified_lines)
|
||||
else
|
||||
call s:reset_hunk_summary()
|
||||
call hunk#reset()
|
||||
endif
|
||||
endfunction
|
||||
command GitGutter call GitGutter(s:current_file())
|
||||
command GitGutter call GitGutter(utility#current_file())
|
||||
|
||||
function! GitGutterDisable()
|
||||
let g:gitgutter_enabled = 0
|
||||
call s:clear_signs(s:file())
|
||||
call s:remove_dummy_sign()
|
||||
call s:reset_hunk_summary()
|
||||
call sign#clear_signs(utility#file())
|
||||
call sign#remove_dummy_sign()
|
||||
call hunk#reset()
|
||||
endfunction
|
||||
command GitGutterDisable call GitGutterDisable()
|
||||
|
||||
function! GitGutterEnable()
|
||||
let g:gitgutter_enabled = 1
|
||||
call GitGutter(s:current_file())
|
||||
call GitGutter(utility#current_file())
|
||||
endfunction
|
||||
command GitGutterEnable call GitGutterEnable()
|
||||
|
||||
@@ -512,25 +104,25 @@ endfunction
|
||||
command GitGutterToggle call GitGutterToggle()
|
||||
|
||||
function! GitGutterLineHighlightsDisable()
|
||||
let s:highlight_lines = 0
|
||||
call s:define_sign_line_highlights()
|
||||
let g:gitgutter_highlight_lines = 0
|
||||
call highlight#define_sign_line_highlights()
|
||||
endfunction
|
||||
command GitGutterLineHighlightsDisable call GitGutterLineHighlightsDisable()
|
||||
|
||||
function! GitGutterLineHighlightsEnable()
|
||||
let s:highlight_lines = 1
|
||||
call s:define_sign_line_highlights()
|
||||
let g:gitgutter_highlight_lines = 1
|
||||
call highlight#define_sign_line_highlights()
|
||||
endfunction
|
||||
command GitGutterLineHighlightsEnable call GitGutterLineHighlightsEnable()
|
||||
|
||||
function! GitGutterLineHighlightsToggle()
|
||||
let s:highlight_lines = (s:highlight_lines ? 0 : 1)
|
||||
call s:define_sign_line_highlights()
|
||||
let g:gitgutter_highlight_lines = (g:gitgutter_highlight_lines ? 0 : 1)
|
||||
call highlight#define_sign_line_highlights()
|
||||
endfunction
|
||||
command GitGutterLineHighlightsToggle call GitGutterLineHighlightsToggle()
|
||||
|
||||
function! GitGutterNextHunk(count)
|
||||
if s:is_active()
|
||||
if utility#is_active()
|
||||
let current_line = line('.')
|
||||
let hunk_count = 0
|
||||
for hunk in s:hunks
|
||||
@@ -547,7 +139,7 @@ endfunction
|
||||
command -count=1 GitGutterNextHunk call GitGutterNextHunk(<count>)
|
||||
|
||||
function! GitGutterPrevHunk(count)
|
||||
if s:is_active()
|
||||
if utility#is_active()
|
||||
let current_line = line('.')
|
||||
let hunk_count = 0
|
||||
for hunk in reverse(copy(s:hunks))
|
||||
@@ -581,14 +173,14 @@ command -count=1 GitGutterPrevHunk call GitGutterPrevHunk(<count>)
|
||||
" `line` - refers to the line number where the change starts
|
||||
" `count` - refers to the number of lines the change covers
|
||||
function! GitGutterGetHunks()
|
||||
return s:is_active() ? s:hunks : []
|
||||
return utility#is_active() ? s:hunks : []
|
||||
endfunction
|
||||
|
||||
" Returns an array that contains a summary of the current hunk status.
|
||||
" The format is [ added, modified, removed ], where each value represents
|
||||
" the number of lines added/modified/removed respectively.
|
||||
function! GitGutterGetHunkSummary()
|
||||
return s:hunk_summary
|
||||
return hunk#summary()
|
||||
endfunction
|
||||
|
||||
nnoremap <silent> <Plug>GitGutterNextHunk :<C-U>execute v:count1 . "GitGutterNextHunk"<CR>
|
||||
@@ -603,19 +195,19 @@ augroup gitgutter
|
||||
autocmd!
|
||||
|
||||
if g:gitgutter_realtime
|
||||
autocmd CursorHold,CursorHoldI * call GitGutter(s:current_file(), 1)
|
||||
autocmd CursorHold,CursorHoldI * call GitGutter(utility#current_file(), 1)
|
||||
endif
|
||||
|
||||
if g:gitgutter_eager
|
||||
autocmd BufEnter,BufWritePost,FileWritePost,FileChangedShellPost * call GitGutter(s:current_file())
|
||||
autocmd BufEnter,BufWritePost,FileWritePost,FileChangedShellPost * call GitGutter(utility#current_file())
|
||||
autocmd TabEnter * call GitGutterAll()
|
||||
if !has('gui_win32')
|
||||
autocmd FocusGained * call GitGutterAll()
|
||||
endif
|
||||
else
|
||||
autocmd BufReadPost,BufWritePost,FileReadPost,FileWritePost,FileChangedShellPost * call GitGutter(s:current_file())
|
||||
autocmd BufReadPost,BufWritePost,FileReadPost,FileWritePost,FileChangedShellPost * call GitGutter(utility#current_file())
|
||||
endif
|
||||
autocmd ColorScheme * call s:define_sign_column_highlight() | call s:define_highlights()
|
||||
autocmd ColorScheme * call highlight#define_sign_column_highlight() | call highlight#define_highlights()
|
||||
augroup END
|
||||
|
||||
" }}}
|
||||
|
||||
Reference in New Issue
Block a user