vim-signify/autoload/sy/sign.vim
Marco Hinz ee299579c7
Assume at least 7.3.442
That version addded `:doau <nomodeline>`. Soon this plugin will be Vim 8+ only
anyway.

Fixes https://github.com/mhinz/vim-signify/issues/301
2019-08-08 17:12:22 +02:00

309 lines
8.9 KiB
VimL
Raw Permalink 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 = {}
let signlist = sy#util#execute('sign place buffer='. a:sy.buffer)
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 [old_line, new_line, old_count, new_count] = sy#sign#parse_hunk(line)
" Workaround for non-conventional diff output in older Fossil versions:
" https://fossil-scm.org/forum/forumpost/834ce0f1e1
" Fixed as of: https://fossil-scm.org/index.html/info/7fd2a3652ea7368a
if a:vcs == 'fossil' && new_line == 0
let new_line = old_line - 1 - deleted
endif
" 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'))
endwhile
while offset < new_count
let added += 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, '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: #parse_hunk {{{1
" Parse a hunk as '@@ -273,3 +267,14' into [old_line, new_line, old_count, new_count]
function! sy#sign#parse_hunk(diffline) abort
let tokens = matchlist(a:diffline, '^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)')
return [
\ str2nr(tokens[1]),
\ str2nr(tokens[3]),
\ empty(tokens[2]) ? 1 : str2nr(tokens[2]),
\ empty(tokens[4]) ? 1 : str2nr(tokens[4])
\ ]
endfunction
" Function: #set_signs {{{1
function! sy#sign#set_signs(sy, vcs, diff) abort
call sy#verbose('sy#sign#set_signs()', a:vcs)
if a:sy.stats == [-1, -1, -1]
let a:sy.stats = [0, 0, 0]
endif
if empty(a:diff)
call sy#verbose('No changes found.', a:vcs)
let a:sy.stats = [0, 0, 0]
call sy#sign#remove_all_signs(a:sy.buffer)
return
endif
if get(g:, 'signify_line_highlight')
call sy#highlight#line_enable()
else
call sy#highlight#line_disable()
endif
call sy#sign#process_diff(a:sy, a:vcs, a:diff)
if exists('#User#Signify')
doautocmd <nomodeline> User Signify
endif
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