mirror of
https://github.com/amix/vimrc
synced 2025-07-07 00:15:00 +08:00
gitignore sources_non_forked_cache
This commit is contained in:
107
sources_non_forked/vim-gitgutter/autoload/gitgutter/async.vim
Normal file
107
sources_non_forked/vim-gitgutter/autoload/gitgutter/async.vim
Normal file
@ -0,0 +1,107 @@
|
||||
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'))
|
||||
\ )
|
||||
\ )
|
||||
|
||||
let s:jobs = {}
|
||||
|
||||
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
|
||||
let job = 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)
|
||||
\ })
|
||||
let s:jobs[s:job_id(job)] = 1
|
||||
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
|
||||
if a:data != [''] " With Neovim there is always [''] reported on stderr.
|
||||
call self.handler.err(self.buffer)
|
||||
endif
|
||||
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)
|
||||
let jobid = s:job_id(job)
|
||||
if has_key(s:jobs, jobid) | unlet s:jobs[jobid] | endif
|
||||
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
|
||||
|
||||
function! s:job_id(job)
|
||||
" Vim
|
||||
return job_info(a:job).process
|
||||
endfunction
|
109
sources_non_forked/vim-gitgutter/autoload/gitgutter/debug.vim
Normal file
109
sources_non_forked/vim-gitgutter/autoload/gitgutter/debug.vim
Normal file
@ -0,0 +1,109 @@
|
||||
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')
|
||||
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(g:gitgutter_grep.' --version')
|
||||
call s:output( substitute(v, '\n$', '', '') )
|
||||
|
||||
let v = system(g:gitgutter_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
|
||||
|
421
sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim
Normal file
421
sources_non_forked/vim-gitgutter/autoload/gitgutter/diff.vim
Normal file
@ -0,0 +1,421 @@
|
||||
scriptencoding utf8
|
||||
|
||||
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 gitgutter#utility#system(g:gitgutter_git_executable.' '.g:gitgutter_git_args.' -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
|
||||
if gitgutter#utility#repo_path(a:bufnr, 0) == -1
|
||||
throw 'gitgutter path not set'
|
||||
endif
|
||||
|
||||
if gitgutter#utility#repo_path(a:bufnr, 0) == -2
|
||||
throw 'gitgutter not tracked'
|
||||
endif
|
||||
|
||||
if gitgutter#utility#repo_path(a:bufnr, 0) == -3
|
||||
throw 'gitgutter assume unchanged'
|
||||
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 = gitgutter#utility#get_diff_base(a:bufnr).':'.gitgutter#utility#repo_path(a:bufnr, 1)
|
||||
let cmd .= g:gitgutter_git_executable.' '.g:gitgutter_git_args.' --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.' '.g:gitgutter_git_args.' --no-pager'
|
||||
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 g:gitgutter_max_signs != -1 && 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 || g:gitgutter_highlight_linenrs
|
||||
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
|
||||
|
||||
if getbufvar(a:bufnr, '&endofline')
|
||||
call add(bufcontents, '')
|
||||
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
|
||||
|
||||
" The file we are writing to is a temporary file. Sometimes the parent
|
||||
" directory is deleted outside Vim but, because Vim caches the directory
|
||||
" name at startup and does not check for its existence subsequently, Vim
|
||||
" does not realise. This causes E482 errors.
|
||||
try
|
||||
call writefile(bufcontents, a:file, 'b')
|
||||
catch /E482/
|
||||
call mkdir(fnamemodify(a:file, ':h'), '', '0700')
|
||||
call writefile(bufcontents, a:file, 'b')
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:save_last_seen_change(bufnr) abort
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'tick', getbufvar(a:bufnr, 'changedtick'))
|
||||
endfunction
|
@ -0,0 +1,201 @@
|
||||
" This is the minimum number of characters required between regions of change
|
||||
" in a line. It's somewhat arbitrary: higher values mean less visual busyness;
|
||||
" lower values mean more detail.
|
||||
let s:gap_between_regions = 5
|
||||
|
||||
|
||||
" Calculates the changed portions of lines.
|
||||
"
|
||||
" Based on:
|
||||
"
|
||||
" - diff-highlight (included with git)
|
||||
" https://github.com/git/git/blob/master/contrib/diff-highlight/DiffHighlight.pm
|
||||
"
|
||||
" - Diff Strategies, Neil Fraser
|
||||
" https://neil.fraser.name/writing/diff/
|
||||
|
||||
|
||||
" Returns a list of intra-line changed regions.
|
||||
" Each element is a list:
|
||||
"
|
||||
" [
|
||||
" line number (1-based),
|
||||
" type ('+' or '-'),
|
||||
" start column (1-based, inclusive),
|
||||
" stop column (1-based, inclusive),
|
||||
" ]
|
||||
"
|
||||
" Args:
|
||||
" hunk_body - list of lines
|
||||
function! gitgutter#diff_highlight#process(hunk_body)
|
||||
" Check whether we have the same number of lines added as removed.
|
||||
let [removed, added] = [0, 0]
|
||||
for line in a:hunk_body
|
||||
if line[0] == '-'
|
||||
let removed += 1
|
||||
elseif line[0] == '+'
|
||||
let added += 1
|
||||
endif
|
||||
endfor
|
||||
if removed != added
|
||||
return []
|
||||
endif
|
||||
|
||||
let regions = []
|
||||
|
||||
for i in range(removed)
|
||||
" pair lines by position
|
||||
let rline = a:hunk_body[i]
|
||||
let aline = a:hunk_body[i + removed]
|
||||
|
||||
call s:diff(rline, aline, i, i+removed, 0, 0, regions, 1)
|
||||
endfor
|
||||
|
||||
return regions
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:diff(rline, aline, rlinenr, alinenr, rprefix, aprefix, regions, whole_line)
|
||||
" diff marker does not count as a difference in prefix
|
||||
let start = a:whole_line ? 1 : 0
|
||||
let prefix = s:common_prefix(a:rline[start:], a:aline[start:])
|
||||
if a:whole_line
|
||||
let prefix += 1
|
||||
endif
|
||||
let [rsuffix, asuffix] = s:common_suffix(a:rline, a:aline, prefix+1)
|
||||
|
||||
" region of change (common prefix and suffix removed)
|
||||
let rtext = a:rline[prefix+1:rsuffix-1]
|
||||
let atext = a:aline[prefix+1:asuffix-1]
|
||||
|
||||
" singular insertion
|
||||
if empty(rtext)
|
||||
if !a:whole_line || len(atext) != len(a:aline) " not whole line
|
||||
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+asuffix+1-1])
|
||||
endif
|
||||
return
|
||||
endif
|
||||
|
||||
" singular deletion
|
||||
if empty(atext)
|
||||
if !a:whole_line || len(rtext) != len(a:rline) " not whole line
|
||||
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+rsuffix+1-1])
|
||||
endif
|
||||
return
|
||||
endif
|
||||
|
||||
" two insertions
|
||||
let j = stridx(atext, rtext)
|
||||
if j != -1
|
||||
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+prefix+j+1])
|
||||
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1+j+len(rtext), a:aprefix+asuffix+1-1])
|
||||
return
|
||||
endif
|
||||
|
||||
" two deletions
|
||||
let j = stridx(rtext, atext)
|
||||
if j != -1
|
||||
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+prefix+j+1])
|
||||
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1+j+len(atext), a:rprefix+rsuffix+1-1])
|
||||
return
|
||||
endif
|
||||
|
||||
" two edits
|
||||
let lcs = s:lcs(rtext, atext)
|
||||
" TODO do we need to ensure we don't get more than 2 elements when splitting?
|
||||
if len(lcs) > s:gap_between_regions
|
||||
let redits = s:split(rtext, lcs)
|
||||
let aedits = s:split(atext, lcs)
|
||||
call s:diff(redits[0], aedits[0], a:rlinenr, a:alinenr, a:rprefix+prefix+1, a:aprefix+prefix+1, a:regions, 0)
|
||||
call s:diff(redits[1], aedits[1], a:rlinenr, a:alinenr, a:rprefix+prefix+1+len(redits[0])+len(lcs), a:aprefix+prefix+1+len(aedits[0])+len(lcs), a:regions, 0)
|
||||
return
|
||||
endif
|
||||
|
||||
" fall back to highlighting entire changed area
|
||||
|
||||
" if a change (but not the whole line)
|
||||
if !a:whole_line || ((prefix != 0 || rsuffix != len(a:rline)) && prefix+1 < rsuffix)
|
||||
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+rsuffix+1-1])
|
||||
endif
|
||||
|
||||
" if a change (but not the whole line)
|
||||
if !a:whole_line || ((prefix != 0 || asuffix != len(a:aline)) && prefix+1 < asuffix)
|
||||
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+asuffix+1-1])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:lcs(s1, s2)
|
||||
if empty(a:s1) || empty(a:s2)
|
||||
return ''
|
||||
endif
|
||||
|
||||
let matrix = map(repeat([repeat([0], len(a:s2)+1)], len(a:s1)+1), 'copy(v:val)')
|
||||
|
||||
let maxlength = 0
|
||||
let endindex = len(a:s1)
|
||||
|
||||
for i in range(1, len(a:s1))
|
||||
for j in range(1, len(a:s2))
|
||||
if a:s1[i-1] ==# a:s2[j-1]
|
||||
let matrix[i][j] = 1 + matrix[i-1][j-1]
|
||||
if matrix[i][j] > maxlength
|
||||
let maxlength = matrix[i][j]
|
||||
let endindex = i - 1
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
return a:s1[endindex - maxlength + 1 : endindex]
|
||||
endfunction
|
||||
|
||||
|
||||
" Returns 0-based index of last character of common prefix
|
||||
" If there is no common prefix, returns -1.
|
||||
"
|
||||
" a, b - strings
|
||||
"
|
||||
function! s:common_prefix(a, b)
|
||||
let len = min([len(a:a), len(a:b)])
|
||||
if len == 0
|
||||
return -1
|
||||
endif
|
||||
for i in range(len)
|
||||
if a:a[i:i] !=# a:b[i:i]
|
||||
return i - 1
|
||||
endif
|
||||
endfor
|
||||
return i
|
||||
endfunction
|
||||
|
||||
|
||||
" Returns 0-based indices of start of common suffix
|
||||
"
|
||||
" a, b - strings
|
||||
" start - 0-based index to start from
|
||||
function! s:common_suffix(a, b, start)
|
||||
let [sa, sb] = [len(a:a), len(a:b)]
|
||||
while sa >= a:start && sb >= a:start
|
||||
if a:a[sa] ==# a:b[sb]
|
||||
let sa -= 1
|
||||
let sb -= 1
|
||||
else
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
return [sa+1, sb+1]
|
||||
endfunction
|
||||
|
||||
|
||||
" Split a string on another string.
|
||||
" Assumes 1 occurrence of the delimiter.
|
||||
function! s:split(str, delimiter)
|
||||
let i = stridx(a:str, a:delimiter)
|
||||
|
||||
if i == 0
|
||||
return ['', a:str[len(a:delimiter):]]
|
||||
endif
|
||||
|
||||
return [a:str[:i-1], a:str[i+len(a:delimiter):]]
|
||||
endfunction
|
115
sources_non_forked/vim-gitgutter/autoload/gitgutter/fold.vim
Normal file
115
sources_non_forked/vim-gitgutter/autoload/gitgutter/fold.vim
Normal file
@ -0,0 +1,115 @@
|
||||
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
|
||||
|
||||
|
||||
function! gitgutter#fold#foldtext()
|
||||
if !gitgutter#fold#is_changed()
|
||||
return foldtext()
|
||||
endif
|
||||
|
||||
return substitute(foldtext(), ':', ' (*):', '')
|
||||
endfunction
|
||||
|
||||
|
||||
" Returns 1 if any of the folded lines have been changed
|
||||
" (added, removed, or modified), 0 otherwise.
|
||||
function! gitgutter#fold#is_changed()
|
||||
for hunk in gitgutter#hunk#hunks(bufnr(''))
|
||||
let hunk_begin = hunk[2]
|
||||
let hunk_end = hunk[2] + (hunk[3] == 0 ? 1 : hunk[3])
|
||||
|
||||
if hunk_end < v:foldstart
|
||||
continue
|
||||
endif
|
||||
|
||||
if hunk_begin > v:foldend
|
||||
break
|
||||
endif
|
||||
|
||||
return 1
|
||||
endfor
|
||||
|
||||
return 0
|
||||
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
|
||||
|
@ -0,0 +1,245 @@
|
||||
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(''))
|
||||
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#linenr_disable() abort
|
||||
let g:gitgutter_highlight_linenrs = 0
|
||||
call s:define_sign_linenr_highlights()
|
||||
|
||||
if !g:gitgutter_signs
|
||||
call gitgutter#sign#clear_signs(bufnr(''))
|
||||
endif
|
||||
|
||||
redraw!
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#linenr_enable() abort
|
||||
let old_highlight_linenrs = g:gitgutter_highlight_linenrs
|
||||
|
||||
let g:gitgutter_highlight_linenrs = 1
|
||||
call s:define_sign_linenr_highlights()
|
||||
|
||||
if !old_highlight_linenrs && !g:gitgutter_signs
|
||||
call gitgutter#all(1)
|
||||
endif
|
||||
|
||||
redraw!
|
||||
endfunction
|
||||
|
||||
function! gitgutter#highlight#linenr_toggle() abort
|
||||
if g:gitgutter_highlight_linenrs
|
||||
call gitgutter#highlight#linenr_disable()
|
||||
else
|
||||
call gitgutter#highlight#linenr_enable()
|
||||
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.
|
||||
for type in ["Add", "Change", "Delete"]
|
||||
if hlexists("GitGutter".type) && s:get_foreground_colors("GitGutter".type) != ['NONE', 'NONE']
|
||||
if g:gitgutter_set_sign_backgrounds
|
||||
execute "highlight GitGutter".type." guibg=".guibg." ctermbg=".ctermbg
|
||||
endif
|
||||
continue
|
||||
elseif s:useful_diff_colours()
|
||||
let [guifg, ctermfg] = s:get_foreground_colors('Diff'.type)
|
||||
else
|
||||
let [guifg, ctermfg] = s:get_foreground_fallback_colors(type)
|
||||
endif
|
||||
execute "highlight GitGutter".type." guifg=".guifg." guibg=".guibg." ctermfg=".ctermfg." ctermbg=".ctermbg
|
||||
endfor
|
||||
|
||||
if hlexists("GitGutterChangeDelete") && g:gitgutter_set_sign_backgrounds
|
||||
execute "highlight GitGutterChangeDelete guibg=".guibg." ctermbg=".ctermbg
|
||||
endif
|
||||
|
||||
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
|
||||
|
||||
highlight default link GitGutterAddLineNr CursorLineNr
|
||||
highlight default link GitGutterChangeLineNr CursorLineNr
|
||||
highlight default link GitGutterDeleteLineNr CursorLineNr
|
||||
highlight default link GitGutterChangeDeleteLineNr CursorLineNr
|
||||
|
||||
" Highlights used intra line.
|
||||
highlight default GitGutterAddIntraLine gui=reverse cterm=reverse
|
||||
highlight default GitGutterDeleteIntraLine gui=reverse cterm=reverse
|
||||
" Set diff syntax colours (used in the preview window) - diffAdded,diffChanged,diffRemoved -
|
||||
" to match the signs, if not set aleady.
|
||||
for [dtype,type] in [['Added','Add'], ['Changed','Change'], ['Removed','Delete']]
|
||||
if !hlexists('diff'.dtype)
|
||||
let [guifg, ctermfg] = s:get_foreground_colors('GitGutter'.type)
|
||||
execute "highlight diff".dtype." guifg=".guifg." ctermfg=".ctermfg." guibg=NONE ctermbg=NONE"
|
||||
endif
|
||||
endfor
|
||||
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
|
||||
|
||||
call s:define_sign_text()
|
||||
call gitgutter#highlight#define_sign_text_highlights()
|
||||
call s:define_sign_line_highlights()
|
||||
call s:define_sign_linenr_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=NONE
|
||||
sign define GitGutterLineModified linehl=NONE
|
||||
sign define GitGutterLineRemoved linehl=NONE
|
||||
sign define GitGutterLineRemovedFirstLine linehl=NONE
|
||||
sign define GitGutterLineRemovedAboveAndBelow linehl=NONE
|
||||
sign define GitGutterLineModifiedRemoved linehl=NONE
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:define_sign_linenr_highlights() abort
|
||||
if has('nvim-0.3.2')
|
||||
try
|
||||
if g:gitgutter_highlight_linenrs
|
||||
sign define GitGutterLineAdded numhl=GitGutterAddLineNr
|
||||
sign define GitGutterLineModified numhl=GitGutterChangeLineNr
|
||||
sign define GitGutterLineRemoved numhl=GitGutterDeleteLineNr
|
||||
sign define GitGutterLineRemovedFirstLine numhl=GitGutterDeleteLineNr
|
||||
sign define GitGutterLineRemovedAboveAndBelow numhl=GitGutterDeleteLineNr
|
||||
sign define GitGutterLineModifiedRemoved numhl=GitGutterChangeDeleteLineNr
|
||||
else
|
||||
sign define GitGutterLineAdded numhl=NONE
|
||||
sign define GitGutterLineModified numhl=NONE
|
||||
sign define GitGutterLineRemoved numhl=NONE
|
||||
sign define GitGutterLineRemovedFirstLine numhl=NONE
|
||||
sign define GitGutterLineRemovedAboveAndBelow numhl=NONE
|
||||
sign define GitGutterLineModifiedRemoved numhl=NONE
|
||||
endif
|
||||
catch /E475/
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:get_hl(group, what, mode) abort
|
||||
let r = synIDattr(synIDtrans(hlID(a:group)), a:what, a:mode)
|
||||
if empty(r) || r == -1
|
||||
return 'NONE'
|
||||
endif
|
||||
return r
|
||||
endfunction
|
||||
|
||||
function! s:get_foreground_colors(group) abort
|
||||
let ctermfg = s:get_hl(a:group, 'fg', 'cterm')
|
||||
let guifg = s:get_hl(a:group, 'fg', 'gui')
|
||||
return [guifg, ctermfg]
|
||||
endfunction
|
||||
|
||||
function! s:get_background_colors(group) abort
|
||||
let ctermbg = s:get_hl(a:group, 'bg', 'cterm')
|
||||
let guibg = s:get_hl(a:group, 'bg', 'gui')
|
||||
return [guibg, ctermbg]
|
||||
endfunction
|
||||
|
||||
function! s:useful_diff_colours()
|
||||
let [guifg_add, ctermfg_add] = s:get_foreground_colors('DiffAdd')
|
||||
let [guifg_del, ctermfg_del] = s:get_foreground_colors('DiffDelete')
|
||||
|
||||
return guifg_add != guifg_del && ctermfg_add != ctermfg_del
|
||||
endfunction
|
||||
|
||||
function! s:get_foreground_fallback_colors(type)
|
||||
if a:type == 'Add'
|
||||
return ['#009900', '2']
|
||||
elseif a:type == 'Change'
|
||||
return ['#bbbb00', '3']
|
||||
elseif a:type == 'Delete'
|
||||
return ['#ff2222', '1']
|
||||
endif
|
||||
endfunction
|
629
sources_non_forked/vim-gitgutter/autoload/gitgutter/hunk.vim
Normal file
629
sources_non_forked/vim-gitgutter/autoload/gitgutter/hunk.vim
Normal file
@ -0,0 +1,629 @@
|
||||
let s:winid = 0
|
||||
let s:preview_bufnr = 0
|
||||
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
|
||||
|
||||
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) | return | endif
|
||||
|
||||
let hunks = gitgutter#hunk#hunks(bufnr)
|
||||
if empty(hunks)
|
||||
call gitgutter#utility#warn('No hunks in file')
|
||||
return
|
||||
endif
|
||||
|
||||
let current_line = line('.')
|
||||
let hunk_count = 0
|
||||
for hunk in hunks
|
||||
if hunk[2] > current_line
|
||||
let hunk_count += 1
|
||||
if hunk_count == a:count
|
||||
execute 'normal!' hunk[2] . 'Gzv'
|
||||
if g:gitgutter_show_msg_on_hunk_jumping
|
||||
redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
|
||||
endif
|
||||
if gitgutter#hunk#is_preview_window_open()
|
||||
call gitgutter#hunk#preview()
|
||||
endif
|
||||
return
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
call gitgutter#utility#warn('No more hunks')
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#prev_hunk(count) abort
|
||||
let bufnr = bufnr('')
|
||||
if !gitgutter#utility#is_active(bufnr) | return | endif
|
||||
|
||||
let hunks = gitgutter#hunk#hunks(bufnr)
|
||||
if empty(hunks)
|
||||
call gitgutter#utility#warn('No hunks in file')
|
||||
return
|
||||
endif
|
||||
|
||||
let current_line = line('.')
|
||||
let hunk_count = 0
|
||||
for hunk in reverse(copy(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 . 'Gzv'
|
||||
if g:gitgutter_show_msg_on_hunk_jumping
|
||||
redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
|
||||
endif
|
||||
if gitgutter#hunk#is_preview_window_open()
|
||||
call gitgutter#hunk#preview()
|
||||
endif
|
||||
return
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
call gitgutter#utility#warn('No previous hunks')
|
||||
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
|
||||
if !s:in_hunk_preview_window() && !gitgutter#utility#has_repo_path(bufnr('')) | return | endif
|
||||
|
||||
if a:0 && (a:1 != 1 || a:2 != line('$'))
|
||||
call s:hunk_op(function('s:stage'), a:1, a:2)
|
||||
else
|
||||
call s:hunk_op(function('s:stage'))
|
||||
endif
|
||||
silent! call repeat#set("\<Plug>(GitGutterStageHunk)", -1)
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#undo() abort
|
||||
if !gitgutter#utility#has_repo_path(bufnr('')) | return | endif
|
||||
|
||||
call s:hunk_op(function('s:undo'))
|
||||
silent! call repeat#set("\<Plug>(GitGutterUndoHunk)", -1)
|
||||
endfunction
|
||||
|
||||
function! gitgutter#hunk#preview() abort
|
||||
if !gitgutter#utility#has_repo_path(bufnr('')) | return | endif
|
||||
|
||||
call s:hunk_op(function('s:preview'))
|
||||
silent! call repeat#set("\<Plug>(GitGutterPreviewHunk)", -1)
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:hunk_op(op, ...)
|
||||
let bufnr = bufnr('')
|
||||
|
||||
if s:in_hunk_preview_window()
|
||||
if string(a:op) =~ '_stage'
|
||||
" combine hunk-body in preview window with updated hunk-header
|
||||
let hunk_body = getline(1, '$')
|
||||
|
||||
let [removed, added] = [0, 0]
|
||||
for line in hunk_body
|
||||
if line[0] == '-'
|
||||
let removed += 1
|
||||
elseif line[0] == '+'
|
||||
let added += 1
|
||||
endif
|
||||
endfor
|
||||
|
||||
let hunk_header = b:hunk_header
|
||||
" from count
|
||||
let hunk_header[4] = substitute(hunk_header[4], '\(-\d\+\)\(,\d\+\)\?', '\=submatch(1).",".removed', '')
|
||||
" to count
|
||||
let hunk_header[4] = substitute(hunk_header[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".added', '')
|
||||
|
||||
let hunk_diff = join(hunk_header + hunk_body, "\n")."\n"
|
||||
|
||||
call s:goto_original_window()
|
||||
call gitgutter#hunk#close_hunk_preview_window()
|
||||
call s:stage(hunk_diff)
|
||||
endif
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
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, g:gitgutter_diff_relative_to, 1)
|
||||
let g:gitgutter_async = async
|
||||
|
||||
call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff))
|
||||
call gitgutter#diff#process_hunks(bufnr, gitgutter#hunk#hunks(bufnr)) " so the hunk summary is updated
|
||||
|
||||
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
|
||||
let hunk_diff = gitgutter#diff#hunk_diff(bufnr, diff)
|
||||
|
||||
if a:0
|
||||
let hunk_first_line = s:current_hunk()[2]
|
||||
let hunk_diff = s:part_of_diff(hunk_diff, a:1-hunk_first_line, a:2-hunk_first_line)
|
||||
endif
|
||||
|
||||
call a:op(hunk_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.' '.g:gitgutter_git_args.' apply --cached --unidiff-zero - '),
|
||||
\ diff)
|
||||
if v:shell_error
|
||||
call gitgutter#utility#warn('Patch does not apply')
|
||||
else
|
||||
if exists('#User#GitGutterStage')
|
||||
execute 'doautocmd' s:nomodeline 'User GitGutterStage'
|
||||
endif
|
||||
endif
|
||||
|
||||
" 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, '\r\?\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 lines = split(a:hunk_diff, '\r\?\n')
|
||||
let header = lines[0:4]
|
||||
let body = lines[5:]
|
||||
|
||||
call s:open_hunk_preview_window()
|
||||
call s:populate_hunk_preview_window(header, body)
|
||||
call s:enable_staging_from_hunk_preview_window()
|
||||
if &previewwindow
|
||||
call s:goto_original_window()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
" Returns a new hunk diff using the specified lines from the given one.
|
||||
" Assumes all lines are additions.
|
||||
" a:first, a:last - 0-based indexes into the body of the hunk.
|
||||
function! s:part_of_diff(hunk_diff, first, last)
|
||||
let diff_lines = split(a:hunk_diff, '\n', 1)
|
||||
|
||||
" adjust 'to' line count in header
|
||||
let diff_lines[4] = substitute(diff_lines[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".(a:last-a:first+1)', '')
|
||||
|
||||
return join(diff_lines[0:4] + diff_lines[5+a:first:5+a:last], "\n")."\n"
|
||||
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
|
||||
|
||||
|
||||
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], '+\zs\(\d\+\)', '\=submatch(1)+line_adjustment', '')
|
||||
return join(diff, "\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
|
||||
|
||||
|
||||
function! s:in_hunk_preview_window()
|
||||
if g:gitgutter_preview_win_floating
|
||||
return win_id2win(s:winid) == winnr()
|
||||
else
|
||||
return &previewwindow
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
" Floating window: does not move cursor to floating window.
|
||||
" Preview window: moves cursor to preview window.
|
||||
function! s:open_hunk_preview_window()
|
||||
if g:gitgutter_preview_win_floating
|
||||
if exists('*nvim_open_win')
|
||||
call gitgutter#hunk#close_hunk_preview_window()
|
||||
|
||||
let buf = nvim_create_buf(v:false, v:false)
|
||||
" Set default width and height for now.
|
||||
let s:winid = nvim_open_win(buf, v:false, g:gitgutter_floating_window_options)
|
||||
call nvim_buf_set_option(buf, 'filetype', 'diff')
|
||||
call nvim_buf_set_option(buf, 'buftype', 'acwrite')
|
||||
call nvim_buf_set_option(buf, 'bufhidden', 'delete')
|
||||
call nvim_buf_set_option(buf, 'swapfile', v:false)
|
||||
call nvim_buf_set_name(buf, 'gitgutter://hunk-preview')
|
||||
|
||||
" Assumes cursor is in original window.
|
||||
autocmd CursorMoved <buffer> ++once call gitgutter#hunk#close_hunk_preview_window()
|
||||
|
||||
if g:gitgutter_close_preview_on_escape
|
||||
" Map <Esc> to close the floating preview.
|
||||
nnoremap <buffer> <silent> <Esc> :<C-U>call gitgutter#hunk#close_hunk_preview_window()<CR>
|
||||
" Ensure that when the preview window is closed, the map is removed.
|
||||
autocmd User GitGutterPreviewClosed silent! nunmap <buffer> <Esc>
|
||||
autocmd CursorMoved <buffer> ++once silent! nunmap <buffer> <Esc>
|
||||
execute "autocmd WinClosed <buffer=".winbufnr(s:winid)."> doautocmd" s:nomodeline "User GitGutterPreviewClosed"
|
||||
endif
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('*popup_create')
|
||||
if g:gitgutter_close_preview_on_escape
|
||||
let g:gitgutter_floating_window_options.filter = function('s:close_popup_on_escape')
|
||||
endif
|
||||
|
||||
let s:winid = popup_create('', g:gitgutter_floating_window_options)
|
||||
|
||||
call setbufvar(winbufnr(s:winid), '&filetype', 'diff')
|
||||
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
if exists('&previewpopup')
|
||||
let [previewpopup, &previewpopup] = [&previewpopup, '']
|
||||
endif
|
||||
|
||||
" Specifying where to open the preview window can lead to the cursor going
|
||||
" to an unexpected window when the preview window is closed (#769).
|
||||
silent! noautocmd execute g:gitgutter_preview_win_location 'pedit gitgutter://hunk-preview'
|
||||
silent! wincmd P
|
||||
setlocal statusline=%{''}
|
||||
doautocmd WinEnter
|
||||
if exists('*win_getid')
|
||||
let s:winid = win_getid()
|
||||
else
|
||||
let s:preview_bufnr = bufnr('')
|
||||
endif
|
||||
setlocal filetype=diff buftype=acwrite bufhidden=delete
|
||||
" Reset some defaults in case someone else has changed them.
|
||||
setlocal noreadonly modifiable noswapfile
|
||||
if g:gitgutter_close_preview_on_escape
|
||||
" Ensure cursor goes to the expected window.
|
||||
nnoremap <buffer> <silent> <Esc> :<C-U>wincmd p<Bar>pclose<CR>
|
||||
endif
|
||||
|
||||
if exists('&previewpopup')
|
||||
let &previewpopup=previewpopup
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:close_popup_on_escape(winid, key)
|
||||
if a:key == "\<Esc>"
|
||||
call popup_close(a:winid)
|
||||
return 1
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
|
||||
" Floating window: does not care where cursor is.
|
||||
" Preview window: assumes cursor is in preview window.
|
||||
function! s:populate_hunk_preview_window(header, body)
|
||||
if g:gitgutter_preview_win_floating
|
||||
if exists('*nvim_open_win')
|
||||
" Assumes cursor is not in previewing window.
|
||||
call nvim_buf_set_var(winbufnr(s:winid), 'hunk_header', a:header)
|
||||
|
||||
let [_scrolloff, &scrolloff] = [&scrolloff, 0]
|
||||
|
||||
let [width, height] = s:screen_lines(a:body)
|
||||
let height = min([height, g:gitgutter_floating_window_options.height])
|
||||
call nvim_win_set_width(s:winid, width)
|
||||
call nvim_win_set_height(s:winid, height)
|
||||
|
||||
let &scrolloff=_scrolloff
|
||||
|
||||
call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, [])
|
||||
call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, a:body)
|
||||
call nvim_buf_set_option(winbufnr(s:winid), 'modified', v:false)
|
||||
|
||||
let ns_id = nvim_create_namespace('GitGutter')
|
||||
call nvim_buf_clear_namespace(winbufnr(s:winid), ns_id, 0, -1)
|
||||
for region in gitgutter#diff_highlight#process(a:body)
|
||||
let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
|
||||
call nvim_buf_add_highlight(winbufnr(s:winid), ns_id, group, region[0]-1, region[2]-1, region[3])
|
||||
endfor
|
||||
|
||||
call nvim_win_set_cursor(s:winid, [1,0])
|
||||
endif
|
||||
|
||||
if exists('*popup_create')
|
||||
call popup_settext(s:winid, a:body)
|
||||
|
||||
for region in gitgutter#diff_highlight#process(a:body)
|
||||
let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
|
||||
call win_execute(s:winid, "call matchaddpos('".group."', [[".region[0].", ".region[2].", ".(region[3]-region[2]+1)."]])")
|
||||
endfor
|
||||
endif
|
||||
|
||||
else
|
||||
let b:hunk_header = a:header
|
||||
|
||||
%delete _
|
||||
call setline(1, a:body)
|
||||
setlocal nomodified
|
||||
|
||||
let [_, height] = s:screen_lines(a:body)
|
||||
execute 'resize' height
|
||||
1
|
||||
|
||||
call clearmatches()
|
||||
for region in gitgutter#diff_highlight#process(a:body)
|
||||
let group = region[1] == '+' ? 'GitGutterAddIntraLine' : 'GitGutterDeleteIntraLine'
|
||||
call matchaddpos(group, [[region[0], region[2], region[3]-region[2]+1]])
|
||||
endfor
|
||||
|
||||
1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
" Calculates the number of columns and the number of screen lines the given
|
||||
" array of lines will take up, taking account of wrapping.
|
||||
function! s:screen_lines(lines)
|
||||
let [_virtualedit, &virtualedit]=[&virtualedit, 'all']
|
||||
let cursor = getcurpos()
|
||||
normal! g$
|
||||
let available_width = virtcol('.')
|
||||
call setpos('.', cursor)
|
||||
let &virtualedit=_virtualedit
|
||||
let width = min([max(map(copy(a:lines), 'strdisplaywidth(v:val)')), available_width])
|
||||
|
||||
if exists('*reduce')
|
||||
let height = reduce(a:lines, { acc, val -> acc + strdisplaywidth(val) / width + (strdisplaywidth(val) % width == 0 ? 0 : 1) }, 0)
|
||||
else
|
||||
let height = eval(join(map(copy(a:lines), 'strdisplaywidth(v:val) / width + (strdisplaywidth(v:val) % width == 0 ? 0 : 1)'), '+'))
|
||||
endif
|
||||
|
||||
return [width, height]
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:enable_staging_from_hunk_preview_window()
|
||||
augroup gitgutter_hunk_preview
|
||||
autocmd!
|
||||
let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr
|
||||
execute 'autocmd BufWriteCmd <buffer='.bufnr.'> GitGutterStageHunk'
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:goto_original_window()
|
||||
noautocmd wincmd p
|
||||
doautocmd WinEnter
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#hunk#close_hunk_preview_window()
|
||||
let bufnr = s:winid != 0 ? winbufnr(s:winid) : s:preview_bufnr
|
||||
call setbufvar(bufnr, '&modified', 0)
|
||||
|
||||
if g:gitgutter_preview_win_floating
|
||||
if win_id2win(s:winid) > 0
|
||||
execute win_id2win(s:winid).'wincmd c'
|
||||
endif
|
||||
else
|
||||
pclose
|
||||
endif
|
||||
|
||||
let s:winid = 0
|
||||
let s:preview_bufnr = 0
|
||||
endfunction
|
||||
|
||||
|
||||
function gitgutter#hunk#is_preview_window_open()
|
||||
if g:gitgutter_preview_win_floating
|
||||
if win_id2win(s:winid) > 0
|
||||
execute win_id2win(s:winid).'wincmd c'
|
||||
endif
|
||||
else
|
||||
for i in range(1, winnr('$'))
|
||||
if getwinvar(i, '&previewwindow')
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
250
sources_non_forked/vim-gitgutter/autoload/gitgutter/sign.vim
Normal file
250
sources_non_forked/vim-gitgutter/autoload/gitgutter/sign.vim
Normal file
@ -0,0 +1,250 @@
|
||||
" For older Vims without sign_place() the plugin has to manaage the sign ids.
|
||||
let s:first_sign_id = 3000
|
||||
let s:next_sign_id = s:first_sign_id
|
||||
" 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 && !g:gitgutter_highlight_linenrs
|
||||
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 && !g:gitgutter_highlight_linenrs
|
||||
call gitgutter#sign#clear_signs(bufnr(''))
|
||||
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 from the buffer being processed.
|
||||
function! gitgutter#sign#clear_signs(bufnr) abort
|
||||
if exists('*sign_unplace')
|
||||
call sign_unplace('gitgutter', {'buffer': a:bufnr})
|
||||
return
|
||||
endif
|
||||
|
||||
|
||||
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
|
||||
if exists('*sign_unplace')
|
||||
" Vim is (hopefully) now quick enough to remove all signs then place new ones.
|
||||
call sign_unplace('gitgutter', {'buffer': a:bufnr})
|
||||
|
||||
let modified_lines = s:handle_double_hunk(a:modified_lines)
|
||||
let signs = map(copy(modified_lines), '{'.
|
||||
\ '"buffer": a:bufnr,'.
|
||||
\ '"group": "gitgutter",'.
|
||||
\ '"name": s:highlight_name_for_change(v:val[1]),'.
|
||||
\ '"lnum": v:val[0],'.
|
||||
\ '"priority": g:gitgutter_sign_priority'.
|
||||
\ '}')
|
||||
|
||||
if exists('*sign_placelist')
|
||||
call sign_placelist(signs)
|
||||
return
|
||||
endif
|
||||
|
||||
for sign in signs
|
||||
call sign_place(0, sign.group, sign.name, sign.buffer, {'lnum': sign.lnum, 'priority': sign.priority})
|
||||
endfor
|
||||
return
|
||||
endif
|
||||
|
||||
|
||||
" Derive a delta between the current signs and the ones we want.
|
||||
" Remove signs from lines that no longer need a sign.
|
||||
" Upsert the remaining signs.
|
||||
|
||||
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)
|
||||
|
||||
call s:remove_signs(a:bufnr, obsolete_signs, s:remove_all_old_signs)
|
||||
call s:upsert_new_gitgutter_signs(a:bufnr, a:modified_lines)
|
||||
endfunction
|
||||
|
||||
|
||||
"
|
||||
" Internal functions
|
||||
"
|
||||
|
||||
|
||||
function! s:find_current_signs(bufnr) abort
|
||||
let gitgutter_signs = {} " <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>}
|
||||
if !g:gitgutter_sign_allow_clobber
|
||||
let other_signs = [] " [<line_number (number),...]
|
||||
endif
|
||||
|
||||
if exists('*getbufinfo')
|
||||
let bufinfo = getbufinfo(a:bufnr)[0]
|
||||
let signs = has_key(bufinfo, 'signs') ? bufinfo.signs : []
|
||||
else
|
||||
let signs = []
|
||||
|
||||
redir => signlines
|
||||
silent execute "sign place buffer=" . a:bufnr
|
||||
redir END
|
||||
|
||||
for signline in filter(split(signlines, '\n')[2:], 'v:val =~# "="')
|
||||
" Typical sign line before v8.1.0614: line=88 id=1234 name=GitGutterLineAdded
|
||||
" We assume splitting is faster than a regexp.
|
||||
let components = split(signline)
|
||||
call add(signs, {
|
||||
\ 'lnum': str2nr(split(components[0], '=')[1]),
|
||||
\ 'id': str2nr(split(components[1], '=')[1]),
|
||||
\ 'name': split(components[2], '=')[1]
|
||||
\ })
|
||||
endfor
|
||||
endif
|
||||
|
||||
for sign in signs
|
||||
if sign.name =~# 'GitGutter'
|
||||
" 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, sign.lnum)
|
||||
execute "sign unplace" gitgutter_signs[sign.lnum].id
|
||||
endif
|
||||
let gitgutter_signs[sign.lnum] = {'id': sign.id, 'name': sign.name}
|
||||
else
|
||||
if !g:gitgutter_sign_allow_clobber
|
||||
call add(other_signs, sign.lnum)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', gitgutter_signs)
|
||||
if !g:gitgutter_sign_allow_clobber
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'other_signs', other_signs)
|
||||
endif
|
||||
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 && (g:gitgutter_sign_allow_clobber || empty(gitgutter#utility#getbufvar(a:bufnr, 'other_signs')))
|
||||
execute "sign unplace * buffer=" . a:bufnr
|
||||
else
|
||||
for id in a:sign_ids
|
||||
execute "sign unplace" id
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:upsert_new_gitgutter_signs(bufnr, modified_lines) abort
|
||||
if !g:gitgutter_sign_allow_clobber
|
||||
let other_signs = gitgutter#utility#getbufvar(a:bufnr, 'other_signs')
|
||||
endif
|
||||
let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')
|
||||
|
||||
let modified_lines = s:handle_double_hunk(a:modified_lines)
|
||||
|
||||
for line in modified_lines
|
||||
let line_number = line[0] " <number>
|
||||
if g:gitgutter_sign_allow_clobber || 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
|
||||
|
||||
|
||||
" 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.
|
||||
function! s:handle_double_hunk(modified_lines)
|
||||
if a:modified_lines[0:1] == [[1, 'removed_first_line'], [1, 'removed']]
|
||||
return [[1, 'removed_above_and_below']] + a:modified_lines[2:]
|
||||
endif
|
||||
|
||||
return a:modified_lines
|
||||
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
|
||||
|
||||
|
255
sources_non_forked/vim-gitgutter/autoload/gitgutter/utility.vim
Normal file
255
sources_non_forked/vim-gitgutter/autoload/gitgutter/utility.vim
Normal file
@ -0,0 +1,255 @@
|
||||
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 ggvars = getbufvar(buffer, 'gitgutter')
|
||||
if type(ggvars) == type('')
|
||||
unlet ggvars
|
||||
let ggvars = {}
|
||||
call setbufvar(buffer, 'gitgutter', ggvars)
|
||||
endif
|
||||
let ggvars[a:varname] = a:val
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#getbufvar(buffer, varname, ...)
|
||||
let ggvars = getbufvar(a:buffer, 'gitgutter')
|
||||
if type(ggvars) == type({}) && has_key(ggvars, a:varname)
|
||||
return ggvars[a:varname]
|
||||
endif
|
||||
if a:0
|
||||
return a:1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#warn(message) abort
|
||||
echohl WarningMsg
|
||||
echo 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 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 gitgutter#utility#getbufvar(a:bufnr, 'enabled') &&
|
||||
\ !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
|
||||
|
||||
function! gitgutter#utility#has_repo_path(bufnr)
|
||||
return index(['', -1, -2], gitgutter#utility#repo_path(a:bufnr, 0)) == -1
|
||||
endfunction
|
||||
|
||||
" Path of file relative to repo root.
|
||||
"
|
||||
" * empty string - not set
|
||||
" * non-empty string - path
|
||||
" * -1 - pending
|
||||
" * -2 - not tracked by git
|
||||
" * -3 - assume unchanged
|
||||
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
|
||||
|
||||
|
||||
let s:set_path_handler = {}
|
||||
|
||||
function! s:set_path_handler.out(buffer, listing) abort
|
||||
let listing = s:strip_trailing_new_line(a:listing)
|
||||
let [status, path] = [listing[0], listing[2:]]
|
||||
if status =~# '[a-z]'
|
||||
call gitgutter#utility#setbufvar(a:buffer, 'path', -3)
|
||||
else
|
||||
call gitgutter#utility#setbufvar(a:buffer, 'path', path)
|
||||
endif
|
||||
|
||||
if type(self.continuation) == type(function('tr'))
|
||||
call self.continuation()
|
||||
else
|
||||
call call(self.continuation.function, self.continuation.arguments)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:set_path_handler.err(buffer) abort
|
||||
call gitgutter#utility#setbufvar(a:buffer, 'path', -2)
|
||||
endfunction
|
||||
|
||||
|
||||
" continuation - a funcref or hash to call after setting the repo path asynchronously.
|
||||
"
|
||||
" Returns 'async' if the the path is set asynchronously, 0 otherwise.
|
||||
function! gitgutter#utility#set_repo_path(bufnr, continuation) abort
|
||||
" Values of path:
|
||||
" * non-empty string - path
|
||||
" * -1 - pending
|
||||
" * -2 - not tracked by git
|
||||
" * -3 - assume unchanged
|
||||
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'path', -1)
|
||||
let cmd = gitgutter#utility#cd_cmd(a:bufnr,
|
||||
\ g:gitgutter_git_executable.' '.g:gitgutter_git_args.
|
||||
\ ' ls-files -v --error-unmatch --full-name -z -- '.
|
||||
\ gitgutter#utility#shellescape(s:filename(a:bufnr)))
|
||||
|
||||
if g:gitgutter_async && gitgutter#async#available() && !has('vim_starting')
|
||||
let handler = copy(s:set_path_handler)
|
||||
let handler.continuation = a:continuation
|
||||
call gitgutter#async#execute(cmd, a:bufnr, handler)
|
||||
return 'async'
|
||||
endif
|
||||
|
||||
let listing = gitgutter#utility#system(cmd)
|
||||
|
||||
if v:shell_error
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'path', -2)
|
||||
return
|
||||
endif
|
||||
|
||||
let listing = s:strip_trailing_new_line(listing)
|
||||
let [status, path] = [listing[0], listing[2:]]
|
||||
if status =~# '[a-z]'
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'path', -3)
|
||||
else
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'path', path)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#utility#cd_cmd(bufnr, cmd) abort
|
||||
let cd = s:unc_path(a:bufnr) ? 'pushd' : (gitgutter#utility#windows() && s:dos_shell() ? '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:dos_shell()
|
||||
return &shell == 'cmd.exe' || &shell == 'command.com'
|
||||
endfunction
|
||||
|
||||
function! s:use_known_shell() abort
|
||||
if has('unix') && &shell !=# 'sh'
|
||||
let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote]
|
||||
let &shell = 'sh'
|
||||
set shellcmdflag=-c shellredir=>%s\ 2>&1
|
||||
endif
|
||||
if has('win32') && (&shell =~# 'pwsh' || &shell =~# 'powershell')
|
||||
let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote]
|
||||
let &shell = 'cmd.exe'
|
||||
set shellcmdflag=/s\ /c shellredir=>%s\ 2>&1 shellpipe=>%s\ 2>&1 shellquote= shellxquote="
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:restore_shell() abort
|
||||
if (has('unix') || has('win32')) && exists('s:shell')
|
||||
let [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote] = [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#get_diff_base(bufnr)
|
||||
let p = resolve(expand('#'.a:bufnr.':p'))
|
||||
let ml = matchlist(p, '\v^fugitive:/.*/(\x{40,})/')
|
||||
if !empty(ml) && !empty(ml[1])
|
||||
return ml[1].'^'
|
||||
endif
|
||||
return g:gitgutter_diff_base
|
||||
endfunction
|
||||
|
||||
function! s:abs_path(bufnr, shellesc)
|
||||
let p = resolve(expand('#'.a:bufnr.':p'))
|
||||
|
||||
" Remove extra parts from fugitive's filepaths
|
||||
let p = substitute(substitute(p, '^fugitive:', '', ''), '\v\.git/\x{40,}/', '', '')
|
||||
|
||||
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
|
Reference in New Issue
Block a user