diff --git a/autoload/sy.vim b/autoload/sy.vim index 3d2624a..5f187ef 100644 --- a/autoload/sy.vim +++ b/autoload/sy.vim @@ -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! * ' + execute printf('autocmd! * ', 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 diff --git a/autoload/sy/repo.vim b/autoload/sy/repo.vim index 6f0d363..7d0db09 100644 --- a/autoload/sy/repo.vim +++ b/autoload/sy/repo.vim @@ -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') diff --git a/autoload/sy/sign.vim b/autoload/sy/sign.vim index c9d2d15..76147ff 100644 --- a/autoload/sy/sign.vim +++ b/autoload/sy/sign.vim @@ -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 diff --git a/autoload/sy/util.vim b/autoload/sy/util.vim index 17b7e8e..a6ca6c0 100644 --- a/autoload/sy/util.vim +++ b/autoload/sy/util.vim @@ -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 diff --git a/doc/signify.txt b/doc/signify.txt index 80eca91..baa137b 100644 --- a/doc/signify.txt +++ b/doc/signify.txt @@ -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* > diff --git a/plugin/signify.vim b/plugin/signify.vim index 460683f..ad5366b 100644 --- a/plugin/signify.vim +++ b/plugin/signify.vim @@ -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(':p')) autocmd BufRead,BufWritePost * call sy#start(b:sy_path) - autocmd BufDelete * call sy#stop(expand('')) + 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 (signify-prev-hunk) endif + +" Function: save {{{1 +function! s:save() + if exists('b:sy') && b:sy.active && &modified + write + endif +endfunction