Introduce smarter sign management

This commit is contained in:
Marco Hinz 2014-10-04 15:55:49 +02:00
parent 5baf161ae4
commit fe3bafce11
6 changed files with 252 additions and 264 deletions

View File

@ -3,23 +3,9 @@
scriptencoding utf-8
" Init: values {{{1
let g:signify_sign_overwrite = get(g:, 'signify_sign_overwrite')
if g:signify_sign_overwrite && (v:version < 703 || (v:version == 703 && !has('patch596')))
echohl WarningMsg
echomsg 'signify: Sign overwriting was disabled. See :help signify-option-sign_overwrite'
echohl NONE
let g:signify_sign_overwrite = 0
endif
if !exists('g:signify_skip_filetype')
let g:signify_skip_filetype = { 'help': 1 }
endif
let g:id_top = 0x100
let g:sy_cache = {}
sign define SignifyPlaceholder text=. texthl=SignifySignChange linehl=
" Function: #start {{{1
function! sy#start(path) abort
if g:signify_locked
@ -37,7 +23,14 @@ function! sy#start(path) abort
" new buffer.. add to list of registered files
if !exists('b:sy') || b:sy.path != a:path
let b:sy = { 'path': a:path, 'buffer': bufnr(''), 'active': 0, 'type': 'unknown', 'hunks': [], 'id_top': g:id_top, 'stats': [-1, -1, -1] }
let b:sy = {
\ 'path' : a:path,
\ 'buffer': bufnr(''),
\ 'active': 0,
\ 'type' : 'unknown',
\ 'hunks' : [],
\ 'id_top': g:id_top,
\ 'stats' : [-1, -1, -1] }
if get(g:, 'signify_disable_by_default')
return
endif
@ -78,55 +71,42 @@ function! sy#start(path) abort
" update signs
else
let diff = sy#repo#get_diff_{b:sy.type}()[1]
if empty(diff)
call sy#sign#remove_all(b:sy.buffer)
return
endif
let b:sy.id_top = g:id_top
endif
if get(g:, 'signify_line_highlight')
call sy#highlight#line_enable()
else
call sy#highlight#line_disable()
call sy#highlight#line_enable()
else
call sy#highlight#line_disable()
endif
execute 'sign place 99999 line=1 name=SignifyPlaceholder buffer='. b:sy.buffer
call sy#sign#remove_all(b:sy.buffer)
if !g:signify_sign_overwrite
call sy#sign#get_others()
endif
call sy#repo#process_diff(diff)
sign unplace 99999
call sy#sign#process_diff(diff)
let b:sy.id_top = (g:id_top - 1)
endfunction
" Function: #stop {{{1
function! sy#stop(bnum) abort
let bvars = getbufvar(a:bnum, '')
if empty(bvars) || !has_key(bvars, 'sy')
function! sy#stop() abort
if !exists('b:sy')
return
endif
call sy#sign#remove_all(a:bnum)
call sy#sign#remove_all_signs()
augroup signify
execute 'autocmd! * <buffer='. a:bnum .'>'
execute printf('autocmd! * <buffer=%d>', b:sy.buffer)
augroup END
endfunction
" Function: #toggle {{{1
function! sy#toggle() abort
if !exists('b:sy') || empty(b:sy.path)
if !exists('b:sy')
echomsg 'signify: I cannot sy empty buffers!'
return
endif
if b:sy.active
call sy#stop(b:sy.buffer)
call sy#stop()
let b:sy.active = 0
let b:sy.stats = [-1, -1, -1]
else

View File

