vim-signify/autoload/sy/sign.vim
Marco Hinz 8351e5212f
Improve detection of nested repos managed by different VCS
Assume a hg repo below a git repo. `git diff` on a file managed by hg, will
return a successful exit value, but no output.

So, if we got a successful exit value from multiple VCS tools, and none of them
gave any output, keep them all as potential candidates. The next time Sy is run,
only these candidates will be tested again.

If one of them returns a proper diff, set b:sy.updated_by to that VCS and prune
all other candidates.

References #235
2018-04-17 17:35:08 +02:00

271 lines
7.8 KiB
VimL
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

" vim: et sw=2 sts=2
scriptencoding utf-8
" Init: values {{{1
if get(g:, 'signify_sign_show_text', 1)
let s:sign_delete = get(g:, 'signify_sign_delete', '_')
else
let s:sign_delete = ' '
endif
let s:sign_show_count = get(g:, 'signify_sign_show_count', 1)
let s:delete_highlight = ['', 'SignifyLineDelete']
" Function: #id_next {{{1
function! sy#sign#id_next(sy) abort
let id = a:sy.signid
let a:sy.signid += 1
return id
endfunction
" Function: #get_current_signs {{{1
function! sy#sign#get_current_signs(sy) abort
let a:sy.internal = {}
let a:sy.external = {}
redir => signlist
silent! execute 'sign place buffer='. a:sy.buffer
redir END
for signline in split(signlist, '\n')[2:]
let tokens = matchlist(signline, '\v^\s+\S+\=(\d+)\s+\S+\=(\d+)\s+\S+\=(.*)$')
let line = str2nr(tokens[1])
let id = str2nr(tokens[2])
let type = tokens[3]
if type =~# '^Signify'
" Handle ambiguous signs. Assume you have signs on line 3 and 4.
" Removing line 3 would lead to the second sign to be shifted up
" to line 3. Now there are still 2 signs, both one line 3.
if has_key(a:sy.internal, line)
execute 'sign unplace' a:sy.internal[line].id 'buffer='.a:sy.buffer
endif
let a:sy.internal[line] = { 'type': type, 'id': id }
else
let a:sy.external[line] = id
endif
endfor
endfunction
" Function: #process_diff {{{1
function! sy#sign#process_diff(sy, vcs, diff) abort
let a:sy.signtable = {}
let a:sy.hunks = []
let [added, modified, deleted] = [0, 0, 0]
call sy#sign#get_current_signs(a:sy)
" Determine where we have to put our signs.
for line in filter(a:diff, 'v:val =~ "^@@ "')
let a:sy.lines = []
let ids = []
let tokens = matchlist(line, '^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)')
let old_line = str2nr(tokens[1])
let new_line = str2nr(tokens[3])
let old_count = empty(tokens[2]) ? 1 : str2nr(tokens[2])
let new_count = empty(tokens[4]) ? 1 : str2nr(tokens[4])
" 2 lines added:
" @@ -5,0 +6,2 @@ this is line 5
" +this is line 5
" +this is line 5
if (old_count == 0) && (new_count >= 1)
let added += new_count
let offset = 0
while offset < new_count
let line = new_line + offset
let offset += 1
if s:external_sign_present(a:sy, line) | continue | endif
call add(ids, s:add_sign(a:sy, line, 'SignifyAdd'))
endwhile
" 2 lines removed:
" @@ -6,2 +5,0 @@ this is line 5
" -this is line 6
" -this is line 7
elseif (old_count >= 1) && (new_count == 0)
if s:external_sign_present(a:sy, new_line) | continue | endif
let deleted += old_count
if new_line == 0
call add(ids, s:add_sign(a:sy, 1, 'SignifyRemoveFirstLine'))
elseif s:sign_show_count
let text = s:sign_delete . (old_count <= 99 ? old_count : '>')
while strwidth(text) > 2
let text = substitute(text, '.', '', '')
endwhile
call add(ids, s:add_sign(a:sy, new_line, 'SignifyDelete'. old_count, text))
else
call add(ids, s:add_sign(a:sy, new_line, 'SignifyDeleteMore', s:sign_delete))
endif
" 2 lines changed:
" @@ -5,2 +5,2 @@ this is line 4
" -this is line 5
" -this is line 6
" +this os line 5
" +this os line 6
elseif old_count == new_count
let modified += old_count
let offset = 0
while offset < new_count
let line = new_line + offset
let offset += 1
if s:external_sign_present(a:sy, line) | continue | endif
call add(ids, s:add_sign(a:sy, line, 'SignifyChange'))
endwhile
else
" 2 lines changed; 2 lines removed:
" @@ -5,4 +5,2 @@ this is line 4
" -this is line 5
" -this is line 6
" -this is line 7
" -this is line 8
" +this os line 5
" +this os line 6
if old_count > new_count
let modified += new_count
let removed = old_count - new_count
let deleted += removed
let offset = 0
while offset < new_count - 1
let line = new_line + offset
let offset += 1
if s:external_sign_present(a:sy, line) | continue | endif
call add(ids, s:add_sign(a:sy, line, 'SignifyChange'))
endwhile
let line = new_line + offset
if s:external_sign_present(a:sy, line) | continue | endif
call add(ids, s:add_sign(a:sy, line, (removed > 9)
\ ? 'SignifyChangeDeleteMore'
\ : 'SignifyChangeDelete'. removed))
" lines changed and added:
" @@ -5 +5,3 @@ this is line 4
" -this is line 5
" +this os line 5
" +this is line 42
" +this is line 666
else
let modified += old_count
let offset = 0
while offset < old_count
let line = new_line + offset
let offset += 1
if s:external_sign_present(a:sy, line) | continue | endif
call add(ids, s:add_sign(a:sy, line, 'SignifyChange'))
let added += 1
endwhile
while offset < new_count
let line = new_line + offset
let offset += 1
if s:external_sign_present(a:sy, line) | continue | endif
call add(ids, s:add_sign(a:sy, line, 'SignifyAdd'))
endwhile
endif
endif
if !empty(ids)
call add(a:sy.hunks, {
\ 'ids' : ids,
\ 'start': a:sy.lines[0],
\ 'end' : a:sy.lines[-1] })
endif
endfor
" Remove obsoleted signs.
for line in filter(keys(a:sy.internal), '!has_key(a:sy.signtable, v:val)')
execute 'sign unplace' a:sy.internal[line].id 'buffer='.a:sy.buffer
endfor
if has('gui_macvim') && has('gui_running') && mode() == 'n'
" MacVim needs an extra kick in the butt, when setting signs from the
" exit handler. :redraw would trigger a "hanging cursor" issue.
call feedkeys("\<c-l>", 'n')
endif
if empty(a:sy.updated_by) && empty(a:sy.hunks)
call sy#verbose('Successful exit value, but no diff. Keep VCS for time being.', a:vcs)
return
endif
call sy#verbose('Signs updated.', a:vcs)
let a:sy.updated_by = a:vcs
if len(a:sy.vcs) > 1
call sy#verbose('Disable all other VCS.', a:vcs)
let a:sy.vcs = [a:vcs]
endif
let a:sy.stats = [added, modified, deleted]
endfunction
" Function: #remove_all_signs {{{1
function! sy#sign#remove_all_signs(bufnr) abort
let sy = getbufvar(a:bufnr, 'sy')
for hunk in sy.hunks
for id in hunk.ids
execute 'sign unplace' id 'buffer='.a:bufnr
endfor
endfor
let sy.hunks = []
endfunction
" Function: s:add_sign {{{1
function! s:add_sign(sy, line, type, ...) abort
call add(a:sy.lines, a:line)
let a:sy.signtable[a:line] = 1
if has_key(a:sy.internal, a:line)
" There is a sign on this line already.
if a:type == a:sy.internal[a:line].type
" Keep current sign since the new one is of the same type.
return a:sy.internal[a:line].id
else
" Update sign by overwriting the ID of the current sign.
let id = a:sy.internal[a:line].id
endif
endif
if !exists('id')
let id = sy#sign#id_next(a:sy)
endif
if a:type =~# 'SignifyDelete'
execute printf('sign define %s text=%s texthl=SignifySignDelete linehl=%s',
\ a:type,
\ a:1,
\ s:delete_highlight[g:signify_line_highlight])
endif
execute printf('sign place %d line=%d name=%s buffer=%s',
\ id,
\ a:line,
\ a:type,
\ a:sy.buffer)
return id
endfunction
" Function: s:external_sign_present {{{1
function! s:external_sign_present(sy, line) abort
if has_key(a:sy.external, a:line)
if has_key(a:sy.internal, a:line)
" Remove Sy signs from lines with other signs.
execute 'sign unplace' a:sy.internal[a:line].id 'buffer='.a:sy.buffer
endif
return 1
endif
endfunction