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 scriptencoding utf-8
" Init: values {{{1 " 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:id_top = 0x100
let g:sy_cache = {} let g:sy_cache = {}
sign define SignifyPlaceholder text=. texthl=SignifySignChange linehl=
" Function: #start {{{1 " Function: #start {{{1
function! sy#start(path) abort function! sy#start(path) abort
if g:signify_locked if g:signify_locked
@ -37,7 +23,14 @@ function! sy#start(path) abort
" new buffer.. add to list of registered files " new buffer.. add to list of registered files
if !exists('b:sy') || b:sy.path != a:path 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') if get(g:, 'signify_disable_by_default')
return return
endif endif
@ -78,10 +71,6 @@ function! sy#start(path) abort
" update signs " update signs
else else
let diff = sy#repo#get_diff_{b:sy.type}()[1] 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 let b:sy.id_top = g:id_top
endif endif
@ -91,42 +80,33 @@ function! sy#start(path) abort
call sy#highlight#line_disable() call sy#highlight#line_disable()
endif endif
execute 'sign place 99999 line=1 name=SignifyPlaceholder buffer='. b:sy.buffer call sy#sign#process_diff(diff)
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
let b:sy.id_top = (g:id_top - 1) let b:sy.id_top = (g:id_top - 1)
endfunction endfunction
" Function: #stop {{{1 " Function: #stop {{{1
function! sy#stop(bnum) abort function! sy#stop() abort
let bvars = getbufvar(a:bnum, '') if !exists('b:sy')
if empty(bvars) || !has_key(bvars, 'sy')
return return
endif endif
call sy#sign#remove_all(a:bnum) call sy#sign#remove_all_signs()
augroup signify augroup signify
execute 'autocmd! * <buffer='. a:bnum .'>' execute printf('autocmd! * <buffer=%d>', b:sy.buffer)
augroup END augroup END
endfunction endfunction
" Function: #toggle {{{1 " Function: #toggle {{{1
function! sy#toggle() abort function! sy#toggle() abort
if !exists('b:sy') || empty(b:sy.path) if !exists('b:sy')
echomsg 'signify: I cannot sy empty buffers!' echomsg 'signify: I cannot sy empty buffers!'
return return
endif endif
if b:sy.active if b:sy.active
call sy#stop(b:sy.buffer) call sy#stop()
let b:sy.active = 0 let b:sy.active = 0
let b:sy.stats = [-1, -1, -1] let b:sy.stats = [-1, -1, -1]
else else

View File

@ -3,8 +3,6 @@
scriptencoding utf-8 scriptencoding utf-8
" Init: values {{{1 " Init: values {{{1
let s:sign_delete = get(g:, 'signify_sign_delete', '_')
if !exists('g:signify_diffoptions') if !exists('g:signify_diffoptions')
let g:signify_diffoptions = {} let g:signify_diffoptions = {}
endif endif
@ -171,148 +169,6 @@ function! sy#repo#get_diff_perforce() abort
return v:shell_error ? [0, ''] : [1, diff] return v:shell_error ? [0, ''] : [1, diff]
endfunction 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: #get_stats {{{1
function! sy#repo#get_stats() abort function! sy#repo#get_stats() abort
if !exists('b:sy') || !has_key(b:sy, 'stats') if !exists('b:sy') || !has_key(b:sy, 'stats')

View File

@ -3,71 +3,237 @@
scriptencoding utf-8 scriptencoding utf-8
" Init: values {{{1 " Init: values {{{1
let s:sign_delete = get(g:, 'signify_sign_delete', '_')
let s:delete_highlight = ['', 'SignifyLineDelete'] let s:delete_highlight = ['', 'SignifyLineDelete']
" Function: #get_others {{{1 " Function: #get_next_id {{{1
function! sy#sign#get_others() abort function! sy#sign#get_next_id() abort
let s:other_signs_line_numbers = {} 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 let lang = v:lang
silent! execute 'language message C' silent! execute 'language message C'
redir => signlist redir => signlist
silent! execute 'sign place buffer='. b:sy.buffer silent! execute 'sign place buffer='. b:sy.buffer
redir END 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 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 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 " Function: #process_diff {{{1
" Preserve non-signify signs function! sy#sign#process_diff(diff) abort
if !g:signify_sign_overwrite && has_key(s:other_signs_line_numbers, sign.lnum) let b:sy.signtable = {}
continue let b:sy.hunks = []
endif let [added, modified, deleted] = [0, 0, 0]
call add(hunk.ids, g:id_top) call sy#sign#get_current_signs()
if sign.type =~# 'SignifyDelete'
execute 'sign define '. sign.type .' text='. sign.text .' texthl=SignifySignDelete linehl='. s:delete_highlight[g:signify_line_highlight] " Determine where we have to put our signs.
execute 'sign place' g:id_top 'line='. sign.lnum 'name='. sign.type 'buffer='. b:sy.buffer 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 else
execute 'sign place' g:id_top 'line='. sign.lnum 'name='. sign.type 'buffer='. b:sy.buffer call add(ids, s:add_sign(new_line, 'SignifyDeleteMore', s:sign_delete .'>'))
endif endif
let g:id_top += 1 " 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
" 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
if !empty(ids)
call add(b:sy.hunks, {
\ 'ids' : ids,
\ 'start': b:sy.lines[0],
\ 'end' : b:sy.lines[-1] })
endif
endfor 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 endfunction
" Function: #remove_all {{{1 function! s:add_sign(line, type, ...) abort
function! sy#sign#remove_all(bnum) abort call add(b:sy.lines, a:line)
let sy = getbufvar(a:bnum, 'sy') let b:sy.signtable[a:line] = 1
if g:signify_sign_overwrite if has_key(b:sy.internal, a:line)
execute 'sign unplace * buffer='. sy.buffer " 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 else
for hunk in sy.hunks " Update sign by overwriting the ID of the current sign.
let id = b:sy.internal[a:line].id
endif
endif
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 for id in hunk.ids
execute 'sign unplace' id execute 'sign unplace' id
endfor endfor
endfor endfor
endif
let sy.hunks = [] let b:sy.hunks = []
let sy.stats = [0, 0, 0] let b:sy.stats = [0, 0, 0]
endfunction endfunction

View File

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

View File

@ -94,7 +94,6 @@ All available options:~
|g:signify_mapping_toggle| |g:signify_mapping_toggle|
|g:signify_skip_filetype| |g:signify_skip_filetype|
|g:signify_skip_filename| |g:signify_skip_filename|
|g:signify_sign_overwrite|
|g:signify_update_on_bufenter| |g:signify_update_on_bufenter|
|g:signify_update_on_focusgained| |g:signify_update_on_focusgained|
|g:signify_line_highlight| |g:signify_line_highlight|
@ -194,21 +193,6 @@ NOTE: Filenames have to be absolute paths.
Default: Both are empty. 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* *g:signify_update_on_bufenter*
> >

View File

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