@ -3,8 +3,6 @@
scriptencoding utf-8
" Init: values {{{1
let s:sign_delete = get(g:, 'signify_sign_delete', '_')
if !exists('g:signify_diffoptions')
let g:signify_diffoptions = {}
endif
@ -171,148 +169,6 @@ function! sy#repo#get_diff_perforce() abort
return v:shell_error ? [0, ''] : [1, diff]
endfunction
" Function: #process_diff {{{1
function! sy#repo#process_diff(diff) abort
let added = 0
let deleted = 0
let modified = 0
" Determine where we have to put our signs.
for line in filter(split(a:diff, '\n'), 'v:val =~ "^@@ "')
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])
let signs = []
" 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
call add(signs, {
\ 'type': 'SignifyAdd',
\ 'lnum': new_line + offset })
let offset += 1
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)
let deleted += old_count
if new_line == 0
call add(signs, {
\ 'type': 'SignifyRemoveFirstLine',
\ 'lnum': 1 })
elseif old_count <= 99
call add(signs, {
\ 'type': 'SignifyDelete'. old_count,
\ 'text': substitute(s:sign_delete . old_count, '.*\ze..$', '', ''),
\ 'lnum': new_line })
else
call add(signs, {
\ 'type': 'SignifyDeleteMore',
\ 'lnum': new_line,
\ 'text': 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
call add(signs, {
\ 'type': 'SignifyChange',
\ 'lnum': new_line + offset })
let offset += 1
endwhile
else
" 2 lines changed; 2 lines deleted:
" @@ -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)
call add(signs, {
\ 'type': 'SignifyChange',
\ 'lnum': new_line + offset })
let offset += 1
endwhile
call add(signs, {
\ 'type': (removed > 9) ? 'SignifyChangeDeleteMore' : 'SignifyChangeDelete'. removed,
\ 'lnum': new_line })
" 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 added += (new_count - old_count)
let offset = 0
while offset < old_count
call add(signs, {
\ 'type': 'SignifyChange',
\ 'lnum': new_line + offset })
let offset += 1
endwhile
while offset < new_count
call add(signs, {
\ 'type': 'SignifyAdd',
\ 'lnum': new_line + offset })
let offset += 1
endwhile
endif
endif
call sy#sign#set(signs)
endfor
let b:sy.stats = [added, modified, deleted]
endfunction
" Function: #get_stats {{{1
function! sy#repo#get_stats() abort
if !exists('b:sy') || !has_key(b:sy, 'stats')

View File

@ -3,71 +3,237 @@
scriptencoding utf-8
" Init: values {{{1
let s:sign_delete = get(g:, 'signify_sign_delete', '_')
let s:delete_highlight = ['', 'SignifyLineDelete']
" Function: #get_others {{{1
function! sy#sign#get_others() abort
let s:other_signs_line_numbers = {}
" Function: #get_next_id {{{1
function! sy#sign#get_next_id() abort
let tmp = g:id_top
let g:id_top += 1
return tmp
endfunction
" Function: #get_current_signs {{{1
function! sy#sign#get_current_signs() abort
let b:sy.internal = {}
let b:sy.external = {}
let lang = v:lang
silent! execute 'language message C'
redir => signlist
silent! execute 'sign place buffer='. b:sy.buffer
redir END
let lines = filter(split(signlist, '\n'), 'v:val =~ "^\\s\\+line"')
if lines[0] =~ 99999
call remove(lines, 0)
endif
for line in lines
let lnum = matchlist(line, '\v^\s+line\=(\d+)')[1]
let s:other_signs_line_numbers[lnum] = 1
endfor
silent! execute 'language message' lang
for signline in split(signlist, '\n')[2:]
let tokens = matchlist(signline, '\v^\s+line\=(\d+)\s+id\=(\d+)\s+name\=(.*)$')
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(b:sy.internal, line)
execute 'sign unplace' b:sy.internal[line].id
endif
let b:sy.internal[line] = { 'type': type, 'id': id }
else
let b:sy.external[line] = id
endif
endfor
endfunction
" Function: #set {{{1
function! sy#sign#set(signs)
let hunk = { 'ids': [], 'start': a:signs[0].lnum, 'end': a:signs[-1].lnum }
for sign in a:signs
" Preserve non-signify signs
if !g:signify_sign_overwrite && has_key(s:other_signs_line_numbers, sign.lnum)
continue
endif
" Function: #process_diff {{{1
function! sy#sign#process_diff(diff) abort
let b:sy.signtable = {}
let b:sy.hunks = []
let [added, modified, deleted] = [0, 0, 0]
call add(hunk.ids, g:id_top)
if sign.type =~# 'SignifyDelete'
execute 'sign define '. sign.type .' text='. sign.text .' texthl=SignifySignDelete linehl='. s:delete_highlight[g:signify_line_highlight]
execute 'sign place' g:id_top 'line='. sign.lnum 'name='. sign.type 'buffer='. b:sy.buffer
call sy#sign#get_current_signs()
" Determine where we have to put our signs.
for line in filter(split(a:diff, '\n'), 'v:val =~ "^@@ "')
let b: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(line) | continue | endif
call add(ids, s:add_sign(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(new_line) | continue | endif
let deleted += old_count
if new_line == 0
call add(ids, s:add_sign(1, 'SignifyRemoveFirstLine'))
elseif old_count <= 99
call add(ids, s:add_sign(new_line, 'SignifyDelete'. old_count, substitute(s:sign_delete . old_count, '.*\ze..$', '', '')))
else
call add(ids, s:add_sign(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(line) | continue | endif
call add(ids, s:add_sign(line, 'SignifyChange'))
endwhile
else
execute 'sign place' g:id_top 'line='. sign.lnum 'name='. sign.type 'buffer='. b:sy.buffer
" 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(line) | continue | endif
call add(ids, s:add_sign(line, 'SignifyChange'))
endwhile
let line = new_line + offset
if s:external_sign_present(line) | continue | endif
call add(ids, s:add_sign(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(line) | continue | endif
call add(ids, s:add_sign(line, 'SignifyChange'))
let added += 1
endwhile
while offset < new_count
let line = new_line + offset
let offset += 1
if s:external_sign_present(line) | continue | endif
call add(ids, s:add_sign(line, 'SignifyAdd'))
endwhile
endif
endif
let g:id_top += 1
if !empty(ids)
call add(b:sy.hunks, {
\ 'ids' : ids,
\ 'start': b:sy.lines[0],
\ 'end' : b:sy.lines[-1] })
endif
endfor
call add(b:sy.hunks, hunk)
" Remove obsoleted signs.
for line in filter(keys(b:sy.internal), '!has_key(b:sy.signtable, v:val)')
execute 'sign unplace' b:sy.internal[line].id
endfor
let b:sy.stats = [added, modified, deleted]
endfunction
" Function: #remove_all {{{1
function! sy#sign#remove_all(bnum) abort
let sy = getbufvar(a:bnum, 'sy')
function! s:add_sign(line, type, ...) abort
call add(b:sy.lines, a:line)
let b:sy.signtable[a:line] = 1
if g:signify_sign_overwrite
execute 'sign unplace * buffer='. sy.buffer
else
for hunk in sy.hunks
for id in hunk.ids
execute 'sign unplace' id
endfor
endfor
if has_key(b:sy.internal, a:line)
" There is a sign on this line already.
if a:type == b:sy.internal[a:line].type
" Keep current sign since the new one is of the same type.
return b:sy.internal[a:line].id
else
" Update sign by overwriting the ID of the current sign.
let id = b:sy.internal[a:line].id
endif
endif
let sy.hunks = []
let sy.stats = [0, 0, 0]
if !exists('id')
let id = sy#sign#get_next_id()
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,
\ b:sy.buffer)
return id
endfunction
function! s:external_sign_present(line) abort
if has_key(b:sy.external, a:line)
if has_key(b:sy.internal, a:line)
" Remove Sy signs from lines with other signs.
execute 'sign unplace' b:sy.internal[a:line].id
endif
return 1
endif
endfunction
" Function: #remove_all_signs {{{1
function! sy#sign#remove_all_signs() abort
for hunk in b:sy.hunks
for id in hunk.ids
execute 'sign unplace' id
endfor
endfor
let b:sy.hunks = []
let b:sy.stats = [0, 0, 0]
endfunction

View File

@ -30,12 +30,14 @@ endfunction
" Function: #run_in_dir {{{1
function! sy#util#run_in_dir(dir, cmd) abort
let chdir = haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
let cwd = getcwd()
try
exe chdir .' '. fnameescape(fnamemodify(a:dir, ':p'))
let resp = system(a:cmd)
execute chdir fnameescape(fnamemodify(a:dir, ':p'))
let ret = system(a:cmd)
finally
exe chdir .' '. fnameescape(cwd)
execute chdir fnameescape(cwd)
endtry
return resp
return ret
endfunction

View File

@ -94,7 +94,6 @@ All available options:~
|g:signify_mapping_toggle|
|g:signify_skip_filetype|
|g:signify_skip_filename|
|g:signify_sign_overwrite|
|g:signify_update_on_bufenter|
|g:signify_update_on_focusgained|
|g:signify_line_highlight|
@ -194,21 +193,6 @@ NOTE: Filenames have to be absolute paths.
Default: Both are empty.
------------------------------------------------------------------------------
*g:signify_sign_overwrite*
>
let g:signify_sign_overwrite = 0
<
Setting this to 1 means that Sy doesn't have to manage its signs and just
removes ALL signs before adding its own signs. This typically results
in a tad more performance, especially when a lot of signs are in use.
If you need plugin compatibility though, you should set this to 0. In this
case Sy will remember which signs it set last and only removes those at the
next update.
NOTE: This feature needs at least Vim 7.3.596!
------------------------------------------------------------------------------
*g:signify_update_on_bufenter*
>

View File

@ -2,10 +2,11 @@
scriptencoding utf-8
if exists('g:loaded_signify') || !has('signs') || &cp
if exists('g:loaded_signify') || !has('signs') || &compatible
finish
endif
" Init: values {{{1
let g:loaded_signify = 1
let g:signify_locked = 0
@ -16,34 +17,26 @@ augroup signify
autocmd VimEnter * call sy#highlight#setup()
autocmd BufRead,BufEnter,SessionLoadPost * let b:sy_path = resolve(expand('<afile>:p'))
autocmd BufRead,BufWritePost * call sy#start(b:sy_path)
autocmd BufDelete * call sy#stop(expand('<abuf>'))
autocmd BufDelete * call sy#stop()
autocmd QuickFixCmdPre *vimgrep* let g:signify_locked = 1
autocmd QuickFixCmdPost *vimgrep* let g:signify_locked = 0
if get(g:, 'signify_update_on_bufenter')
autocmd BufEnter * nested
\ if exists('b:sy') && b:sy.active && &modified |
\ write |
\ endif
autocmd BufEnter * nested call s:save()
endif
if get(g:, 'signify_cursorhold_normal')
autocmd CursorHold * nested
\ if exists('b:sy') && b:sy.active && &modified |
\ write |
\ endif
autocmd CursorHold * nested call s:save()
endif
if get(g:, 'signify_cursorhold_insert')
autocmd CursorHoldI * nested
\ if exists('b:sy') && b:sy.active && &modified |
\ write |
\ endif
autocmd CursorHoldI * nested call s:save()
endif
if get(g:, 'signify_update_on_focusgained') && !has('gui_win32')
autocmd FocusGained * if exists('b:sy') | call sy#start(b:sy.path) | endif
autocmd FocusGained *
\ if exists('b:sy') |
\ call sy#start(b:sy.path) |
\ endif
endif
augroup END
@ -89,3 +82,10 @@ endif
if empty(maparg('[c', 'n'))
nmap [c <plug>(signify-prev-hunk)
endif
" Function: save {{{1
function! s:save()
if exists('b:sy') && b:sy.active && &modified
write
endif
endfunction