diff options
| author | Kumar Damani <damani.kumar@gmail.com> | 2019-04-26 18:49:26 +0000 |
|---|---|---|
| committer | Kumar Damani <damani.kumar@gmail.com> | 2019-04-26 18:49:26 +0000 |
| commit | 5a6ed5fcb3b89d0c2b4e8ba2953c4ba806d4b171 (patch) | |
| tree | 5be0c043b0c8d632e35a8b4ef7e23452b103b94d /vim/.vim/plugged/vim-gitgutter/autoload | |
| parent | af821092fe78ef159d7cb6536bb006712dc01b9d (diff) | |
added vim dir
Diffstat (limited to 'vim/.vim/plugged/vim-gitgutter/autoload')
9 files changed, 1817 insertions, 0 deletions
diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter.vim new file mode 100644 index 0000000..2e17fdb --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter.vim @@ -0,0 +1,160 @@ +let s:t_string = type('') + +" Primary functions {{{ + +function! gitgutter#all(force) abort + let visible = tabpagebuflist() + + for bufnr in range(1, bufnr('$') + 1) + if buflisted(bufnr) + let file = expand('#'.bufnr.':p') + if !empty(file) + if index(visible, bufnr) != -1 + call gitgutter#init_buffer(bufnr) + call gitgutter#process_buffer(bufnr, a:force) + elseif a:force + call s:reset_tick(bufnr) + endif + endif + endif + endfor +endfunction + + +" Finds the file's path relative to the repo root. +function! gitgutter#init_buffer(bufnr) + if gitgutter#utility#is_active(a:bufnr) + let p = gitgutter#utility#repo_path(a:bufnr, 0) + if type(p) != s:t_string || empty(p) + call gitgutter#utility#set_repo_path(a:bufnr) + call s:setup_maps() + endif + endif +endfunction + + +function! gitgutter#process_buffer(bufnr, force) abort + " NOTE a:bufnr is not necessarily the current buffer. + + if gitgutter#utility#is_active(a:bufnr) + if a:force || s:has_fresh_changes(a:bufnr) + + let diff = '' + try + let diff = gitgutter#diff#run_diff(a:bufnr, 'index', 0) + catch /gitgutter not tracked/ + call gitgutter#debug#log('Not tracked: '.gitgutter#utility#file(a:bufnr)) + catch /gitgutter diff failed/ + call gitgutter#debug#log('Diff failed: '.gitgutter#utility#file(a:bufnr)) + call gitgutter#hunk#reset(a:bufnr) + endtry + + if diff != 'async' + call gitgutter#diff#handler(a:bufnr, diff) + endif + + endif + endif +endfunction + + +function! gitgutter#disable() abort + " get list of all buffers (across all tabs) + for bufnr in range(1, bufnr('$') + 1) + if buflisted(bufnr) + let file = expand('#'.bufnr.':p') + if !empty(file) + call s:clear(bufnr) + endif + endif + endfor + + let g:gitgutter_enabled = 0 +endfunction + +function! gitgutter#enable() abort + let g:gitgutter_enabled = 1 + call gitgutter#all(1) +endfunction + +function! gitgutter#toggle() abort + if g:gitgutter_enabled + call gitgutter#disable() + else + call gitgutter#enable() + endif +endfunction + + +function! gitgutter#buffer_disable() abort + let bufnr = bufnr('') + call gitgutter#utility#setbufvar(bufnr, 'enabled', 0) + call s:clear(bufnr) +endfunction + +function! gitgutter#buffer_enable() abort + let bufnr = bufnr('') + call gitgutter#utility#setbufvar(bufnr, 'enabled', 1) + call gitgutter#process_buffer(bufnr, 1) +endfunction + +function! gitgutter#buffer_toggle() abort + if gitgutter#utility#getbufvar(bufnr(''), 'enabled', 1) + call gitgutter#buffer_disable() + else + call gitgutter#buffer_enable() + endif +endfunction + +" }}} + +function! s:setup_maps() + if !g:gitgutter_map_keys + return + endif + + if !hasmapto('<Plug>GitGutterPrevHunk') && maparg('[c', 'n') ==# '' + nmap <buffer> [c <Plug>GitGutterPrevHunk + endif + if !hasmapto('<Plug>GitGutterNextHunk') && maparg(']c', 'n') ==# '' + nmap <buffer> ]c <Plug>GitGutterNextHunk + endif + + if !hasmapto('<Plug>GitGutterStageHunk') && maparg('<Leader>hs', 'n') ==# '' + nmap <buffer> <Leader>hs <Plug>GitGutterStageHunk + endif + if !hasmapto('<Plug>GitGutterUndoHunk') && maparg('<Leader>hu', 'n') ==# '' + nmap <buffer> <Leader>hu <Plug>GitGutterUndoHunk + endif + if !hasmapto('<Plug>GitGutterPreviewHunk') && maparg('<Leader>hp', 'n') ==# '' + nmap <buffer> <Leader>hp <Plug>GitGutterPreviewHunk + endif + + if !hasmapto('<Plug>GitGutterTextObjectInnerPending') && maparg('ic', 'o') ==# '' + omap <buffer> ic <Plug>GitGutterTextObjectInnerPending + endif + if !hasmapto('<Plug>GitGutterTextObjectOuterPending') && maparg('ac', 'o') ==# '' + omap <buffer> ac <Plug>GitGutterTextObjectOuterPending + endif + if !hasmapto('<Plug>GitGutterTextObjectInnerVisual') && maparg('ic', 'x') ==# '' + xmap <buffer> ic <Plug>GitGutterTextObjectInnerVisual + endif + if !hasmapto('<Plug>GitGutterTextObjectOuterVisual') && maparg('ac', 'x') ==# '' + xmap <buffer> ac <Plug>GitGutterTextObjectOuterVisual + endif +endfunction + +function! s:has_fresh_changes(bufnr) abort + return getbufvar(a:bufnr, 'changedtick') != gitgutter#utility#getbufvar(a:bufnr, 'tick') +endfunction + +function! s:reset_tick(bufnr) abort + call gitgutter#utility#setbufvar(a:bufnr, 'tick', 0) +endfunction + +function! s:clear(bufnr) + call gitgutter#sign#clear_signs(a:bufnr) + call gitgutter#sign#remove_dummy_sign(a:bufnr, 1) + call gitgutter#hunk#reset(a:bufnr) + call s:reset_tick(a:bufnr) +endfunction diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/async.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/async.vim new file mode 100644 index 0000000..40f2ad2 --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/async.vim @@ -0,0 +1,95 @@ +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, bufnr, handler) abort + call gitgutter#debug#log('[async] '.a:cmd) + + let options = { + \ 'stdoutbuffer': [], + \ 'buffer': a:bufnr, + \ 'handler': a:handler + \ } + let command = s:build_command(a:cmd) + + if has('nvim') + call jobstart(command, extend(options, { + \ 'on_stdout': function('s:on_stdout_nvim'), + \ 'on_stderr': function('s:on_stderr_nvim'), + \ 'on_exit': function('s:on_exit_nvim') + \ })) + else + call job_start(command, { + \ 'out_cb': function('s:on_stdout_vim', options), + \ 'err_cb': function('s:on_stderr_vim', options), + \ 'close_cb': function('s:on_exit_vim', options) + \ }) + endif +endfunction + + +function! s:build_command(cmd) + if has('unix') + return ['sh', '-c', a:cmd] + endif + + if has('win32') + return has('nvim') ? ['cmd.exe', '/c', a:cmd] : 'cmd.exe /c '.a:cmd + endif + + throw 'unknown os' +endfunction + + +function! s:on_stdout_nvim(_job_id, data, _event) dict abort + if empty(self.stdoutbuffer) + let self.stdoutbuffer = a:data + else + let self.stdoutbuffer = self.stdoutbuffer[:-2] + + \ [self.stdoutbuffer[-1] . a:data[0]] + + \ a:data[1:] + endif +endfunction + +function! s:on_stderr_nvim(_job_id, _data, _event) dict abort + call self.handler.err(self.buffer) +endfunction + +function! s:on_exit_nvim(_job_id, exit_code, _event) dict abort + if !a:exit_code + call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n")) + endif +endfunction + + +function! s:on_stdout_vim(_channel, data) dict abort + call add(self.stdoutbuffer, a:data) +endfunction + +function! s:on_stderr_vim(channel, _data) dict abort + call self.handler.err(self.buffer) +endfunction + +function! s:on_exit_vim(channel) dict abort + let job = ch_getjob(a:channel) + while 1 + if job_status(job) == 'dead' + let exit_code = job_info(job).exitval + break + endif + sleep 5m + endwhile + + if !exit_code + call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n")) + endif +endfunction diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/debug.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/debug.vim new file mode 100644 index 0000000..79d197e --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/debug.vim @@ -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 s:vim_version() + call s:separator() + + call s:git_version() + call s:separator() + + call s:grep_version() + call s:separator() + + call s:option('updatetime') + call s:option('shell') + call s:option('shellcmdflag') + call s:option('shellpipe') + call s:option('shellquote') + call s:option('shellredir') + call s:option('shellslash') + call s:option('shelltemp') + call s:option('shelltype') + call s:option('shellxescape') + call s:option('shellxquote') +endfunction + + +function! s:separator() + call s:output('') +endfunction + +function! s:vim_version() + redir => version_info + silent execute 'version' + redir END + call s:output(split(version_info, '\n')[0:2]) +endfunction + +function! s:git_version() + let v = system(g:gitgutter_git_executable.' --version') + call s:output( substitute(v, '\n$', '', '') ) +endfunction + +function! s:grep_version() + let v = system('grep --version') + call s:output( substitute(v, '\n$', '', '') ) + + let v = system('grep --help') + call s:output( substitute(v, '\%x00', '', 'g') ) +endfunction + +function! s:option(name) + if exists('+' . a:name) + let v = eval('&' . a:name) + call s:output(a:name . '=' . v) + " redir => output + " silent execute "verbose set " . a:name . "?" + " redir END + " call s:output(a:name . '=' . output) + else + call s:output(a:name . ' [n/a]') + end +endfunction + +function! s: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 + diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/diff.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/diff.vim new file mode 100644 index 0000000..b270db7 --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/diff.vim @@ -0,0 +1,405 @@ +let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : '' + +let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@' + +" True for git v1.7.2+. +function! s:git_supports_command_line_config_override() abort + call system(g:gitgutter_git_executable.' -c foo.bar=baz --version') + return !v:shell_error +endfunction + +let s:c_flag = s:git_supports_command_line_config_override() + + +let s:temp_from = tempname() +let s:temp_buffer = tempname() +let s:counter = 0 + +" Returns a diff of the buffer against the index or the working tree. +" +" 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. +" +" When diffing against the index: +" +" 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 +" +" where myfileA comes from +" +" git show :myfile > myfileA +" +" and myfileB is the buffer contents. +" +" Regarding line endings: +" +" git-show does not convert line endings. +" git-diff FILE FILE does convert line endings for the given files. +" +" If a file has CRLF line endings and git's core.autocrlf is true, +" the file in git's object store will have LF line endings. Writing +" it out via git-show will produce a file with LF line endings. +" +" If this last file is one of the files passed to git-diff, git-diff will +" convert its line endings to CRLF before diffing -- which is what we want -- +" but also by default output a warning on stderr. +" +" warning: LF will be replace by CRLF in <temp file>. +" The file will have its original line endings in your working directory. +" +" When running the diff asynchronously, the warning message triggers the stderr +" callbacks which assume the overall command has failed and reset all the +" signs. As this is not what we want, and we can safely ignore the warning, +" we turn it off by passing the '-c "core.safecrlf=false"' argument to +" git-diff. +" +" When writing the temporary files we preserve the original file's extension +" so that repos using .gitattributes to control EOL conversion continue to +" convert correctly. +" +" Arguments: +" +" bufnr - the number of the buffer to be diffed +" from - 'index' or 'working_tree'; what the buffer is diffed against +" preserve_full_diff - truthy to return the full diff or falsey to return only +" the hunk headers (@@ -x,y +m,n @@); only possible if +" grep is available. +function! gitgutter#diff#run_diff(bufnr, from, preserve_full_diff) abort + while gitgutter#utility#repo_path(a:bufnr, 0) == -1 + sleep 5m + endwhile + + if gitgutter#utility#repo_path(a:bufnr, 0) == -2 + throw 'gitgutter not tracked' + endif + + " Wrap compound commands in parentheses to make Windows happy. + " bash doesn't mind the parentheses. + let cmd = '(' + + " Append buffer number to temp filenames to avoid race conditions between + " writing and reading the files when asynchronously processing multiple + " buffers. + + " Without the buffer number, buff_file would have a race between the + " second gitgutter#process_buffer() writing the file (synchronously, below) + " and the first gitgutter#process_buffer()'s async job reading it (with + " git-diff). + let buff_file = s:temp_buffer.'.'.a:bufnr + + " Add a counter to avoid a similar race with two quick writes of the same buffer. + " Use a modulus greater than a maximum reasonable number of visible buffers. + let s:counter = (s:counter + 1) % 20 + let buff_file .= '.'.s:counter + + let extension = gitgutter#utility#extension(a:bufnr) + if !empty(extension) + let buff_file .= '.'.extension + endif + + " Write buffer to temporary file. + " Note: this is synchronous. + call s:write_buffer(a:bufnr, buff_file) + + if a:from ==# 'index' + " Without the buffer number, from_file would have a race in the shell + " between the second process writing it (with git-show) and the first + " reading it (with git-diff). + let from_file = s:temp_from.'.'.a:bufnr + + " Add a counter to avoid a similar race with two quick writes of the same buffer. + let from_file .= '.'.s:counter + + if !empty(extension) + let from_file .= '.'.extension + endif + + " Write file from index to temporary file. + let index_name = g:gitgutter_diff_base.':'.gitgutter#utility#repo_path(a:bufnr, 1) + let cmd .= g:gitgutter_git_executable.' --no-pager show '.index_name.' > '.from_file.' && ' + + elseif a:from ==# 'working_tree' + let from_file = gitgutter#utility#repo_path(a:bufnr, 1) + endif + + " Call git-diff. + let cmd .= g:gitgutter_git_executable.' --no-pager '.g:gitgutter_git_args + if s:c_flag + let cmd .= ' -c "diff.autorefreshindex=0"' + let cmd .= ' -c "diff.noprefix=false"' + let cmd .= ' -c "core.safecrlf=false"' + endif + let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' -- '.from_file.' '.buff_file + + " Pipe git-diff output into grep. + if !a:preserve_full_diff && !empty(g:gitgutter_grep) + let cmd .= ' | '.g:gitgutter_grep.' '.gitgutter#utility#shellescape('^@@ ') + endif + + " grep exits with 1 when no matches are found; git-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' + + let cmd .= ')' + + let cmd = gitgutter#utility#cd_cmd(a:bufnr, cmd) + + if g:gitgutter_async && gitgutter#async#available() + call gitgutter#async#execute(cmd, a:bufnr, { + \ 'out': function('gitgutter#diff#handler'), + \ 'err': function('gitgutter#hunk#reset'), + \ }) + return 'async' + + else + let diff = gitgutter#utility#system(cmd) + + if v:shell_error + call gitgutter#debug#log(diff) + throw 'gitgutter diff failed' + endif + + return diff + endif +endfunction + + +function! gitgutter#diff#handler(bufnr, diff) abort + call gitgutter#debug#log(a:diff) + + if !bufexists(a:bufnr) + return + endif + + call gitgutter#hunk#set_hunks(a:bufnr, gitgutter#diff#parse_diff(a:diff)) + let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr)) + + let signs_count = len(modified_lines) + if signs_count > g:gitgutter_max_signs + call gitgutter#utility#warn_once(a:bufnr, printf( + \ 'exceeded maximum number of signs (%d > %d, configured by g:gitgutter_max_signs).', + \ signs_count, g:gitgutter_max_signs), 'max_signs') + call gitgutter#sign#clear_signs(a:bufnr) + + else + if g:gitgutter_signs || g:gitgutter_highlight_lines + call gitgutter#sign#update_signs(a:bufnr, modified_lines) + endif + endif + + call s:save_last_seen_change(a:bufnr) + if exists('#User#GitGutter') + let g:gitgutter_hook_context = {'bufnr': a:bufnr} + execute 'doautocmd' s:nomodeline 'User GitGutter' + unlet g:gitgutter_hook_context + 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 + +" This function is public so it may be used by other plugins +" e.g. vim-signature. +function! gitgutter#diff#process_hunks(bufnr, hunks) abort + let modified_lines = [] + for hunk in a:hunks + call extend(modified_lines, s:process_hunk(a:bufnr, hunk)) + endfor + return modified_lines +endfunction + +" Returns [ [<line_number (number)>, <name (string)>], ...] +function! s:process_hunk(bufnr, 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 s:is_added(from_count, to_count) + call s:process_added(modifications, from_count, to_count, to_line) + call gitgutter#hunk#increment_lines_added(a:bufnr, to_count) + + elseif s:is_removed(from_count, to_count) + call s:process_removed(modifications, from_count, to_count, to_line) + call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count) + + elseif s:is_modified(from_count, to_count) + call s:process_modified(modifications, from_count, to_count, to_line) + call gitgutter#hunk#increment_lines_modified(a:bufnr, 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) + call gitgutter#hunk#increment_lines_added(a:bufnr, to_count - from_count) + call gitgutter#hunk#increment_lines_modified(a:bufnr, 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) + call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count) + call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count - to_count) + + endif + return modifications +endfunction + +function! s:is_added(from_count, to_count) abort + return a:from_count == 0 && a:to_count > 0 +endfunction + +function! s:is_removed(from_count, to_count) abort + return a:from_count > 0 && a:to_count == 0 +endfunction + +function! s:is_modified(from_count, to_count) abort + 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) abort + 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) abort + 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) 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! s: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! s: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! s: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! s: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 + + +" Returns a diff for the current hunk. +" Assumes there is only 1 current hunk unless the optional argument is given, +" in which case the cursor is in two hunks and the argument specifies the one +" to choose. +" +" Optional argument: 0 (to use the first hunk) or 1 (to use the second). +function! gitgutter#diff#hunk_diff(bufnr, full_diff, ...) + let modified_diff = [] + let hunk_index = 0 + let keep_line = 1 + " Don't keepempty when splitting because the diff we want may not be the + " final one. Instead add trailing NL at end of function. + for line in split(a:full_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) + + if a:0 && hunk_index != a:1 + let keep_line = 0 + endif + + let hunk_index += 1 + endif + if keep_line + call add(modified_diff, line) + endif + endfor + return join(modified_diff, "\n")."\n" +endfunction + + +function! s:write_buffer(bufnr, file) + let bufcontents = getbufline(a:bufnr, 1, '$') + + if bufcontents == [''] && line2byte(1) == -1 + " Special case: completely empty buffer. + " A nearly empty buffer of only a newline has line2byte(1) == 1. + call writefile([], a:file) + return + endif + + if getbufvar(a:bufnr, '&fileformat') ==# 'dos' + call map(bufcontents, 'v:val."\r"') + endif + + let fenc = getbufvar(a:bufnr, '&fileencoding') + if fenc !=# &encoding + call map(bufcontents, 'iconv(v:val, &encoding, "'.fenc.'")') + endif + + if getbufvar(a:bufnr, '&bomb') + let bufcontents[0]=''.bufcontents[0] + endif + + call writefile(bufcontents, a:file) +endfunction + + +function! s:save_last_seen_change(bufnr) abort + call gitgutter#utility#setbufvar(a:bufnr, 'tick', getbufvar(a:bufnr, 'changedtick')) +endfunction + + diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/fold.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/fold.vim new file mode 100644 index 0000000..ffc7691 --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/fold.vim @@ -0,0 +1,84 @@ +function! gitgutter#fold#enable() + call s:save_fold_state() + + call s:set_fold_levels() + setlocal foldexpr=gitgutter#fold#level(v:lnum) + setlocal foldmethod=expr + setlocal foldlevel=0 + setlocal foldenable + + call gitgutter#utility#setbufvar(bufnr(''), 'folded', 1) +endfunction + + +function! gitgutter#fold#disable() + call s:restore_fold_state() + call gitgutter#utility#setbufvar(bufnr(''), 'folded', 0) +endfunction + + +function! gitgutter#fold#toggle() + if s:folded() + call gitgutter#fold#disable() + else + call gitgutter#fold#enable() + endif +endfunction + + +function! gitgutter#fold#level(lnum) + return gitgutter#utility#getbufvar(bufnr(''), 'fold_levels')[a:lnum] +endfunction + + +" A line in a hunk has a fold level of 0. +" A line within 3 lines of a hunk has a fold level of 1. +" All other lines have a fold level of 2. +function! s:set_fold_levels() + let fold_levels = [''] + + for lnum in range(1, line('$')) + let in_hunk = gitgutter#hunk#in_hunk(lnum) + call add(fold_levels, (in_hunk ? 0 : 2)) + endfor + + let lines_of_context = 3 + + for lnum in range(1, line('$')) + if fold_levels[lnum] == 2 + let pre = lnum >= 3 ? lnum - lines_of_context : 0 + let post = lnum + lines_of_context + if index(fold_levels[pre:post], 0) != -1 + let fold_levels[lnum] = 1 + endif + endif + endfor + + call gitgutter#utility#setbufvar(bufnr(''), 'fold_levels', fold_levels) +endfunction + + +function! s:save_fold_state() + let bufnr = bufnr('') + call gitgutter#utility#setbufvar(bufnr, 'foldlevel', &foldlevel) + call gitgutter#utility#setbufvar(bufnr, 'foldmethod', &foldmethod) + if &foldmethod ==# 'manual' + mkview + endif +endfunction + +function! s:restore_fold_state() + let bufnr = bufnr('') + let &foldlevel = gitgutter#utility#getbufvar(bufnr, 'foldlevel') + let &foldmethod = gitgutter#utility#getbufvar(bufnr, 'foldmethod') + if &foldmethod ==# 'manual' + loadview + else + normal! zx + endif +endfunction + +function! s:folded() + return gitgutter#utility#getbufvar(bufnr(''), 'folded') +endfunction + diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/highlight.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/highlight.vim new file mode 100644 index 0000000..160856f --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/highlight.vim @@ -0,0 +1,170 @@ +function! gitgutter#highlight#line_disable() abort + let g:gitgutter_highlight_lines = 0 + call s:define_sign_line_highlights() + + if !g:gitgutter_signs + call gitgutter#sign#clear_signs(bufnr('')) + call gitgutter#sign#remove_dummy_sign(bufnr(''), 0) + endif + + redraw! +endfunction + +function! gitgutter#highlight#line_enable() abort + let old_highlight_lines = g:gitgutter_highlight_lines + + let g:gitgutter_highlight_lines = 1 + call s:define_sign_line_highlights() + + if !old_highlight_lines && !g:gitgutter_signs + call gitgutter#all(1) + endif + + redraw! +endfunction + +function! gitgutter#highlight#line_toggle() abort + if g:gitgutter_highlight_lines + call gitgutter#highlight#line_disable() + else + call gitgutter#highlight#line_enable() + endif +endfunction + + +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] = s:get_background_colors('SignColumn') + + " Highlights used by the signs. + + " When they are invisible. + 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 GitGutterChangeInvisible + + " When they are visible. + " By default use Diff* foreground colors with SignColumn's background. + for type in ['Add', 'Change', 'Delete'] + let [guifg, ctermfg] = s:get_foreground_colors('Diff'.type) + execute "highlight GitGutter".type."Default guifg=".guifg." guibg=".guibg." ctermfg=".ctermfg." ctermbg=".ctermbg + execute "highlight default link GitGutter".type." GitGutter".type."Default" + endfor + highlight default link GitGutterChangeDelete GitGutterChange + + " 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 GitGutterLineRemovedAboveAndBelow + sign define GitGutterLineModifiedRemoved + sign define GitGutterDummy + + call s:define_sign_text() + call gitgutter#highlight#define_sign_text_highlights() + call s:define_sign_line_highlights() +endfunction + +function! s: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 GitGutterLineRemovedAboveAndBelow text=" . g:gitgutter_sign_removed_above_and_below + 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 GitGutterLineRemovedAboveAndBelow 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 GitGutterLineRemovedAboveAndBelow texthl=GitGutterDeleteInvisible + sign define GitGutterLineModifiedRemoved texthl=GitGutterChangeDeleteInvisible + endif +endfunction + +function! s: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 GitGutterLineRemovedAboveAndBelow 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 GitGutterLineRemovedAboveAndBelow linehl= + sign define GitGutterLineModifiedRemoved linehl= + endif +endfunction + +function! s:get_foreground_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 s:get_foreground_colors(link_matches[1]) + endif + + let ctermfg = s:match_highlight(highlight, 'ctermfg=\([0-9A-Za-z]\+\)') + let guifg = s:match_highlight(highlight, 'guifg=\([#0-9A-Za-z]\+\)') + return [guifg, ctermfg] +endfunction + +function! s: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 s:get_background_colors(link_matches[1]) + endif + + let ctermbg = s:match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)') + let guibg = s:match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)') + return [guibg, ctermbg] +endfunction + +function! s:match_highlight(highlight, pattern) abort + let matches = matchlist(a:highlight, a:pattern) + if len(matches) == 0 + return 'NONE' + endif + return matches[1] +endfunction diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim new file mode 100644 index 0000000..50ea5f4 --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/hunk.vim @@ -0,0 +1,332 @@ +function! gitgutter#hunk#set_hunks(bufnr, hunks) abort + call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks) + call s:reset_summary(a:bufnr) +endfunction + +function! gitgutter#hunk#hunks(bufnr) abort + return gitgutter#utility#getbufvar(a:bufnr, 'hunks', []) +endfunction + +function! gitgutter#hunk#reset(bufnr) abort + call gitgutter#utility#setbufvar(a:bufnr, 'hunks', []) + call s:reset_summary(a:bufnr) +endfunction + + +function! gitgutter#hunk#summary(bufnr) abort + return gitgutter#utility#getbufvar(a:bufnr, 'summary', [0,0,0]) +endfunction + +function! s:reset_summary(bufnr) abort + call gitgutter#utility#setbufvar(a:bufnr, 'summary', [0,0,0]) +endfunction + +function! gitgutter#hunk#increment_lines_added(bufnr, count) abort + let summary = gitgutter#hunk#summary(a:bufnr) + let summary[0] += a:count + call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary) +endfunction + +function! gitgutter#hunk#increment_lines_modified(bufnr, count) abort + let summary = gitgutter#hunk#summary(a:bufnr) + let summary[1] += a:count + call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary) +endfunction + +function! gitgutter#hunk#increment_lines_removed(bufnr, count) abort + let summary = gitgutter#hunk#summary(a:bufnr) + let summary[2] += a:count + call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary) +endfunction + + +function! gitgutter#hunk#next_hunk(count) abort + let bufnr = bufnr('') + if gitgutter#utility#is_active(bufnr) + let current_line = line('.') + let hunk_count = 0 + for hunk in gitgutter#hunk#hunks(bufnr) + if hunk[2] > current_line + let hunk_count += 1 + if hunk_count == a:count + execute 'normal!' hunk[2] . 'Gzv' + return + endif + endif + endfor + call gitgutter#utility#warn('No more hunks') + endif +endfunction + +function! gitgutter#hunk#prev_hunk(count) abort + let bufnr = bufnr('') + if gitgutter#utility#is_active(bufnr) + let current_line = line('.') + let hunk_count = 0 + for hunk in reverse(copy(gitgutter#hunk#hunks(bufnr))) + 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 . 'Gzv' + 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! s:current_hunk() abort + let bufnr = bufnr('') + let current_hunk = [] + + for hunk in gitgutter#hunk#hunks(bufnr) + if gitgutter#hunk#cursor_in_hunk(hunk) + let current_hunk = hunk + break + endif + endfor + + return current_hunk +endfunction + +" Returns truthy if the cursor is in two hunks (which can only happen if the +" cursor is on the first line and lines above have been deleted and lines +" immediately below have been deleted) or falsey otherwise. +function! s:cursor_in_two_hunks() + let hunks = gitgutter#hunk#hunks(bufnr('')) + + if line('.') == 1 && len(hunks) > 1 && hunks[0][2:3] == [0, 0] && hunks[1][2:3] == [1, 0] + return 1 + endif + + return 0 +endfunction + +" A line can be in 0 or 1 hunks, with the following exception: when the first +" line(s) of a file has been deleted, and the new second line (and +" optionally below) has been deleted, the new first line is in two hunks. +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 + + +function! gitgutter#hunk#in_hunk(lnum) + " Hunks are sorted in the order they appear in the buffer. + for hunk in gitgutter#hunk#hunks(bufnr('')) + " if in a hunk on first line of buffer + if a:lnum == 1 && hunk[2] == 0 + return 1 + endif + + " if in a hunk generally + if a:lnum >= hunk[2] && a:lnum < hunk[2] + (hunk[3] == 0 ? 1 : hunk[3]) + return 1 + endif + + " if hunk starts after the given line + if a:lnum < hunk[2] + return 0 + endif + endfor + + return 0 +endfunction + + +function! gitgutter#hunk#text_object(inner) abort + let hunk = s: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 + + +function! gitgutter#hunk#stage() abort + call s:hunk_op(function('s:stage')) + silent! call repeat#set("\<Plug>GitGutterStageHunk", -1)<CR> +endfunction + +function! gitgutter#hunk#undo() abort + call s:hunk_op(function('s:undo')) + silent! call repeat#set("\<Plug>GitGutterUndoHunk", -1)<CR> +endfunction + +function! gitgutter#hunk#preview() abort + call s:hunk_op(function('s:preview')) + silent! call repeat#set("\<Plug>GitGutterPreviewHunk", -1)<CR> +endfunction + + +function! s:hunk_op(op) + let bufnr = bufnr('') + + if gitgutter#utility#is_active(bufnr) + " Get a (synchronous) diff. + let [async, g:gitgutter_async] = [g:gitgutter_async, 0] + let diff = gitgutter#diff#run_diff(bufnr, 'index', 1) + let g:gitgutter_async = async + + call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff)) + + if empty(s:current_hunk()) + call gitgutter#utility#warn('cursor is not in a hunk') + elseif s:cursor_in_two_hunks() + let choice = input('Choose hunk: upper or lower (u/l)? ') + " Clear input + normal! :<ESC> + if choice =~ 'u' + call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 0)) + elseif choice =~ 'l' + call a:op(gitgutter#diff#hunk_diff(bufnr, diff, 1)) + else + call gitgutter#utility#warn('did not recognise your choice') + endif + else + call a:op(gitgutter#diff#hunk_diff(bufnr, diff)) + endif + endif +endfunction + + +function! s:stage(hunk_diff) + let bufnr = bufnr('') + let diff = s:adjust_header(bufnr, a:hunk_diff) + " Apply patch to index. + call gitgutter#utility#system( + \ gitgutter#utility#cd_cmd(bufnr, g:gitgutter_git_executable.' apply --cached --unidiff-zero - '), + \ diff) + + " Refresh gitgutter's view of buffer. + call gitgutter#process_buffer(bufnr, 1) +endfunction + + +function! s:undo(hunk_diff) + " Apply reverse patch to buffer. + let hunk = gitgutter#diff#parse_hunk(split(a:hunk_diff, '\n')[4]) + let lines = map(split(a:hunk_diff, '\n')[5:], 'v:val[1:]') + let lnum = hunk[2] + let added_only = hunk[1] == 0 && hunk[3] > 0 + let removed_only = hunk[1] > 0 && hunk[3] == 0 + + if removed_only + call append(lnum, lines) + elseif added_only + execute lnum .','. (lnum+len(lines)-1) .'d' + else + call append(lnum-1, lines[0:hunk[1]]) + execute (lnum+hunk[1]) .','. (lnum+hunk[1]+hunk[3]) .'d' + endif +endfunction + + +function! s:preview(hunk_diff) + let hunk_lines = split(s:discard_header(a:hunk_diff), "\n") + let hunk_lines_length = len(hunk_lines) + let previewheight = min([hunk_lines_length, &previewheight]) + + silent! wincmd P + if !&previewwindow + noautocmd execute 'bo' previewheight 'new' + set previewwindow + else + execute 'resize' previewheight + endif + + setlocal noreadonly modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile + execute "%delete_" + call append(0, hunk_lines) + normal! gg + setlocal readonly nomodifiable + + noautocmd wincmd p +endfunction + + +function! s:adjust_header(bufnr, hunk_diff) + let filepath = gitgutter#utility#repo_path(a:bufnr, 0) + return s:adjust_hunk_summary(s:fix_file_references(filepath, a:hunk_diff)) +endfunction + + +" Replaces references to temp files with the actual file. +function! s:fix_file_references(filepath, hunk_diff) + let lines = split(a:hunk_diff, '\n') + + let left_prefix = matchstr(lines[2], '[abciow12]').'/' + let right_prefix = matchstr(lines[3], '[abciow12]').'/' + let quote = lines[0][11] == '"' ? '"' : '' + + let left_file = quote.left_prefix.a:filepath.quote + let right_file = quote.right_prefix.a:filepath.quote + + let lines[0] = 'diff --git '.left_file.' '.right_file + let lines[2] = '--- '.left_file + let lines[3] = '+++ '.right_file + + return join(lines, "\n")."\n" +endfunction + +if $VIM_GITGUTTER_TEST + function! gitgutter#hunk#fix_file_references(filepath, hunk_diff) + return s:fix_file_references(a:filepath, a:hunk_diff) + endfunction +endif + + +function! s:adjust_hunk_summary(hunk_diff) abort + let line_adjustment = s:line_adjustment_for_current_hunk() + let diff = split(a:hunk_diff, '\n', 1) + let diff[4] = substitute(diff[4], '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '') + return join(diff, "\n") +endfunction + + +function! s:discard_header(hunk_diff) + return join(split(a:hunk_diff, '\n', 1)[5:], "\n") +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! s:line_adjustment_for_current_hunk() abort + let bufnr = bufnr('') + let adj = 0 + for hunk in gitgutter#hunk#hunks(bufnr) + if gitgutter#hunk#cursor_in_hunk(hunk) + break + else + let adj += hunk[1] - hunk[3] + endif + endfor + return adj +endfunction + diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/sign.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/sign.vim new file mode 100644 index 0000000..4c23dbe --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/sign.vim @@ -0,0 +1,230 @@ +" 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")) + + +function! gitgutter#sign#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(1) + endif +endfunction + +function! gitgutter#sign#disable() abort + let g:gitgutter_signs = 0 + call gitgutter#highlight#define_sign_text_highlights() + + if !g:gitgutter_highlight_lines + call gitgutter#sign#clear_signs(bufnr('')) + call gitgutter#sign#remove_dummy_sign(bufnr(''), 0) + endif +endfunction + +function! gitgutter#sign#toggle() abort + if g:gitgutter_signs + call gitgutter#sign#disable() + else + call gitgutter#sign#enable() + endif +endfunction + + +" Removes gitgutter's signs (excluding dummy sign) from the buffer being processed. +function! gitgutter#sign#clear_signs(bufnr) abort + call s:find_current_signs(a:bufnr) + + let sign_ids = map(values(gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')), 'v:val.id') + call s:remove_signs(a:bufnr, sign_ids, 1) + call gitgutter#utility#setbufvar(a:bufnr, '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(bufnr, modified_lines) abort + call s:find_current_signs(a:bufnr) + + let new_gitgutter_signs_line_numbers = map(copy(a:modified_lines), 'v:val[0]') + let obsolete_signs = s:obsolete_gitgutter_signs_to_remove(a:bufnr, new_gitgutter_signs_line_numbers) + + let flicker_possible = s:remove_all_old_signs && !empty(a:modified_lines) + if flicker_possible + call s:add_dummy_sign(a:bufnr) + endif + + call s:remove_signs(a:bufnr, obsolete_signs, s:remove_all_old_signs) + call s:upsert_new_gitgutter_signs(a:bufnr, a:modified_lines) + + if flicker_possible + call gitgutter#sign#remove_dummy_sign(a:bufnr, 0) + endif +endfunction + + +function! s:add_dummy_sign(bufnr) abort + if !gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign') + execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . a:bufnr + call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 1) + endif +endfunction + +function! gitgutter#sign#remove_dummy_sign(bufnr, force) abort + if gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign') && (a:force || !g:gitgutter_sign_column_always) + execute "sign unplace" s:dummy_sign_id "buffer=" . a:bufnr + call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 0) + endif +endfunction + + +" +" Internal functions +" + + +function! s:find_current_signs(bufnr) abort + 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=" . a: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 gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', dummy_sign_placed) + call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', gitgutter_signs) + call gitgutter#utility#setbufvar(a:bufnr, 'other_signs', other_signs) +endfunction + + +" Returns a list of [<id (number)>, ...] +" Sets `s:remove_all_old_signs` as a side-effect. +function! s:obsolete_gitgutter_signs_to_remove(bufnr, new_gitgutter_signs_line_numbers) abort + let signs_to_remove = [] " list of [<id (number)>, ...] + let remove_all_signs = 1 + let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, '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! s:remove_signs(bufnr, sign_ids, all_signs) abort + if a:all_signs && s:supports_star && empty(gitgutter#utility#getbufvar(a:bufnr, 'other_signs')) + let dummy_sign_present = gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign') + execute "sign unplace * buffer=" . a:bufnr + if dummy_sign_present + execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . a:bufnr + endif + else + for id in a:sign_ids + execute "sign unplace" id + endfor + endif +endfunction + + +function! s:upsert_new_gitgutter_signs(bufnr, modified_lines) abort + let other_signs = gitgutter#utility#getbufvar(a:bufnr, 'other_signs') + let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs') + + " Handle special case where the first line is the site of two hunks: + " lines deleted above at the start of the file, and lines deleted + " immediately below. + if a:modified_lines[0:1] == [[1, 'removed_first_line'], [1, 'removed']] + let modified_lines = [[1, 'removed_above_and_below']] + a:modified_lines[2:] + else + let modified_lines = a:modified_lines + endif + + for line in modified_lines + let line_number = line[0] " <number> + if index(other_signs, line_number) == -1 " don't clobber others' signs + let name = s:highlight_name_for_change(line[1]) + if !has_key(old_gitgutter_signs, line_number) " insert + let id = s:next_sign_id() + execute "sign place" id "line=" . line_number "name=" . name "buffer=" . a: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=" . a:bufnr + end + endif + endif + endfor + " At this point b:gitgutter_gitgutter_signs is out of date. +endfunction + + +function! s: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 + + +function! s: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' + elseif a:text ==# 'removed_above_and_below' + return 'GitGutterLineRemovedAboveAndBelow' + endif +endfunction + + diff --git a/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/utility.vim b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/utility.vim new file mode 100644 index 0000000..470f222 --- /dev/null +++ b/vim/.vim/plugged/vim-gitgutter/autoload/gitgutter/utility.vim @@ -0,0 +1,222 @@ +function! gitgutter#utility#supports_overscore_sign() + if gitgutter#utility#windows() + return &encoding ==? 'utf-8' + else + return &termencoding ==? &encoding || &termencoding == '' + endif +endfunction + +function! gitgutter#utility#setbufvar(buffer, varname, val) + let buffer = +a:buffer + " Default value for getbufvar() was introduced in Vim 7.3.831. + let bvars = getbufvar(buffer, '') + if empty(bvars) + let bvars = {} + endif + let dict = get(bvars, 'gitgutter', {}) + let needs_setting = empty(dict) + let dict[a:varname] = a:val + if needs_setting + call setbufvar(buffer, 'gitgutter', dict) + endif +endfunction + +function! gitgutter#utility#getbufvar(buffer, varname, ...) + let dict = get(getbufvar(a:buffer, ''), 'gitgutter', {}) + if has_key(dict, a:varname) + return dict[a:varname] + else + if a:0 + return a:1 + endif + endif +endfunction + +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(bufnr, message, key) abort + if empty(gitgutter#utility#getbufvar(a:bufnr, a:key)) + call gitgutter#utility#setbufvar(a:bufnr, a:key, '1') + echohl WarningMsg + redraw | echom '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(bufnr) abort + return g:gitgutter_enabled && + \ gitgutter#utility#getbufvar(a:bufnr, 'enabled', 1) && + \ !pumvisible() && + \ s:is_file_buffer(a:bufnr) && + \ s:exists_file(a:bufnr) && + \ s:not_git_dir(a:bufnr) +endfunction + +function! s:not_git_dir(bufnr) abort + return s:dir(a:bufnr) !~ '[/\\]\.git\($\|[/\\]\)' +endfunction + +function! s:is_file_buffer(bufnr) abort + return empty(getbufvar(a:bufnr, '&buftype')) +endfunction + +" From tpope/vim-fugitive +function! s:winshell() + return &shell =~? 'cmd' || exists('+shellslash') && !&shellslash +endfunction + +" From tpope/vim-fugitive +function! gitgutter#utility#shellescape(arg) abort + if a:arg =~ '^[A-Za-z0-9_/.-]\+$' + return a:arg + elseif s:winshell() + return '"' . substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g') . '"' + else + return shellescape(a:arg) + endif +endfunction + +function! gitgutter#utility#file(bufnr) + return s:abs_path(a:bufnr, 1) +endfunction + +" Not shellescaped +function! gitgutter#utility#extension(bufnr) abort + return fnamemodify(s:abs_path(a:bufnr, 0), ':e') +endfunction + +function! gitgutter#utility#system(cmd, ...) abort + call gitgutter#debug#log(a:cmd, a:000) + + call s:use_known_shell() + silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1) + call s:restore_shell() + + return output +endfunction + +" Path of file relative to repo root. +" +" * empty string - not set +" * non-empty string - path +" * -1 - pending +" * -2 - not tracked by git +function! gitgutter#utility#repo_path(bufnr, shellesc) abort + let p = gitgutter#utility#getbufvar(a:bufnr, 'path') + return a:shellesc ? gitgutter#utility#shellescape(p) : p +endfunction + +function! gitgutter#utility#set_repo_path(bufnr) abort + " Values of path: + " * non-empty string - path + " * -1 - pending + " * -2 - not tracked by git + + call gitgutter#utility#setbufvar(a:bufnr, 'path', -1) + let cmd = gitgutter#utility#cd_cmd(a:bufnr, g:gitgutter_git_executable.' ls-files --error-unmatch --full-name -z -- '.gitgutter#utility#shellescape(s:filename(a:bufnr))) + + if g:gitgutter_async && gitgutter#async#available() + if has('lambda') + call gitgutter#async#execute(cmd, a:bufnr, { + \ 'out': {bufnr, path -> gitgutter#utility#setbufvar(bufnr, 'path', s:strip_trailing_new_line(path))}, + \ 'err': {bufnr -> gitgutter#utility#setbufvar(bufnr, 'path', -2)}, + \ }) + else + if has('nvim') && !has('nvim-0.2.0') + call gitgutter#async#execute(cmd, a:bufnr, { + \ 'out': function('s:set_path'), + \ 'err': function('s:not_tracked_by_git') + \ }) + else + call gitgutter#async#execute(cmd, a:bufnr, { + \ 'out': function('s:set_path'), + \ 'err': function('s:set_path', [-2]) + \ }) + endif + endif + else + let path = gitgutter#utility#system(cmd) + if v:shell_error + call gitgutter#utility#setbufvar(a:bufnr, 'path', -2) + else + call gitgutter#utility#setbufvar(a:bufnr, 'path', s:strip_trailing_new_line(path)) + endif + endif +endfunction + +if has('nvim') && !has('nvim-0.2.0') + function! s:not_tracked_by_git(bufnr) + call s:set_path(a:bufnr, -2) + endfunction +endif + +function! s:set_path(bufnr, path) + if a:bufnr == -2 + let [bufnr, path] = [a:path, a:bufnr] + call gitgutter#utility#setbufvar(bufnr, 'path', path) + else + call gitgutter#utility#setbufvar(a:bufnr, 'path', s:strip_trailing_new_line(a:path)) + endif +endfunction + +function! gitgutter#utility#cd_cmd(bufnr, cmd) abort + let cd = s:unc_path(a:bufnr) ? 'pushd' : (gitgutter#utility#windows() ? 'cd /d' : 'cd') + return cd.' '.s:dir(a:bufnr).' && '.a:cmd +endfunction + +function! s:unc_path(bufnr) + return s:abs_path(a:bufnr, 0) =~ '^\\\\' +endfunction + +function! s:use_known_shell() abort + if has('unix') && &shell !=# 'sh' + let [s:shell, s:shellcmdflag, s:shellredir] = [&shell, &shellcmdflag, &shellredir] + let &shell = 'sh' + set shellcmdflag=-c shellredir=>%s\ 2>&1 + endif +endfunction + +function! s:restore_shell() abort + if has('unix') && exists('s:shell') + let [&shell, &shellcmdflag, &shellredir] = [s:shell, s:shellcmdflag, s:shellredir] + endif +endfunction + +function! s:abs_path(bufnr, shellesc) + let p = resolve(expand('#'.a:bufnr.':p')) + return a:shellesc ? gitgutter#utility#shellescape(p) : p +endfunction + +function! s:dir(bufnr) abort + return gitgutter#utility#shellescape(fnamemodify(s:abs_path(a:bufnr, 0), ':h')) +endfunction + +" Not shellescaped. +function! s:filename(bufnr) abort + return fnamemodify(s:abs_path(a:bufnr, 0), ':t') +endfunction + +function! s:exists_file(bufnr) abort + return filereadable(s:abs_path(a:bufnr, 0)) +endfunction + +" Get rid of any trailing new line or SOH character. +" +" git ls-files -z produces output with null line termination. +" Vim's system() replaces any null characters in the output +" with SOH (start of header), i.e. ^A. +function! s:strip_trailing_new_line(line) abort + return substitute(a:line, '[[:cntrl:]]$', '', '') +endfunction + +function! gitgutter#utility#windows() + return has('win64') || has('win32') || has('win16') +endfunction |
