mirror of
https://github.com/amix/vimrc
synced 2025-06-16 01:25:00 +08:00
Fix #243
Cleanup corrupt submodules and add them again via update_plugins.py script
This commit is contained in:
253
sources_non_forked/vim-gitgutter/autoload/gitgutter.vim
Normal file
253
sources_non_forked/vim-gitgutter/autoload/gitgutter.vim
Normal file
@ -0,0 +1,253 @@
|
||||
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
|
||||
|
||||
" Primary functions {{{
|
||||
|
||||
function! gitgutter#all() abort
|
||||
for buffer_id in gitgutter#utility#dedup(tabpagebuflist())
|
||||
let file = expand('#' . buffer_id . ':p')
|
||||
if !empty(file)
|
||||
call gitgutter#process_buffer(buffer_id, 0)
|
||||
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()
|
||||
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
|
||||
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()
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#handle_diff(diff) abort
|
||||
call gitgutter#debug#log(a:diff)
|
||||
|
||||
call setbufvar(gitgutter#utility#bufnr(), 'gitgutter_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 = []
|
||||
for i in range(tabpagenr('$'))
|
||||
call extend(buflist, tabpagebuflist(i + 1))
|
||||
endfor
|
||||
|
||||
for buffer_id in gitgutter#utility#dedup(buflist)
|
||||
let file = expand('#' . buffer_id . ':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()
|
||||
endif
|
||||
endfor
|
||||
|
||||
let g:gitgutter_enabled = 0
|
||||
endfunction
|
||||
|
||||
function! gitgutter#enable() abort
|
||||
let g:gitgutter_enabled = 1
|
||||
call gitgutter#all()
|
||||
endfunction
|
||||
|
||||
function! gitgutter#toggle() abort
|
||||
if g:gitgutter_enabled
|
||||
call gitgutter#disable()
|
||||
else
|
||||
call gitgutter#enable()
|
||||
endif
|
||||
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!
|
||||
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!
|
||||
endfunction
|
||||
|
||||
function! gitgutter#line_highlights_toggle() abort
|
||||
if g:gitgutter_highlight_lines
|
||||
call gitgutter#line_highlights_disable()
|
||||
else
|
||||
call gitgutter#line_highlights_enable()
|
||||
endif
|
||||
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
|
||||
let wl = winline()
|
||||
silent edit
|
||||
let offset = wl - winline()
|
||||
execute "normal! ".offset."\<C-Y>"
|
||||
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
|
||||
|
||||
" }}}
|
211
sources_non_forked/vim-gitgutter/autoload/gitgutter/async.vim
Normal file
211
sources_non_forked/vim-gitgutter/autoload/gitgutter/async.vim
Normal file
@ -0,0 +1,211 @@
|
||||
let s:jobs = {}
|
||||
|
||||
" Nvim has always supported async commands.
|
||||
"
|
||||
" Vim introduced async in 7.4.1826.
|
||||
"
|
||||
" gVim didn't support aync until 7.4.1850 (though I haven't been able to
|
||||
" verify this myself).
|
||||
"
|
||||
" MacVim-GUI didn't support async until 7.4.1832 (actually commit
|
||||
" 88f4fe0 but 7.4.1832 was the first subsequent patch release).
|
||||
let s:available = has('nvim') || (
|
||||
\ has('job') && (
|
||||
\ (has('patch-7-4-1826') && !has('gui_running')) ||
|
||||
\ (has('patch-7-4-1850') && has('gui_running')) ||
|
||||
\ (has('patch-7-4-1832') && has('gui_macvim'))
|
||||
\ )
|
||||
\ )
|
||||
|
||||
function! gitgutter#async#available()
|
||||
return s:available
|
||||
endfunction
|
||||
|
||||
function! gitgutter#async#execute(cmd) abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
|
||||
if has('nvim')
|
||||
if has('unix')
|
||||
let command = ["sh", "-c", a:cmd]
|
||||
elseif has('win32')
|
||||
let command = ["cmd.exe", "/c", a:cmd]
|
||||
else
|
||||
throw 'unknown os'
|
||||
endif
|
||||
" Make the job use a shell while avoiding (un)quoting problems.
|
||||
let job_id = jobstart(command, {
|
||||
\ 'buffer': bufnr,
|
||||
\ 'on_stdout': function('gitgutter#async#handle_diff_job_nvim'),
|
||||
\ 'on_stderr': function('gitgutter#async#handle_diff_job_nvim'),
|
||||
\ 'on_exit': function('gitgutter#async#handle_diff_job_nvim')
|
||||
\ })
|
||||
call gitgutter#debug#log('[nvim job: '.job_id.', buffer: '.bufnr.'] '.a:cmd)
|
||||
if job_id < 1
|
||||
throw 'diff failed'
|
||||
endif
|
||||
|
||||
" Note that when `cmd` doesn't produce any output, i.e. the diff is empty,
|
||||
" the `stdout` event is not fired on the job handler. Therefore we keep
|
||||
" track of the jobs ourselves so we can spot empty diffs.
|
||||
call s:job_started(job_id)
|
||||
|
||||
else
|
||||
" Make the job use a shell.
|
||||
"
|
||||
" Pass a handler for stdout but not for stderr so that errors are
|
||||
" ignored (and thus signs are not updated; this assumes that an error
|
||||
" only occurs when a file is not tracked by git).
|
||||
|
||||
if has('unix')
|
||||
let command = ["sh", "-c", a:cmd]
|
||||
elseif has('win32')
|
||||
" Help docs recommend {command} be a string on Windows. But I think
|
||||
" they also say that will run the command directly, which I believe would
|
||||
" mean the redirection and pipe stuff wouldn't work.
|
||||
" let command = "cmd.exe /c ".a:cmd
|
||||
let command = ["cmd.exe", "/c", a:cmd]
|
||||
else
|
||||
throw 'unknown os'
|
||||
endif
|
||||
|
||||
let job = job_start(command, {
|
||||
\ 'out_cb': 'gitgutter#async#handle_diff_job_vim',
|
||||
\ 'close_cb': 'gitgutter#async#handle_diff_job_vim_close'
|
||||
\ })
|
||||
call gitgutter#debug#log('[vim job: '.string(job_info(job)).', buffer: '.bufnr.'] '.a:cmd)
|
||||
|
||||
call s:job_started(s:channel_id(job_getchannel(job)), bufnr)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#async#handle_diff_job_nvim(job_id, data, event) abort
|
||||
call gitgutter#debug#log('job_id: '.a:job_id.', event: '.a:event.', buffer: '.self.buffer)
|
||||
|
||||
let job_bufnr = self.buffer
|
||||
if bufexists(job_bufnr)
|
||||
let current_buffer = gitgutter#utility#bufnr()
|
||||
call gitgutter#utility#set_buffer(job_bufnr)
|
||||
|
||||
if a:event == 'stdout'
|
||||
" a:data is a list
|
||||
call s:job_finished(a:job_id)
|
||||
if gitgutter#utility#is_active()
|
||||
call gitgutter#handle_diff(gitgutter#utility#stringify(a:data))
|
||||
endif
|
||||
|
||||
elseif a:event == 'exit'
|
||||
" If the exit event is triggered without a preceding stdout event,
|
||||
" the diff was empty.
|
||||
if s:is_job_started(a:job_id)
|
||||
if gitgutter#utility#is_active()
|
||||
call gitgutter#handle_diff("")
|
||||
endif
|
||||
call s:job_finished(a:job_id)
|
||||
endif
|
||||
|
||||
else " a:event is stderr
|
||||
call gitgutter#hunk#reset()
|
||||
call s:job_finished(a:job_id)
|
||||
|
||||
endif
|
||||
|
||||
call gitgutter#utility#set_buffer(current_buffer)
|
||||
else
|
||||
call s:job_finished(a:job_id)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
" Channel is in NL mode.
|
||||
function! gitgutter#async#handle_diff_job_vim(channel, line) abort
|
||||
call gitgutter#debug#log('channel: '.a:channel.', line: '.a:line)
|
||||
|
||||
call s:accumulate_job_output(s:channel_id(a:channel), a:line)
|
||||
endfunction
|
||||
|
||||
function! gitgutter#async#handle_diff_job_vim_close(channel) abort
|
||||
call gitgutter#debug#log('channel: '.a:channel)
|
||||
|
||||
let channel_id = s:channel_id(a:channel)
|
||||
let job_bufnr = s:job_buffer(channel_id)
|
||||
|
||||
if bufexists(job_bufnr)
|
||||
let current_buffer = gitgutter#utility#bufnr()
|
||||
call gitgutter#utility#set_buffer(job_bufnr)
|
||||
|
||||
if gitgutter#utility#is_active()
|
||||
call gitgutter#handle_diff(s:job_output(channel_id))
|
||||
endif
|
||||
|
||||
call gitgutter#utility#set_buffer(current_buffer)
|
||||
endif
|
||||
call s:job_finished(channel_id)
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:channel_id(channel) abort
|
||||
return ch_info(a:channel)['id']
|
||||
endfunction
|
||||
|
||||
|
||||
"
|
||||
" Keep track of jobs.
|
||||
"
|
||||
" nvim: receives all the job's output at once so we don't need to accumulate
|
||||
" it ourselves. We can pass the buffer number into the job so we don't need
|
||||
" to track that either.
|
||||
"
|
||||
" s:jobs {} -> key: job's id, value: anything truthy
|
||||
"
|
||||
" vim: receives the job's output line by line so we need to accumulate it.
|
||||
" We also need to keep track of the buffer the job is running for.
|
||||
" Vim job's don't have an id. Instead we could use the external process's id
|
||||
" or the channel's id (there seems to be 1 channel per job). Arbitrarily
|
||||
" choose the channel's id.
|
||||
"
|
||||
" s:jobs {} -> key: channel's id, value: {} key: output, value: [] job's output
|
||||
" key: buffer: value: buffer number
|
||||
|
||||
|
||||
" nvim:
|
||||
" id: job's id
|
||||
"
|
||||
" vim:
|
||||
" id: channel's id
|
||||
" arg: buffer number
|
||||
function! s:job_started(id, ...) abort
|
||||
if a:0 " vim
|
||||
let s:jobs[a:id] = {'output': [], 'buffer': a:1}
|
||||
else " nvim
|
||||
let s:jobs[a:id] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:is_job_started(id) abort
|
||||
return has_key(s:jobs, a:id)
|
||||
endfunction
|
||||
|
||||
function! s:accumulate_job_output(id, line) abort
|
||||
call add(s:jobs[a:id].output, a:line)
|
||||
endfunction
|
||||
|
||||
" Returns a string
|
||||
function! s:job_output(id) abort
|
||||
if has_key(s:jobs, a:id)
|
||||
return gitgutter#utility#stringify(s:jobs[a:id].output)
|
||||
else
|
||||
return ""
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:job_buffer(id) abort
|
||||
return s:jobs[a:id].buffer
|
||||
endfunction
|
||||
|
||||
function! s:job_finished(id) abort
|
||||
if has_key(s:jobs, a:id)
|
||||
unlet s:jobs[a:id]
|
||||
endif
|
||||
endfunction
|
||||
|
119
sources_non_forked/vim-gitgutter/autoload/gitgutter/debug.vim
Normal file
119
sources_non_forked/vim-gitgutter/autoload/gitgutter/debug.vim
Normal file
@ -0,0 +1,119 @@
|
||||
let s:plugin_dir = expand('<sfile>:p:h:h:h').'/'
|
||||
let s:log_file = s:plugin_dir.'gitgutter.log'
|
||||
let s:channel_log = s:plugin_dir.'channel.log'
|
||||
let s:new_log_session = 1
|
||||
|
||||
|
||||
function! gitgutter#debug#debug()
|
||||
" Open a scratch buffer
|
||||
vsplit __GitGutter_Debug__
|
||||
normal! ggdG
|
||||
setlocal buftype=nofile
|
||||
setlocal bufhidden=delete
|
||||
setlocal noswapfile
|
||||
|
||||
call gitgutter#debug#vim_version()
|
||||
call gitgutter#debug#separator()
|
||||
|
||||
call gitgutter#debug#git_version()
|
||||
call gitgutter#debug#separator()
|
||||
|
||||
call gitgutter#debug#grep_version()
|
||||
call gitgutter#debug#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')
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#debug#separator()
|
||||
call gitgutter#debug#output('')
|
||||
endfunction
|
||||
|
||||
function! gitgutter#debug#vim_version()
|
||||
redir => version_info
|
||||
silent execute 'version'
|
||||
redir END
|
||||
call gitgutter#debug#output(split(version_info, '\n')[0:2])
|
||||
endfunction
|
||||
|
||||
function! gitgutter#debug#git_version()
|
||||
let v = system(g:gitgutter_git_executable.' --version')
|
||||
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
|
||||
endfunction
|
||||
|
||||
function! gitgutter#debug#grep_version()
|
||||
let v = system('grep --version')
|
||||
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
|
||||
|
||||
let v = system('grep --help')
|
||||
call gitgutter#debug#output( substitute(v, '\%x00', '', 'g') )
|
||||
endfunction
|
||||
|
||||
function! gitgutter#debug#option(name)
|
||||
if exists('+' . a:name)
|
||||
let v = eval('&' . a:name)
|
||||
call gitgutter#debug#output(a:name . '=' . v)
|
||||
" redir => output
|
||||
" silent execute "verbose set " . a:name . "?"
|
||||
" redir END
|
||||
" call gitgutter#debug#output(a:name . '=' . output)
|
||||
else
|
||||
call gitgutter#debug#output(a:name . ' [n/a]')
|
||||
end
|
||||
endfunction
|
||||
|
||||
function! gitgutter#debug#output(text)
|
||||
call append(line('$'), a:text)
|
||||
endfunction
|
||||
|
||||
" assumes optional args are calling function's optional args
|
||||
function! gitgutter#debug#log(message, ...) abort
|
||||
if g:gitgutter_log
|
||||
if s:new_log_session && gitgutter#async#available()
|
||||
if exists('*ch_logfile')
|
||||
call ch_logfile(s:channel_log, 'w')
|
||||
endif
|
||||
endif
|
||||
|
||||
execute 'redir >> '.s:log_file
|
||||
if s:new_log_session
|
||||
let s:start = reltime()
|
||||
silent echo "\n==== start log session ===="
|
||||
endif
|
||||
|
||||
let elapsed = reltimestr(reltime(s:start)).' '
|
||||
silent echo ''
|
||||
" callers excluding this function
|
||||
silent echo elapsed.expand('<sfile>')[:-22].':'
|
||||
silent echo elapsed.s:format_for_log(a:message)
|
||||
if a:0 && !empty(a:1)
|
||||
for msg in a:000
|
||||
silent echo elapsed.s:format_for_log(msg)
|
||||
endfor
|
||||
endif
|
||||
redir END
|
||||
|
||||
let s:new_log_session = 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:format_for_log(data) abort
|
||||
if type(a:data) == 1
|
||||
return join(split(a:data,'\n'),"\n")
|
||||
elseif type(a:data) == 3
|
||||
return '['.join(a:data,"\n").']'
|
||||
else
|
||||
return a:data
|
||||
endif
|
||||
endfunction
|
||||
|
342
sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim
Normal file
342
sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim
Normal file
@ -0,0 +1,342 @@
|
||||
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 --color=never -e'
|
||||
endif
|
||||
endif
|
||||
let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
|
||||
|
||||
let s:c_flag = gitgutter#utility#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.
|
||||
"
|
||||
" * Saved: the buffer contents is the same as the file on disk in the working
|
||||
" tree so we simply do:
|
||||
"
|
||||
" git diff myfile
|
||||
"
|
||||
" * 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 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
|
||||
"
|
||||
" 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
|
||||
" Wrap compound commands in parentheses to make Windows happy.
|
||||
" bash doesn't mind the parentheses.
|
||||
let cmd = '('
|
||||
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
let tracked = getbufvar(bufnr, 'gitgutter_tracked') " 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
|
||||
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.' && '
|
||||
|
||||
" 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("']")
|
||||
|
||||
execute 'keepalt noautocmd silent write!' buff_file
|
||||
|
||||
call setbufvar(bufnr, "&mod", modified)
|
||||
call setpos("'[", op_mark_start)
|
||||
call setpos("']", op_mark_end)
|
||||
endif
|
||||
|
||||
let cmd .= g:gitgutter_git_executable
|
||||
if s:c_flag
|
||||
let cmd .= ' -c "diff.autorefreshindex=0"'
|
||||
endif
|
||||
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' '
|
||||
|
||||
if a:realtime
|
||||
let cmd .= ' -- '.blob_file.' '.buff_file
|
||||
else
|
||||
let cmd .= g:gitgutter_diff_base.' -- '.gitgutter#utility#shellescape(gitgutter#utility#filename())
|
||||
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
|
||||
|
||||
let cmd .= ')'
|
||||
|
||||
if !tracked
|
||||
let cmd .= ')'
|
||||
endif
|
||||
|
||||
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)
|
||||
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'
|
||||
endif
|
||||
|
||||
return diff
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#diff#parse_diff(diff) abort
|
||||
let hunks = []
|
||||
for line in split(a:diff, '\n')
|
||||
let hunk_info = gitgutter#diff#parse_hunk(line)
|
||||
if len(hunk_info) == 4
|
||||
call add(hunks, hunk_info)
|
||||
endif
|
||||
endfor
|
||||
return hunks
|
||||
endfunction
|
||||
|
||||
function! gitgutter#diff#parse_hunk(line) abort
|
||||
let matches = matchlist(a:line, s: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])
|
||||
return [from_line, from_count, to_line, to_count]
|
||||
else
|
||||
return []
|
||||
end
|
||||
endfunction
|
||||
|
||||
function! gitgutter#diff#process_hunks(hunks) abort
|
||||
call gitgutter#hunk#reset()
|
||||
let modified_lines = []
|
||||
for hunk in a:hunks
|
||||
call extend(modified_lines, gitgutter#diff#process_hunk(hunk))
|
||||
endfor
|
||||
return modified_lines
|
||||
endfunction
|
||||
|
||||
" Returns [ [<line_number (number)>, <name (string)>], ...]
|
||||
function! gitgutter#diff#process_hunk(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)
|
||||
|
||||
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 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 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 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)
|
||||
|
||||
endif
|
||||
return modifications
|
||||
endfunction
|
||||
|
||||
function! gitgutter#diff#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
|
||||
return a:from_count > 0 && a:to_count == 0
|
||||
endfunction
|
||||
|
||||
function! gitgutter#diff#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
|
||||
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
|
||||
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
|
||||
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! gitgutter#diff#process_removed(modifications, from_count, to_count, to_line) abort
|
||||
if a:to_line == 0
|
||||
call add(a:modifications, [1, 'removed_first_line'])
|
||||
else
|
||||
call add(a:modifications, [a:to_line, 'removed'])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#diff#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
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! gitgutter#diff#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
|
||||
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! gitgutter#diff#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
|
||||
call add(a:modifications, [line_number, 'modified'])
|
||||
let offset += 1
|
||||
endwhile
|
||||
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
|
||||
let modified_diff = []
|
||||
let keep_line = a:keep_header
|
||||
for line in split(a: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)
|
||||
endif
|
||||
if keep_line
|
||||
call add(modified_diff, line)
|
||||
endif
|
||||
endfor
|
||||
|
||||
if a:keep_header
|
||||
return gitgutter#utility#stringify(modified_diff)
|
||||
else
|
||||
" Discard hunk summary too.
|
||||
return gitgutter#utility#stringify(modified_diff[1:])
|
||||
endif
|
||||
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)
|
||||
endfunction
|
||||
|
@ -0,0 +1,115 @@
|
||||
function! gitgutter#highlight#define_sign_column_highlight() abort
|
||||
if g:gitgutter_override_sign_column_highlight
|
||||
highlight! link SignColumn LineNr
|
||||
else
|
||||
highlight default link SignColumn LineNr
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#define_highlights() abort
|
||||
let [guibg, ctermbg] = gitgutter#highlight#get_background_colors('SignColumn')
|
||||
|
||||
" Highlights used by the signs.
|
||||
|
||||
execute "highlight GitGutterAddDefault guifg=#009900 guibg=" . guibg . " ctermfg=2 ctermbg=" . ctermbg
|
||||
execute "highlight GitGutterChangeDefault guifg=#bbbb00 guibg=" . guibg . " ctermfg=3 ctermbg=" . ctermbg
|
||||
execute "highlight GitGutterDeleteDefault guifg=#ff2222 guibg=" . guibg . " ctermfg=1 ctermbg=" . ctermbg
|
||||
highlight default link GitGutterChangeDeleteDefault GitGutterChangeDefault
|
||||
|
||||
execute "highlight GitGutterAddInvisible guifg=bg guibg=" . guibg . " ctermfg=" . ctermbg . " ctermbg=" . ctermbg
|
||||
execute "highlight GitGutterChangeInvisible guifg=bg guibg=" . guibg . " ctermfg=" . ctermbg . " ctermbg=" . ctermbg
|
||||
execute "highlight GitGutterDeleteInvisible guifg=bg guibg=" . guibg . " ctermfg=" . ctermbg . " ctermbg=" . ctermbg
|
||||
highlight default link GitGutterChangeDeleteInvisible GitGutterChangeInvisble
|
||||
|
||||
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 GitGutterChangeLine
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#define_signs() abort
|
||||
sign define GitGutterLineAdded
|
||||
sign define GitGutterLineModified
|
||||
sign define GitGutterLineRemoved
|
||||
sign define GitGutterLineRemovedFirstLine
|
||||
sign define GitGutterLineModifiedRemoved
|
||||
sign define GitGutterDummy
|
||||
|
||||
call gitgutter#highlight#define_sign_text()
|
||||
call gitgutter#highlight#define_sign_text_highlights()
|
||||
call gitgutter#highlight#define_sign_line_highlights()
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#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
|
||||
execute "sign define GitGutterLineRemovedFirstLine text=" . g:gitgutter_sign_removed_first_line
|
||||
execute "sign define GitGutterLineModifiedRemoved text=" . g:gitgutter_sign_modified_removed
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#define_sign_text_highlights() abort
|
||||
" Once a sign's text attribute has been defined, it cannot be undefined or
|
||||
" set to an empty value. So to make signs' text disappear (when toggling
|
||||
" off or disabling) we make them invisible by setting their foreground colours
|
||||
" to the background's.
|
||||
if g:gitgutter_signs
|
||||
sign define GitGutterLineAdded texthl=GitGutterAdd
|
||||
sign define GitGutterLineModified texthl=GitGutterChange
|
||||
sign define GitGutterLineRemoved texthl=GitGutterDelete
|
||||
sign define GitGutterLineRemovedFirstLine texthl=GitGutterDelete
|
||||
sign define GitGutterLineModifiedRemoved texthl=GitGutterChangeDelete
|
||||
else
|
||||
sign define GitGutterLineAdded texthl=GitGutterAddInvisible
|
||||
sign define GitGutterLineModified texthl=GitGutterChangeInvisible
|
||||
sign define GitGutterLineRemoved texthl=GitGutterDeleteInvisible
|
||||
sign define GitGutterLineRemovedFirstLine texthl=GitGutterDeleteInvisible
|
||||
sign define GitGutterLineModifiedRemoved texthl=GitGutterChangeDeleteInvisible
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#define_sign_line_highlights() abort
|
||||
if g:gitgutter_highlight_lines
|
||||
sign define GitGutterLineAdded linehl=GitGutterAddLine
|
||||
sign define GitGutterLineModified linehl=GitGutterChangeLine
|
||||
sign define GitGutterLineRemoved linehl=GitGutterDeleteLine
|
||||
sign define GitGutterLineRemovedFirstLine linehl=GitGutterDeleteLine
|
||||
sign define GitGutterLineModifiedRemoved linehl=GitGutterChangeDeleteLine
|
||||
else
|
||||
sign define GitGutterLineAdded linehl=
|
||||
sign define GitGutterLineModified linehl=
|
||||
sign define GitGutterLineRemoved linehl=
|
||||
sign define GitGutterLineRemovedFirstLine linehl=
|
||||
sign define GitGutterLineModifiedRemoved linehl=
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#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])
|
||||
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]\+\)')
|
||||
return [guibg, ctermbg]
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#match_highlight(highlight, pattern) abort
|
||||
let matches = matchlist(a:highlight, a:pattern)
|
||||
if len(matches) == 0
|
||||
return 'NONE'
|
||||
endif
|
||||
return matches[1]
|
||||
endfunction
|
137
sources_non_forked/vim-gitgutter/autoload/gitgutter/hunk.vim
Normal file
137
sources_non_forked/vim-gitgutter/autoload/gitgutter/hunk.vim
Normal file
@ -0,0 +1,137 @@
|
||||
let s:hunks = []
|
||||
|
||||
function! gitgutter#hunk#set_hunks(hunks) abort
|
||||
let s:hunks = a:hunks
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#hunks() abort
|
||||
return s:hunks
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#summary(bufnr) abort
|
||||
return get(getbufvar(a:bufnr,''), 'gitgutter_summary', [0,0,0])
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#reset() abort
|
||||
call setbufvar(gitgutter#utility#bufnr(), 'gitgutter_summary', [0,0,0])
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#increment_lines_added(count) abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
let summary = gitgutter#hunk#summary(bufnr)
|
||||
let summary[0] += a:count
|
||||
call setbufvar(bufnr, 'gitgutter_summary', summary)
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#increment_lines_modified(count) abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
let summary = gitgutter#hunk#summary(bufnr)
|
||||
let summary[1] += a:count
|
||||
call setbufvar(bufnr, 'gitgutter_summary', summary)
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#increment_lines_removed(count) abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
let summary = gitgutter#hunk#summary(bufnr)
|
||||
let summary[2] += a:count
|
||||
call setbufvar(bufnr, 'gitgutter_summary', summary)
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#next_hunk(count) abort
|
||||
if gitgutter#utility#is_active()
|
||||
let current_line = line('.')
|
||||
let hunk_count = 0
|
||||
for hunk in s:hunks
|
||||
if hunk[2] > current_line
|
||||
let hunk_count += 1
|
||||
if hunk_count == a:count
|
||||
execute 'normal!' hunk[2] . 'G'
|
||||
return
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
call gitgutter#utility#warn('No more hunks')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#prev_hunk(count) abort
|
||||
if gitgutter#utility#is_active()
|
||||
let current_line = line('.')
|
||||
let hunk_count = 0
|
||||
for hunk in reverse(copy(s:hunks))
|
||||
if hunk[2] < current_line
|
||||
let hunk_count += 1
|
||||
if hunk_count == a:count
|
||||
let target = hunk[2] == 0 ? 1 : hunk[2]
|
||||
execute 'normal!' target . 'G'
|
||||
return
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
call gitgutter#utility#warn('No previous hunks')
|
||||
endif
|
||||
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
|
||||
let current_hunk = []
|
||||
|
||||
for hunk in s:hunks
|
||||
if gitgutter#hunk#cursor_in_hunk(hunk)
|
||||
let current_hunk = hunk
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
return current_hunk
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#cursor_in_hunk(hunk) abort
|
||||
let current_line = line('.')
|
||||
|
||||
if current_line == 1 && a:hunk[2] == 0
|
||||
return 1
|
||||
endif
|
||||
|
||||
if current_line >= a:hunk[2] && current_line < a:hunk[2] + (a:hunk[3] == 0 ? 1 : a:hunk[3])
|
||||
return 1
|
||||
endif
|
||||
|
||||
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 s: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()
|
||||
|
||||
if empty(hunk)
|
||||
return
|
||||
endif
|
||||
|
||||
let [first_line, last_line] = [hunk[2], hunk[2] + hunk[3] - 1]
|
||||
|
||||
if ! a:inner
|
||||
let lnum = last_line
|
||||
let eof = line('$')
|
||||
while lnum < eof && empty(getline(lnum + 1))
|
||||
let lnum +=1
|
||||
endwhile
|
||||
let last_line = lnum
|
||||
endif
|
||||
|
||||
execute 'normal! 'first_line.'GV'.last_line.'G'
|
||||
endfunction
|
179
sources_non_forked/vim-gitgutter/autoload/gitgutter/sign.vim
Normal file
179
sources_non_forked/vim-gitgutter/autoload/gitgutter/sign.vim
Normal file
@ -0,0 +1,179 @@
|
||||
" 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:dummy_sign_id = s:first_sign_id - 1
|
||||
" Remove-all-signs optimisation requires Vim 7.3.596+.
|
||||
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()
|
||||
|
||||
let sign_ids = map(values(getbufvar(bufnr, 'gitgutter_gitgutter_signs')), 'v:val.id')
|
||||
call gitgutter#sign#remove_signs(sign_ids, 1)
|
||||
call setbufvar(bufnr, 'gitgutter_gitgutter_signs', {})
|
||||
endfunction
|
||||
|
||||
|
||||
" Updates gitgutter's signs in the buffer being processed.
|
||||
"
|
||||
" 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()
|
||||
|
||||
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 flicker_possible = s:remove_all_old_signs && !empty(a:modified_lines)
|
||||
if flicker_possible
|
||||
call gitgutter#sign#add_dummy_sign()
|
||||
endif
|
||||
|
||||
call gitgutter#sign#remove_signs(obsolete_signs, s:remove_all_old_signs)
|
||||
call gitgutter#sign#upsert_new_gitgutter_signs(a:modified_lines)
|
||||
|
||||
if flicker_possible
|
||||
call gitgutter#sign#remove_dummy_sign(0)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#sign#add_dummy_sign() abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
if !getbufvar(bufnr, 'gitgutter_dummy_sign')
|
||||
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
|
||||
call setbufvar(bufnr, 'gitgutter_dummy_sign', 1)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#sign#remove_dummy_sign(force) abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
if getbufvar(bufnr, 'gitgutter_dummy_sign') && (a:force || !g:gitgutter_sign_column_always)
|
||||
execute "sign unplace" s:dummy_sign_id "buffer=" . bufnr
|
||||
call setbufvar(bufnr, 'gitgutter_dummy_sign', 0)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
"
|
||||
" Internal functions
|
||||
"
|
||||
|
||||
|
||||
function! gitgutter#sign#find_current_signs() abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
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
|
||||
redir END
|
||||
|
||||
for sign_line in filter(split(signs, '\n')[2:], 'v:val =~# "="')
|
||||
" Typical sign line: line=88 id=1234 name=GitGutterLineAdded
|
||||
" We assume splitting is faster than a regexp.
|
||||
let components = split(sign_line)
|
||||
let name = split(components[2], '=')[1]
|
||||
if name =~# 'GitGutterDummy'
|
||||
let dummy_sign_placed = 1
|
||||
else
|
||||
let line_number = str2nr(split(components[0], '=')[1])
|
||||
if name =~# 'GitGutter'
|
||||
let id = str2nr(split(components[1], '=')[1])
|
||||
" Remove orphaned signs (signs placed on lines which have been deleted).
|
||||
" (When a line is deleted its sign lingers. Subsequent lines' signs'
|
||||
" line numbers are decremented appropriately.)
|
||||
if has_key(gitgutter_signs, line_number)
|
||||
execute "sign unplace" gitgutter_signs[line_number].id
|
||||
endif
|
||||
let gitgutter_signs[line_number] = {'id': id, 'name': name}
|
||||
else
|
||||
call add(other_signs, line_number)
|
||||
endif
|
||||
end
|
||||
endfor
|
||||
|
||||
call setbufvar(bufnr, 'gitgutter_dummy_sign', dummy_sign_placed)
|
||||
call setbufvar(bufnr, 'gitgutter_gitgutter_signs', gitgutter_signs)
|
||||
call setbufvar(bufnr, 'gitgutter_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()
|
||||
let signs_to_remove = [] " list of [<id (number)>, ...]
|
||||
let remove_all_signs = 1
|
||||
let old_gitgutter_signs = getbufvar(bufnr, 'gitgutter_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)
|
||||
else
|
||||
let remove_all_signs = 0
|
||||
endif
|
||||
endfor
|
||||
let s:remove_all_old_signs = remove_all_signs
|
||||
return signs_to_remove
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#sign#remove_signs(sign_ids, all_signs) abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
if a:all_signs && s:supports_star && empty(getbufvar(bufnr, 'gitgutter_other_signs'))
|
||||
let dummy_sign_present = getbufvar(bufnr, 'gitgutter_dummy_sign')
|
||||
execute "sign unplace * buffer=" . bufnr
|
||||
if dummy_sign_present
|
||||
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
|
||||
endif
|
||||
else
|
||||
for id in a:sign_ids
|
||||
execute "sign unplace" id
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort
|
||||
let bufnr = gitgutter#utility#bufnr()
|
||||
let other_signs = getbufvar(bufnr, 'gitgutter_other_signs')
|
||||
let old_gitgutter_signs = getbufvar(bufnr, 'gitgutter_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])
|
||||
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
|
||||
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
|
||||
end
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
" At this point b:gitgutter_gitgutter_signs is out of date.
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#sign#next_sign_id() abort
|
||||
let next_id = s:next_sign_id
|
||||
let s:next_sign_id += 1
|
||||
return next_id
|
||||
endfunction
|
||||
|
||||
|
||||
" Only for testing.
|
||||
function! gitgutter#sign#reset()
|
||||
let s:next_sign_id = s:first_sign_id
|
||||
endfunction
|
212
sources_non_forked/vim-gitgutter/autoload/gitgutter/utility.vim
Normal file
212
sources_non_forked/vim-gitgutter/autoload/gitgutter/utility.vim
Normal file
@ -0,0 +1,212 @@
|
||||
let s:file = ''
|
||||
let s:using_xolox_shell = -1
|
||||
let s:exit_code = 0
|
||||
|
||||
function! gitgutter#utility#warn(message) abort
|
||||
echohl WarningMsg
|
||||
echo 'vim-gitgutter: ' . a:message
|
||||
echohl None
|
||||
let v:warningmsg = a:message
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#warn_once(message, key) abort
|
||||
if empty(getbufvar(s:bufnr, a:key))
|
||||
call setbufvar(s:bufnr, a:key, '1')
|
||||
echohl WarningMsg
|
||||
redraw | echo 'vim-gitgutter: ' . a:message
|
||||
echohl None
|
||||
let v:warningmsg = a:message
|
||||
endif
|
||||
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
|
||||
return g:gitgutter_enabled &&
|
||||
\ !pumvisible() &&
|
||||
\ gitgutter#utility#is_file_buffer() &&
|
||||
\ gitgutter#utility#exists_file() &&
|
||||
\ gitgutter#utility#not_git_dir()
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#not_git_dir() abort
|
||||
return gitgutter#utility#full_path_to_directory_of_file() !~ '[/\\]\.git\($\|[/\\]\)'
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#is_file_buffer() abort
|
||||
return empty(getbufvar(s: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
|
||||
function! gitgutter#utility#shellescape(arg) abort
|
||||
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
|
||||
return a:arg
|
||||
elseif &shell =~# 'cmd' || gitgutter#utility#using_xolox_shell()
|
||||
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))
|
||||
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') != getbufvar(s:bufnr, 'gitgutter_last_tick')
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#save_last_seen_change() abort
|
||||
call setbufvar(s:bufnr, 'gitgutter_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
|
||||
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
|
||||
return output
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#file_relative_to_repo_root() abort
|
||||
let file_path_relative_to_repo_root = getbufvar(s:bufnr, 'gitgutter_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 setbufvar(s:bufnr, 'gitgutter_repo_relative_path', file_path_relative_to_repo_root)
|
||||
endif
|
||||
return file_path_relative_to_repo_root
|
||||
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#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
|
||||
|
||||
" 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')
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#strip_trailing_new_line(line) abort
|
||||
return substitute(a:line, '\n$', '', '')
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#git_version() abort
|
||||
return matchstr(system(g:gitgutter_git_executable.' --version'), '[0-9.]\+')
|
||||
endfunction
|
||||
|
||||
" True for git v1.7.2+.
|
||||
function! gitgutter#utility#git_supports_command_line_config_override() abort
|
||||
let [major, minor, patch; _] = split(gitgutter#utility#git_version(), '\.')
|
||||
return major > 1 || (major == 1 && minor > 7) || (minor == 7 && patch > 1)
|
||||
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
|
||||
endfunction
|
Reference in New Issue
Block a user