summaryrefslogtreecommitdiff
path: root/start/signify/autoload
diff options
context:
space:
mode:
Diffstat (limited to 'start/signify/autoload')
-rw-r--r--start/signify/autoload/sy.vim199
-rw-r--r--start/signify/autoload/sy/debug.vim48
-rw-r--r--start/signify/autoload/sy/fold.vim125
-rw-r--r--start/signify/autoload/sy/highlight.vim94
-rw-r--r--start/signify/autoload/sy/jump.vim29
-rw-r--r--start/signify/autoload/sy/repo.vim512
-rw-r--r--start/signify/autoload/sy/sign.vim275
-rw-r--r--start/signify/autoload/sy/util.vim109
8 files changed, 1391 insertions, 0 deletions
diff --git a/start/signify/autoload/sy.vim b/start/signify/autoload/sy.vim
new file mode 100644
index 0000000..b994aff
--- /dev/null
+++ b/start/signify/autoload/sy.vim
@@ -0,0 +1,199 @@
+" vim: et sw=2 sts=2
+
+scriptencoding utf-8
+
+" Init: values {{{1
+let s:has_doau_modeline = v:version > 703 || v:version == 703 && has('patch442')
+
+" Function: #start {{{1
+function! sy#start() abort
+ if g:signify_locked
+ call sy#verbose('Locked.')
+ return
+ endif
+
+ let sy_path = resolve(expand('%:p'))
+ if has('win32')
+ let sy_path = substitute(sy_path, '\v^(\w):\\\\', '\1:\\', '')
+ endif
+
+ if s:skip(sy_path)
+ call sy#verbose('Skip file: '. sy_path)
+ if exists('b:sy')
+ call sy#sign#remove_all_signs(bufnr(''))
+ unlet! b:sy
+ endif
+ return
+ endif
+
+ if !exists('b:sy') || b:sy.path != sy_path
+ call sy#verbose('Register new file: '. sy_path)
+ let b:sy = {
+ \ 'path': sy_path,
+ \ 'buffer': bufnr(''),
+ \ 'active': 0,
+ \ 'detecting': 0,
+ \ 'vcs': [],
+ \ 'hunks': [],
+ \ 'signid': 0x100,
+ \ 'updated_by': '',
+ \ 'stats': [-1, -1, -1],
+ \ 'info': {
+ \ 'dir': fnamemodify(sy_path, ':p:h'),
+ \ 'path': sy#util#escape(sy_path),
+ \ 'file': sy#util#escape(fnamemodify(sy_path, ':t'))
+ \ }}
+ if get(g:, 'signify_disable_by_default')
+ call sy#verbose('Disabled by default.')
+ return
+ endif
+ let b:sy.active = 1
+ call sy#repo#detect()
+ elseif has('vim_starting')
+ call sy#verbose("Don't run Sy more than once during startup.")
+ return
+ elseif !b:sy.active
+ call sy#verbose('Inactive buffer.')
+ return
+ elseif empty(b:sy.vcs)
+ if get(b:sy, 'retry')
+ let b:sy.retry = 0
+ call sy#verbose('Redetecting VCS.')
+ call sy#repo#detect()
+ else
+ if get(b:sy, 'detecting')
+ call sy#verbose('Detection is already in progress.')
+ else
+ call sy#verbose('No VCS found. Disabling.')
+ call sy#disable()
+ endif
+ endif
+ else
+ for vcs in b:sy.vcs
+ let job_id = get(b:, 'sy_job_id_'. vcs)
+ if type(job_id) != type(0) || job_id > 0
+ call sy#verbose('Update is already in progress.', vcs)
+ else
+ call sy#verbose('Updating signs.', vcs)
+ call sy#repo#get_diff_start(vcs)
+ endif
+ endfor
+ endif
+endfunction
+
+" Function: #set_signs {{{1
+function! sy#set_signs(sy, vcs, diff) abort
+ call sy#verbose('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')
+ execute 'doautocmd' (s:has_doau_modeline ? '<nomodeline>' : '') 'User Signify'
+ endif
+endfunction
+
+" Function: #stop {{{1
+function! sy#stop(bufnr) abort
+ let sy = getbufvar(a:bufnr, 'sy')
+ if empty(sy)
+ return
+ endif
+
+ call sy#sign#remove_all_signs(a:bufnr)
+endfunction
+
+" Function: #enable {{{1
+function! sy#enable() abort
+ if !exists('b:sy')
+ call sy#start()
+ return
+ endif
+
+ if !b:sy.active
+ let b:sy.active = 1
+ let b:sy.retry = 1
+ call sy#start()
+ endif
+endfunction
+
+" Function: #disable {{{1
+function! sy#disable() abort
+ if exists('b:sy') && b:sy.active
+ call sy#stop(b:sy.buffer)
+ let b:sy.active = 0
+ let b:sy.stats = [-1, -1, -1]
+ endif
+endfunction
+
+" Function: #toggle {{{1
+function! sy#toggle() abort
+ if !exists('b:sy') || !b:sy.active
+ call sy#enable()
+ else
+ call sy#disable()
+ endif
+endfunction
+
+" Function: #buffer_is_active {{{1
+function! sy#buffer_is_active()
+ return exists('b:sy') && b:sy.active
+endfunction
+
+" Function: #verbose {{{1
+function! sy#verbose(msg, ...) abort
+ if &verbose
+ if type(a:msg) == type([])
+ for msg in a:msg
+ echomsg printf('[sy%s] %s', (a:0 ? ':'.a:1 : ''), msg)
+ endfor
+ else
+ echomsg printf('[sy%s] %s', (a:0 ? ':'.a:1 : ''), a:msg)
+ endif
+ endif
+endfunction
+
+" Function: s:skip {{{1
+function! s:skip(path)
+ if &diff || !filereadable(a:path)
+ return 1
+ endif
+
+ if exists('g:signify_skip_filetype')
+ if has_key(g:signify_skip_filetype, &filetype)
+ return 1
+ elseif has_key(g:signify_skip_filetype, 'help') && (&buftype == 'help')
+ return 1
+ endif
+ endif
+
+ if exists('g:signify_skip_filename') && has_key(g:signify_skip_filename, a:path)
+ return 1
+ endif
+
+ if exists('g:signify_skip_filename_pattern')
+ for pattern in g:signify_skip_filename_pattern
+ if a:path =~ pattern
+ return 1
+ endif
+ endfor
+ endif
+
+ return 0
+endfunction
diff --git a/start/signify/autoload/sy/debug.vim b/start/signify/autoload/sy/debug.vim
new file mode 100644
index 0000000..5beafd5
--- /dev/null
+++ b/start/signify/autoload/sy/debug.vim
@@ -0,0 +1,48 @@
+" vim: et sw=2 sts=2
+
+scriptencoding utf-8
+
+" Function: #list_active_buffers {{{1
+function! sy#debug#list_active_buffers() abort
+ for b in range(1, bufnr('$'))
+ if !buflisted(b) || empty(getbufvar(b, 'sy'))
+ continue
+ endif
+
+ let sy = copy(getbufvar(b, 'sy'))
+ let path = remove(sy, 'path')
+
+ echo "\n". path ."\n". repeat('=', strlen(path))
+
+ for k in ['active', 'buffer', 'vcs', 'stats', 'signid']
+ if k == 'stats'
+ echo printf("%10s = %d added, %d changed, %d removed\n",
+ \ k,
+ \ sy.stats[0],
+ \ sy.stats[1],
+ \ sy.stats[2])
+ else
+ echo printf("%10s = %s\n", k, string(sy[k]))
+ endif
+ endfor
+
+ if empty(sy.hunks)
+ echo printf("%10s = %s\n", 'hunks', '[]')
+ else
+ for i in range(len(sy.hunks))
+ if i == 0
+ echo printf("%10s = start: %d, end: %d, IDs: %s\n",
+ \ 'hunks',
+ \ sy.hunks[i].start,
+ \ sy.hunks[i].end,
+ \ string(sy.hunks[i].ids))
+ else
+ echo printf("%20s: %d, %s: %d, %s: %s\n",
+ \ 'start', sy.hunks[i].start,
+ \ 'end', sy.hunks[i].end,
+ \ 'IDs', string(sy.hunks[i].ids))
+ endif
+ endfor
+ endif
+ endfor
+endfunction
diff --git a/start/signify/autoload/sy/fold.vim b/start/signify/autoload/sy/fold.vim
new file mode 100644
index 0000000..e3afb97
--- /dev/null
+++ b/start/signify/autoload/sy/fold.vim
@@ -0,0 +1,125 @@
+" vim: et sw=2 sts=2
+
+" Function: SignifyFoldExpr {{{1
+function! SignifyFoldExpr(lnum)
+ return s:levels[a:lnum]
+endfunction
+
+" Function: SignifyFoldText {{{1
+function! SignifyFoldText()
+ let linelen = &textwidth ? &textwidth : 80
+ let marker = &foldmarker[:stridx(&foldmarker, ',')-1]
+ let range = foldclosedend(v:foldstart) - foldclosed(v:foldstart) + 1
+
+ let left = substitute(getline(v:foldstart), marker, '', '')
+ let leftlen = len(left)
+
+ let right = printf('%d [%d]', range, v:foldlevel)
+ let rightlen = len(right)
+
+ let tmp = strpart(left, 0, linelen - rightlen)
+ let tmplen = len(tmp)
+
+ if leftlen > tmplen
+ let left = strpart(tmp, 0, tmplen - 4) . '... '
+ let leftlen = tmplen
+ endif
+
+ let fill = repeat(' ', linelen - (leftlen + rightlen))
+
+ " return left . fill . right . repeat(' ', 100)
+ return left . fill . right
+endfunction
+
+" Function: #dispatch {{{1
+function! sy#fold#dispatch(do_tab) abort
+ if a:do_tab
+ call sy#fold#enable(1)
+ else
+ call sy#fold#toggle()
+ endif
+endfunction
+
+" Function: #enable {{{1
+function! sy#fold#enable(do_tab) abort
+ execute sy#util#return_if_no_changes()
+
+ if a:do_tab
+ tabedit %
+ endif
+
+ let [s:context0, s:context1] = get(g:, 'signify_fold_context', [3, 8])
+ let s:levels = s:get_levels(s:get_lines())
+
+ setlocal foldexpr=SignifyFoldExpr(v:lnum)
+ setlocal foldtext=SignifyFoldText()
+ setlocal foldmethod=expr
+ setlocal foldlevel=0
+endfunction
+
+" Function: #disable {{{1
+function! sy#fold#disable() abort
+ let &l:foldmethod = b:sy_folded.method
+ let &l:foldtext = b:sy_folded.text
+ normal! zv
+endfunction
+
+" Function: #toggle {{{1
+function! sy#fold#toggle() abort
+ if exists('b:sy_folded')
+ call sy#fold#disable()
+ if b:sy_folded.method == 'manual'
+ loadview
+ endif
+ unlet b:sy_folded
+ else
+ let b:sy_folded = { 'method': &foldmethod, 'text': &foldtext }
+ if &foldmethod == 'manual'
+ let old_vop = &viewoptions
+ mkview
+ let &viewoptions = old_vop
+ endif
+ call sy#fold#enable(0)
+ endif
+
+ redraw!
+ call sy#start()
+endfunction
+
+" Function: s:get_lines {{{1
+function! s:get_lines() abort
+ let signlist = sy#util#execute('sign place buffer='. b:sy.buffer)
+
+ let lines = []
+ for line in split(signlist, '\n')[2:]
+ call insert(lines, matchlist(line, '\v^\s+line\=(\d+)')[1], 0)
+ endfor
+
+ return reverse(lines)
+endfunction
+" }}}
+
+" Function: s:get_levels {{{1
+function! s:get_levels(lines) abort
+ let levels = {}
+
+ for line in range(1, line('$'))
+ let levels[line] = 2
+ endfor
+
+ for line in a:lines
+ for l in range(line - s:context1, line + s:context1)
+ if (l < 1) || (l > line('$'))
+ continue
+ endif
+ if levels[l] == 2
+ let levels[l] = 1
+ endif
+ for ll in range(line - s:context0, line + s:context0)
+ let levels[ll] = 0
+ endfor
+ endfor
+ endfor
+
+ return levels
+endfunction
diff --git a/start/signify/autoload/sy/highlight.vim b/start/signify/autoload/sy/highlight.vim
new file mode 100644
index 0000000..2ebe507
--- /dev/null
+++ b/start/signify/autoload/sy/highlight.vim
@@ -0,0 +1,94 @@
+" vim: et sw=2 sts=2
+
+scriptencoding utf-8
+
+" Init: values {{{1
+if get(g:, 'signify_sign_show_text', 1)
+ let s:sign_add = get(g:, 'signify_sign_add', '+')
+ let s:sign_delete_first_line = get(g:, 'signify_sign_delete_first_line', '‾')
+ let s:sign_change = get(g:, 'signify_sign_change', '!')
+ let s:sign_changedelete = get(g:, 'signify_sign_changedelete', s:sign_change)
+else
+ let s:sign_add = ' '
+ let s:sign_delete_first_line = ' '
+ let s:sign_change = ' '
+ let s:sign_changedelete = ' '
+endif
+
+let s:sign_show_count = get(g:, 'signify_sign_show_count', 1)
+
+" Function: #setup {{{1
+function! sy#highlight#setup() abort
+ highlight default link SignifyLineAdd DiffAdd
+ highlight default link SignifyLineDelete DiffDelete
+ highlight default link SignifyLineDeleteFirstLine SignifyLineDelete
+ highlight default link SignifyLineChange DiffChange
+ highlight default link SignifyLineChangeDelete SignifyLineChange
+
+ highlight default link SignifySignAdd DiffAdd
+ highlight default link SignifySignDelete DiffDelete
+ highlight default link SignifySignDeleteFirstLine SignifySignDelete
+ highlight default link SignifySignChange DiffChange
+ highlight default link SignifySignChangeDelete SignifySignChange
+endfunction
+
+" Function: #line_enable {{{1
+function! sy#highlight#line_enable() abort
+ execute 'sign define SignifyAdd text='. s:sign_add 'texthl=SignifySignAdd linehl=SignifyLineAdd'
+ execute 'sign define SignifyChange text='. s:sign_change 'texthl=SignifySignChange linehl=SignifyLineChange'
+ execute 'sign define SignifyRemoveFirstLine text='. s:sign_delete_first_line 'texthl=SignifySignDeleteFirstLine linehl=SignifyLineDeleteFirstLine'
+
+ if s:sign_show_count
+ let s:sign_changedelete = substitute(s:sign_changedelete, '^.\zs.*', '', '')
+ for n in range(1, 9)
+ execute 'sign define SignifyChangeDelete'. n 'text='. s:sign_changedelete . n 'texthl=SignifySignChangeDelete linehl=SignifyLineChangeDelete'
+ endfor
+ execute 'sign define SignifyChangeDeleteMore text='. s:sign_changedelete .'> texthl=SignifySignChangeDelete linehl=SignifyLineChangeDelete'
+ else
+ for n in range(1, 9)
+ execute 'sign define SignifyChangeDelete'. n 'text='. s:sign_changedelete 'texthl=SignifySignChangeDelete linehl=SignifyLineChangeDelete'
+ endfor
+ execute 'sign define SignifyChangeDeleteMore text='. s:sign_changedelete 'texthl=SignifySignChangeDelete linehl=SignifyLineChangeDelete'
+ endif
+
+ let g:signify_line_highlight = 1
+endfunction
+
+" Function: #line_disable {{{1
+function! sy#highlight#line_disable() abort
+ execute 'sign define SignifyAdd text='. s:sign_add 'texthl=SignifySignAdd linehl='
+ execute 'sign define SignifyChange text='. s:sign_change 'texthl=SignifySignChange linehl='
+ execute 'sign define SignifyRemoveFirstLine text='. s:sign_delete_first_line 'texthl=SignifySignDeleteFirstLine linehl='
+
+ if s:sign_show_count
+ while strwidth(s:sign_changedelete) > 1
+ let s:sign_changedelete = substitute(s:sign_changedelete, '.', '', '')
+ endwhile
+ for n in range(1, 9)
+ execute 'sign define SignifyChangeDelete'. n 'text='. s:sign_changedelete . n 'texthl=SignifySignChangeDelete linehl='
+ endfor
+ execute 'sign define SignifyChangeDeleteMore text='. s:sign_changedelete .'> texthl=SignifySignChangeDelete linehl='
+ else
+ for n in range(1, 9)
+ execute 'sign define SignifyChangeDelete'. n 'text='. s:sign_changedelete 'texthl=SignifySignChangeDelete linehl='
+ endfor
+ execute 'sign define SignifyChangeDeleteMore text='. s:sign_changedelete 'texthl=SignifySignChangeDelete linehl='
+ endif
+
+ let g:signify_line_highlight = 0
+endfunction
+
+" Function: #line_toggle {{{1
+function! sy#highlight#line_toggle() abort
+ if get(g:, 'signify_line_highlight')
+ call sy#highlight#line_disable()
+ else
+ call sy#highlight#line_enable()
+ endif
+
+ redraw!
+ call sy#start()
+endfunction
+" }}}
+
+call sy#highlight#setup()
diff --git a/start/signify/autoload/sy/jump.vim b/start/signify/autoload/sy/jump.vim
new file mode 100644
index 0000000..69756b1
--- /dev/null
+++ b/start/signify/autoload/sy/jump.vim
@@ -0,0 +1,29 @@
+" vim: et sw=2 sts=2
+
+scriptencoding utf-8
+
+" Function: #next_hunk {{{1
+function! sy#jump#next_hunk(count)
+ execute sy#util#return_if_no_changes()
+
+ let lnum = line('.')
+ let hunks = filter(copy(b:sy.hunks), 'v:val.start > lnum')
+ let hunk = get(hunks, a:count - 1, get(hunks, -1, {}))
+
+ if !empty(hunk)
+ execute 'sign jump '. hunk.ids[0] .' buffer='. b:sy.buffer
+ endif
+endfunction
+
+" Function: #prev_hunk {{{1
+function! sy#jump#prev_hunk(count)
+ execute sy#util#return_if_no_changes()
+
+ let lnum = line('.')
+ let hunks = filter(copy(b:sy.hunks), 'v:val.start < lnum')
+ let hunk = get(hunks, 0 - a:count, get(hunks, 0, {}))
+
+ if !empty(hunk)
+ execute 'sign jump '. hunk.ids[0] .' buffer='. b:sy.buffer
+ endif
+endfunction
diff --git a/start/signify/autoload/sy/repo.vim b/start/signify/autoload/sy/repo.vim
new file mode 100644
index 0000000..64bf07a
--- /dev/null
+++ b/start/signify/autoload/sy/repo.vim
@@ -0,0 +1,512 @@
+" vim: et sw=2 sts=2
+
+scriptencoding utf-8
+
+" Function: #detect {{{1
+function! sy#repo#detect() abort
+ for vcs in s:vcs_list
+ let b:sy.detecting += 1
+ call sy#repo#get_diff_start(vcs)
+ endfor
+endfunction
+
+" Function: s:callback_nvim_stdout{{{1
+function! s:callback_nvim_stdout(_job_id, data, _event) dict abort
+ if empty(self.stdoutbuf) || empty(self.stdoutbuf[-1])
+ let self.stdoutbuf += a:data
+ else
+ let self.stdoutbuf = self.stdoutbuf[:-2]
+ \ + [self.stdoutbuf[-1] . get(a:data, 0, '')]
+ \ + a:data[1:]
+ endif
+endfunction
+
+" Function: s:callback_nvim_exit {{{1
+function! s:callback_nvim_exit(_job_id, exitval, _event) dict abort
+ call s:job_exit(self.bufnr, self.vcs, a:exitval, self.stdoutbuf)
+endfunction
+
+" Function: s:callback_vim_stdout {{{1
+function! s:callback_vim_stdout(_job_id, data) dict abort
+ let self.stdoutbuf += [a:data]
+endfunction
+
+" Function: s:callback_vim_close {{{1
+function! s:callback_vim_close(channel) dict abort
+ let job = ch_getjob(a:channel)
+ while 1
+ if job_status(job) == 'dead'
+ let exitval = job_info(job).exitval
+ break
+ endif
+ sleep 10m
+ endwhile
+ call s:job_exit(self.bufnr, self.vcs, exitval, self.stdoutbuf)
+endfunction
+
+" Function: s:job_exit {{{1
+function! s:job_exit(bufnr, vcs, exitval, diff) abort
+ call sy#verbose('job_exit()', a:vcs)
+ let sy = getbufvar(a:bufnr, 'sy')
+ if empty(sy)
+ call sy#verbose(printf('No b:sy found for %s', bufname(a:bufnr)), a:vcs)
+ return
+ elseif !empty(sy.updated_by) && sy.updated_by != a:vcs
+ call sy#verbose(printf('Signs already got updated by %s.', sy.updated_by), a:vcs)
+ return
+ elseif empty(sy.vcs) && sy.active
+ let sy.detecting -= 1
+ endif
+ call sy#repo#get_diff_{a:vcs}(sy, a:exitval, a:diff)
+ call setbufvar(a:bufnr, 'sy_job_id_'.a:vcs, 0)
+endfunction
+
+" Function: sy#get_diff_start {{{1
+function! sy#repo#get_diff_start(vcs) abort
+ call sy#verbose('get_diff_start()', a:vcs)
+
+ let job_id = get(b:, 'sy_job_id_'.a:vcs)
+ " Neovim
+ if has('nvim')
+ if job_id
+ silent! call jobstop(job_id)
+ endif
+
+ let [cmd, options] = s:initialize_job(a:vcs)
+ let [cwd, chdir] = sy#util#chdir()
+
+ call sy#verbose(['CMD: '. string(cmd), 'CMD DIR: '. b:sy.info.dir, 'ORIG DIR: '. cwd], a:vcs)
+
+ try
+ execute chdir fnameescape(b:sy.info.dir)
+ catch
+ echohl ErrorMsg
+ echomsg 'signify: Changing directory failed: '. b:sy.info.dir
+ echohl NONE
+ return
+ endtry
+ let b:sy_job_id_{a:vcs} = jobstart(cmd, extend(options, {
+ \ 'on_stdout': function('s:callback_nvim_stdout'),
+ \ 'on_exit': function('s:callback_nvim_exit'),
+ \ }))
+ execute chdir fnameescape(cwd)
+
+ " Newer Vim
+ elseif has('patch-7.4.1967')
+ if type(job_id) != type(0)
+ silent! call job_stop(job_id)
+ endif
+
+ let [cmd, options] = s:initialize_job(a:vcs)
+ let [cwd, chdir] = sy#util#chdir()
+
+ call sy#verbose(['CMD: '. string(cmd), 'CMD DIR: '. b:sy.info.dir, 'ORIG DIR: '. cwd], a:vcs)
+
+ try
+ execute chdir fnameescape(b:sy.info.dir)
+ catch
+ echohl ErrorMsg
+ echomsg 'signify: Changing directory failed: '. b:sy.info.dir
+ echohl NONE
+ return
+ endtry
+ let opts = {
+ \ 'in_io': 'null',
+ \ 'out_cb': function('s:callback_vim_stdout', options),
+ \ 'close_cb': function('s:callback_vim_close', options),
+ \ }
+ let b:sy_job_id_{a:vcs} = job_start(cmd, opts)
+ execute chdir fnameescape(cwd)
+
+ " Older Vim
+ else
+ let diff = split(s:run(a:vcs), '\n')
+ call sy#repo#get_diff_{a:vcs}(b:sy, v:shell_error, diff)
+ endif
+endfunction
+
+" Function: s:get_diff_end {{{1
+function! s:get_diff_end(sy, found_diff, vcs, diff) abort
+ call sy#verbose('get_diff_end()', a:vcs)
+ if a:found_diff
+ if index(a:sy.vcs, a:vcs) == -1
+ let a:sy.vcs += [a:vcs]
+ endif
+ call sy#set_signs(a:sy, a:vcs, a:diff)
+ else
+ call sy#verbose('No valid diff found. Disabling this VCS.', a:vcs)
+ endif
+endfunction
+
+" Function: #get_diff_git {{{1
+function! sy#repo#get_diff_git(sy, exitval, diff) abort
+ call sy#verbose('get_diff_git()', 'git')
+ let [found_diff, diff] = a:exitval ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'git', diff)
+endfunction
+
+" Function: #get_diff_hg {{{1
+function! sy#repo#get_diff_hg(sy, exitval, diff) abort
+ call sy#verbose('get_diff_hg()', 'hg')
+ let [found_diff, diff] = a:exitval ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'hg', diff)
+endfunction
+
+" Function: #get_diff_svn {{{1
+function! sy#repo#get_diff_svn(sy, exitval, diff) abort
+ call sy#verbose('get_diff_svn()', 'svn')
+ let [found_diff, diff] = a:exitval ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'svn', diff)
+endfunction
+
+" Function: #get_diff_bzr {{{1
+function! sy#repo#get_diff_bzr(sy, exitval, diff) abort
+ call sy#verbose('get_diff_bzr()', 'bzr')
+ let [found_diff, diff] = (a:exitval =~ '[012]') ? [1, a:diff] : [0, []]
+ call s:get_diff_end(a:sy, found_diff, 'bzr', diff)
+endfunction
+
+" Function: #get_diff_darcs {{{1
+function! sy#repo#get_diff_darcs(sy, exitval, diff) abort
+ call sy#verbose('get_diff_darcs()', 'darcs')
+ let [found_diff, diff] = a:exitval ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'darcs', diff)
+endfunction
+
+" Function: #get_diff_fossil {{{1
+function! sy#repo#get_diff_fossil(sy, exitval, diff) abort
+ call sy#verbose('get_diff_fossil()', 'fossil')
+ let [found_diff, diff] = a:exitval ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'fossil', diff)
+endfunction
+
+" Function: #get_diff_cvs {{{1
+function! sy#repo#get_diff_cvs(sy, exitval, diff) abort
+ call sy#verbose('get_diff_cvs()', 'cvs')
+ let [found_diff, diff] = [0, []]
+ if a:exitval == 1
+ for diffline in a:diff
+ if diffline =~ '^+++'
+ let [found_diff, diff] = [1, a:diff]
+ break
+ endif
+ endfor
+ elseif a:exitval == 0 && len(a:diff) == 0
+ let found_diff = 1
+ endif
+ call s:get_diff_end(a:sy, found_diff, 'cvs', diff)
+endfunction
+
+" Function: #get_diff_rcs {{{1
+function! sy#repo#get_diff_rcs(sy, exitval, diff) abort
+ call sy#verbose('get_diff_rcs()', 'rcs')
+ let [found_diff, diff] = a:exitval == 2 ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'rcs', diff)
+endfunction
+
+" Function: #get_diff_accurev {{{1
+function! sy#repo#get_diff_accurev(sy, exitval, diff) abort
+ call sy#verbose('get_diff_accurev()', 'accurev')
+ let [found_diff, diff] = (a:exitval >= 2) ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'accurev', diff)
+endfunction
+
+" Function: #get_diff_perforce {{{1
+function! sy#repo#get_diff_perforce(sy, exitval, diff) abort
+ call sy#verbose('get_diff_perforce()', 'perforce')
+ let [found_diff, diff] = a:exitval ? [0, []] : [1, a:diff]
+ call s:get_diff_end(a:sy, found_diff, 'perforce', diff)
+endfunction
+
+" Function: #get_diff_tfs {{{1
+function! sy#repo#get_diff_tfs(sy, exitval, diff) abort
+ call sy#verbose('get_diff_tfs()', 'tfs')
+ let [found_diff, diff] = a:exitval ? [0, []] : [1, s:strip_context(a:diff)]
+ call s:get_diff_end(a:sy, found_diff, 'tfs', diff)
+endfunction
+
+" Function: #get_stats {{{1
+function! sy#repo#get_stats() abort
+ return exists('b:sy') ? b:sy.stats : [-1, -1, -1]
+endfunction
+
+" Function: #debug_detection {{{1
+function! sy#repo#debug_detection()
+ if !exists('b:sy')
+ echomsg 'signify: I cannot detect any changes!'
+ return
+ endif
+
+ for vcs in s:vcs_list
+ let cmd = s:expand_cmd(vcs, g:signify_vcs_cmds)
+ echohl Statement
+ echo cmd
+ echo repeat('=', len(cmd))
+ echohl NONE
+
+ let diff = s:run(vcs)
+ if v:shell_error
+ echohl ErrorMsg
+ echo diff
+ echohl NONE
+ else
+ echo empty(diff) ? "<none>" : diff
+ endif
+ echo "\n"
+ endfor
+endfunction
+
+" Function: #diffmode {{{1
+function! sy#repo#diffmode(do_tab) abort
+ execute sy#util#return_if_no_changes()
+
+ let vcs = b:sy.updated_by
+ if !has_key(g:signify_vcs_cmds_diffmode, vcs)
+ echomsg 'SignifyDiff has no support for: '. vcs
+ echomsg 'Open an issue for it at: https://github.com/mhinz/vim-signify/issues'
+ return
+ endif
+ let cmd = s:expand_cmd(vcs, g:signify_vcs_cmds_diffmode)
+ call sy#verbose('SignifyDiff: '. cmd, vcs)
+ let ft = &filetype
+ let fenc = &fenc
+ if a:do_tab
+ tabedit %
+ endif
+ diffthis
+ let [cwd, chdir] = sy#util#chdir()
+ try
+ execute chdir fnameescape(b:sy.info.dir)
+ leftabove vnew
+ if has('iconv')
+ silent put =iconv(system(cmd), fenc, &enc)
+ else
+ silent put =system(cmd)
+ endif
+ finally
+ execute chdir fnameescape(cwd)
+ endtry
+ silent 1delete
+ set buftype=nofile bufhidden=wipe nomodified
+ let &filetype = ft
+ diffthis
+ wincmd p
+ normal! ]czt
+endfunction
+
+" Function: s:initialize_job {{{1
+function! s:initialize_job(vcs) abort
+ let vcs_cmd = s:expand_cmd(a:vcs, g:signify_vcs_cmds)
+ if has('win32')
+ if has('nvim')
+ let cmd = &shell =~ '\v%(cmd|powershell)' ? vcs_cmd : ['sh', '-c', vcs_cmd]
+ else
+ if &shell =~ 'cmd'
+ let cmd = vcs_cmd
+ elseif empty(&shellxquote)
+ let cmd = join([&shell, &shellcmdflag, &shellquote, vcs_cmd, &shellquote])
+ else
+ let cmd = join([&shell, &shellcmdflag, &shellxquote, vcs_cmd, &shellxquote])
+ endif
+ endif
+ else
+ let cmd = ['sh', '-c', vcs_cmd]
+ endif
+ let options = {
+ \ 'stdoutbuf': [],
+ \ 'vcs': a:vcs,
+ \ 'bufnr': bufnr('%'),
+ \ }
+ return [cmd, options]
+endfunction
+
+" Function: s:get_vcs_path {{{1
+function! s:get_vcs_path(vcs) abort
+ return (a:vcs =~# '\v(git|cvs|accurev|tfs)') ? b:sy.info.file : b:sy.info.path
+endfunction
+
+" Function: s:expand_cmd {{{1
+function! s:expand_cmd(vcs, vcs_cmds) abort
+ let cmd = a:vcs_cmds[a:vcs]
+ let cmd = s:replace(cmd, '%f', s:get_vcs_path(a:vcs))
+ let cmd = s:replace(cmd, '%d', s:difftool)
+ let cmd = s:replace(cmd, '%n', s:devnull)
+ return cmd
+endfunction
+
+" Function: s:run {{{1
+function! s:run(vcs)
+ let [cwd, chdir] = sy#util#chdir()
+ try
+ execute chdir fnameescape(b:sy.info.dir)
+ let ret = system(s:expand_cmd(a:vcs, g:signify_vcs_cmds))
+ catch
+ " This exception message can be seen via :SignifyDebugUnknown.
+ " E.g. unquoted VCS programs in vcd_cmds can lead to E484.
+ let ret = v:exception .' at '. v:throwpoint
+ finally
+ execute chdir fnameescape(cwd)
+ return ret
+ endtry
+endfunction
+
+" Function: s:replace {{{1
+function! s:replace(cmd, pat, sub)
+ let parts = split(a:cmd, a:pat, 1)
+ return join(parts, a:sub)
+endfunction
+
+" Function: s:strip_context {{{1
+function! s:strip_context(context)
+ let diff = []
+ let hunk = []
+ let state = 0
+ let lines = a:context
+ let linenr = 0
+
+ while linenr < len(lines)
+ let line = lines[linenr]
+
+ if state == 0
+ if line =~ "^@@ "
+ 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 hunk = []
+ let state = 1
+ else
+ call add(diff,line)
+ endif
+ let linenr += 1
+ elseif index([1,2,3],state) >= 0 && index(['\','/'],line[0]) >= 0
+ let linenr += 1
+ call add(hunk,line)
+ elseif state == 1
+ if line[0] == ' '
+ let old_line += 1
+ let new_line += 1
+ let old_count -= 1
+ let new_count -= 1
+ let linenr += 1
+ else
+ let old_count_part = 0
+ let new_count_part = 0
+ let state = 2
+ endif
+ elseif state == 2
+ if line[0] == '-'
+ call add(hunk,line)
+ let old_count_part += 1
+ let linenr += 1
+ else
+ let state = 3
+ endif
+ elseif state == 3
+ if line[0] == '+'
+ call add(hunk,line)
+ let new_count_part += 1
+ let linenr += 1
+ else
+ call add(diff, printf("@@ -%d%s +%d%s @@",(old_count_part == 0 && old_line > 0) ? old_line -1 : old_line, old_count_part == 1 ? "" : printf(",%d", old_count_part), (new_count_part == 0 && new_line > 0) ? new_line - 1 : new_line, new_count_part == 1 ? "" : printf(",%d", new_count_part)))
+ let diff += hunk
+ let hunk = []
+ let old_count -= old_count_part
+ let new_count -= new_count_part
+ let old_line += old_count_part
+ let new_line += new_count_part
+ let state = 1
+ endif
+ endif
+
+ if state > 0 && new_count <= 0 && old_count <= 0
+ if len(hunk) > 0
+ call add(diff, printf("@@ -%d%s +%d%s @@",(old_count_part == 0 && old_line > 0) ? old_line -1 : old_line, old_count_part == 1 ? "" : printf(",%d", old_count_part), (new_count_part == 0 && new_line > 0) ? new_line - 1 : new_line, new_count_part == 1 ? "" : printf(",%d", new_count_part)))
+ let diff = diff + hunk
+ let hunk = []
+ endif
+ let state = 0
+ endif
+ endwhile
+ if len(hunk) > 0
+ call add(diff, printf("@@ -%d%s +%d%s @@",(old_count_part == 0 && old_line > 0) ? old_line -1 : old_line, old_count_part == 1 ? "" : printf(",%d", old_count_part), (new_count_part == 0 && new_line > 0) ? new_line - 1 : new_line, new_count_part == 1 ? "" : printf(",%d", new_count_part)))
+ let diff = diff + hunk
+ let hunk = []
+ endif
+ return diff
+endfunction
+
+" Variables {{{1
+let s:difftool = get(g:, 'signify_difftool', 'diff')
+if executable(s:difftool)
+ let s:vcs_dict = {
+ \ 'git': 'git',
+ \ 'hg': 'hg',
+ \ 'svn': 'svn',
+ \ 'darcs': 'darcs',
+ \ 'bzr': 'bzr',
+ \ 'fossil': 'fossil',
+ \ 'cvs': 'cvs',
+ \ 'rcs': 'rcsdiff',
+ \ 'accurev': 'accurev',
+ \ 'perforce': 'p4',
+ \ 'tfs': 'tf'
+ \ }
+else
+ call sy#verbose('No "diff" executable found. Disable support for svn, darcs, bzr.')
+ let s:vcs_dict = {
+ \ 'git': 'git',
+ \ 'hg': 'hg',
+ \ 'fossil': 'fossil',
+ \ 'cvs': 'cvs',
+ \ 'rcs': 'rcsdiff',
+ \ 'accurev': 'accurev',
+ \ 'perforce': 'p4',
+ \ 'tfs': 'tf'
+ \ }
+endif
+
+let s:vcs_list = get(g:, 'signify_vcs_list', [])
+if empty(s:vcs_list)
+ let s:vcs_list = keys(filter(s:vcs_dict, 'executable(v:val)'))
+endif
+
+let s:default_vcs_cmds = {
+ \ 'git': 'git diff --no-color --no-ext-diff -U0 -- %f',
+ \ 'hg': 'hg diff --color=never --config aliases.diff= --nodates -U0 -- %f',
+ \ 'svn': 'svn diff --diff-cmd %d -x -U0 -- %f',
+ \ 'bzr': 'bzr diff --using %d --diff-options=-U0 -- %f',
+ \ 'darcs': 'darcs diff --no-pause-for-gui --no-unified --diff-opts=-U0 -- %f',
+ \ 'fossil': 'fossil diff --unified -c 0 -- %f',
+ \ 'cvs': 'cvs diff -U0 -- %f',
+ \ 'rcs': 'rcsdiff -U0 %f 2>%n',
+ \ 'accurev': 'accurev diff %f -- -U0',
+ \ 'perforce': 'p4 info '. sy#util#shell_redirect('%n') . (has('win32') ? ' &&' : ' && env P4DIFF= P4COLORS=') .' p4 diff -du0 %f',
+ \ 'tfs': 'tf diff -version:W -noprompt -format:Unified %f'
+ \ }
+
+let s:default_vcs_cmds_diffmode = {
+ \ 'git': 'git show HEAD:./%f',
+ \ 'hg': 'hg cat %f',
+ \ 'svn': 'svn cat %f',
+ \ 'bzr': 'bzr cat %f',
+ \ 'darcs': 'darcs show contents -- %f',
+ \ 'fossil': 'fossil cat %f',
+ \ 'cvs': 'cvs up -p -- %f 2>%n',
+ \ 'perforce': 'p4 print %f',
+ \ }
+
+if exists('g:signify_vcs_cmds')
+ call extend(g:signify_vcs_cmds, s:default_vcs_cmds, 'keep')
+else
+ let g:signify_vcs_cmds = s:default_vcs_cmds
+endif
+if exists('g:signify_vcs_cmds_diffmode')
+ call extend(g:signify_vcs_cmds_diffmode, s:default_vcs_cmds_diffmode, 'keep')
+else
+ let g:signify_vcs_cmds_diffmode = s:default_vcs_cmds_diffmode
+endif
+
+let s:difftool = sy#util#escape(s:difftool)
+let s:devnull = has('win32') || has ('win64') ? 'NUL' : '/dev/null'
diff --git a/start/signify/autoload/sy/sign.vim b/start/signify/autoload/sy/sign.vim
new file mode 100644
index 0000000..23b7038
--- /dev/null
+++ b/start/signify/autoload/sy/sign.vim
@@ -0,0 +1,275 @@
+" 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 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])
+
+ " 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: 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
+
diff --git a/start/signify/autoload/sy/util.vim b/start/signify/autoload/sy/util.vim
new file mode 100644
index 0000000..43a9cc6
--- /dev/null
+++ b/start/signify/autoload/sy/util.vim
@@ -0,0 +1,109 @@
+" vim: et sw=2 sts=2
+
+scriptencoding utf-8
+
+" Function: #escape {{{1
+function! sy#util#escape(path) abort
+ if exists('+shellslash')
+ let old_ssl = &shellslash
+ if fnamemodify(&shell, ':t') == 'cmd.exe'
+ set noshellslash
+ else
+ set shellslash
+ endif
+ endif
+
+ let path = shellescape(a:path)
+
+ if exists('old_ssl')
+ let &shellslash = old_ssl
+ endif
+
+ return path
+endfunction
+
+" Function: #refresh_windows {{{1
+function! sy#util#refresh_windows() abort
+ if exists('*win_getid')
+ let winid = win_getid()
+ else
+ let winnr = winnr()
+ endif
+
+ if !get(g:, 'signify_cmdwin_active')
+ keepjumps windo if exists('b:sy') | call sy#start() | endif
+ endif
+
+ if exists('winid')
+ call win_gotoid(winid)
+ else
+ execute winnr .'wincmd w'
+ endif
+endfunction
+
+" Function: #hunk_text_object {{{1
+function! sy#util#hunk_text_object(emptylines) abort
+ execute sy#util#return_if_no_changes()
+
+ let lnum = line('.')
+ let hunks = filter(copy(b:sy.hunks), 'v:val.start <= lnum && v:val.end >= lnum')
+
+ if empty(hunks)
+ echomsg 'signify: Here is no hunk.'
+ return
+ endif
+
+ execute hunks[0].start
+ normal! V
+
+ if a:emptylines
+ let lnum = hunks[0].end
+ while getline(lnum+1) =~ '^$'
+ let lnum += 1
+ endwhile
+ execute lnum
+ else
+ execute hunks[0].end
+ endif
+endfunction
+
+" Function: #shell_redirect {{{1
+function! sy#util#shell_redirect(path) abort
+ " if shellredir contains a %s it is replaced with the path
+ " otherwise, just append it (from :help shellredir:
+ " The name of the temporary file can be represented by '%s' if necessary
+ " (the file name is appended automatically if no %s appears in the value
+ " of this option)
+ if &shellredir =~# '%s'
+ return substitute(&shellredir, '\C%s', a:path, 'g')
+ else
+ return &shellredir .' '. a:path
+ endif
+endfunction
+
+" Function: #chdir {{{1
+function! sy#util#chdir() abort
+ let chdir = haslocaldir()
+ \ ? 'lcd'
+ \ : (exists(':tcd') && haslocaldir(-1, 0)) ? 'tcd' : 'cd'
+ return [getcwd(), chdir]
+endfunction
+
+" Function: #has_changes {{{1
+function! sy#util#return_if_no_changes() abort
+ if !exists('b:sy') || empty(b:sy.hunks)
+ echomsg 'signify: There are no changes.'
+ return 'return'
+ endif
+ return ''
+endfunction
+
+" Function: #execute {{{1
+function! sy#util#execute(cmd) abort
+ let lang = v:lang
+ redir => output
+ silent! execute a:cmd
+ redir END
+ silent! execute 'language message' lang
+ return output
+endfunction