vim-airline/autoload/airline/extensions/branch.vim
Christian Brabandt 14ab47cae6 Add mq status for hg repositories
Now that we have async feature for Vim 8, we can add the output of 'hg
qtop' back to the statusline if it is supported
2016-10-20 22:46:24 +02:00

334 lines
9.8 KiB
VimL

" MIT License. Copyright (c) 2013-2016 Bailey Ling.
" vim: et ts=2 sts=2 sw=2
scriptencoding utf-8
let s:has_fugitive = exists('*fugitive#head')
let s:has_lawrencium = exists('*lawrencium#statusline')
let s:has_vcscommand = get(g:, 'airline#extensions#branch#use_vcscommand', 0) && exists('*VCSCommandGetStatusLine')
let s:has_async = airline#util#async
let s:git_cmd = 'git status --porcelain -- '
let s:hg_cmd = 'hg status -u -- '
if !s:has_fugitive && !s:has_lawrencium && !s:has_vcscommand
finish
endif
let s:git_dirs = {}
let s:untracked_git = {}
let s:untracked_hg = {}
let s:head_format = get(g:, 'airline#extensions#branch#format', 0)
if s:head_format == 1
function! s:format_name(name)
return fnamemodify(a:name, ':t')
endfunction
elseif s:head_format == 2
function! s:format_name(name)
return pathshorten(a:name)
endfunction
elseif type(s:head_format) == type('')
function! s:format_name(name)
return call(s:head_format, [a:name])
endfunction
else
function! s:format_name(name)
return a:name
endfunction
endif
function! s:get_git_branch(path)
if !s:has_fugitive
return ''
endif
let name = fugitive#head(7)
if empty(name)
if has_key(s:git_dirs, a:path)
return s:git_dirs[a:path]
endif
let dir = fugitive#extract_git_dir(a:path)
if empty(dir)
let name = ''
else
try
let line = join(readfile(dir . '/HEAD'))
if strpart(line, 0, 16) == 'ref: refs/heads/'
let name = strpart(line, 16)
else
" raw commit hash
let name = strpart(line, 0, 7)
endif
catch
let name = ''
endtry
endif
endif
let s:git_dirs[a:path] = name
return name
endfunction
function! s:get_git_untracked(file)
if empty(a:file) || !executable('git')
return
endif
if !has_key(s:untracked_git, a:file)
if s:has_async
call s:get_vcs_untracked_async(s:git_cmd, a:file)
else
let output = system(s:git_cmd. shellescape(a:file))
let untracked = ''
if output[0:1] is# '??' && output[3:-2] is? a:file
let untracked = get(g:, 'airline#extensions#branch#notexists', g:airline_symbols.notexists)
endif
let s:untracked_git[a:file] = untracked
endif
endif
endfunction
function! s:get_hg_untracked(file)
if empty(a:file) || !executable('hg')
return
endif
" delete cache when unlet b:airline head?
if !has_key(s:untracked_hg, a:file)
if s:has_async
call s:get_vcs_untracked_async(s:hg_cmd, a:file)
else
let untracked = (system(s:hg_cmd. shellescape(a:file))[0] is# '?' ?
\ get(g:, 'airline#extensions#branch#notexists', g:airline_symbols.notexists) : '')
let s:untracked_hg[a:file] = untracked
endif
endif
endfunction
function! s:get_hg_branch()
if s:has_lawrencium
let stl=lawrencium#statusline()
if !empty(stl) && has('job')
call s:get_mq_async('hg qtop', expand('%:p'))
endif
if exists("s:mq") && !empty(s:mq)
if stl is# 'default'
" Shorten default a bit
let stl='def'
endif
let stl.=' ['.s:mq.']'
endif
return stl
endif
return ''
endfunction
if s:has_async
let s:jobs = {}
function! s:on_stdout(channel, msg) dict abort
let self.buf .= a:msg
endfunction
function! s:on_exit(channel) dict abort
let untracked = get(g:, 'airline#extensions#branch#notexists', g:airline_symbols.notexists)
if empty(self.buf)
let s:untracked_{self.cmd}[self.file] = ''
elseif (self.buf[0:1] is# '??' && self.cmd is# 'git') || (self.buf[0] is# '?' && self.cmd is# 'hg')
let s:untracked_{self.cmd}[self.file] = untracked
else
let s:untracked_{self.cmd}[self.file] = ''
endif
if has_key(s:jobs, self.file)
call remove(s:jobs, self.file)
endif
endfunction
function! s:get_vcs_untracked_async(cmd, file)
if g:airline#util#is_windows && &shell =~ 'cmd'
let cmd = a:cmd. shellescape(a:file)
else
let cmd = ['sh', '-c', a:cmd. shellescape(a:file)]
endif
let cmdstring = split(a:cmd)[0]
let options = {'cmd': cmdstring, 'buf': '', 'file': a:file}
if has_key(s:jobs, a:file)
if job_status(get(s:jobs, a:file)) == 'run'
return
elseif has_key(s:jobs, a:file)
call remove(s:jobs, a:file)
endif
endif
let id = job_start(cmd, {
\ 'err_io': 'out',
\ 'out_cb': function('s:on_stdout', options),
\ 'close_cb': function('s:on_exit', options)})
let s:jobs[a:file] = id
endfu
function! s:on_exit_mq(channel) dict abort
if !empty(self.buf)
if self.buf is# 'no patches applied' ||
\ self.buf =~# "unknown command 'qtop'"
let self.buf = ''
elseif exists("s:mq") && s:mq isnot# self.buf
" make sure, statusline is updated
unlet! b:airline_head
endif
let s:mq = self.buf
endif
if has_key(s:jobs, self.file)
call remove(s:jobs, self.file)
endif
endfunction
function! s:get_mq_async(cmd, file)
if g:airline#util#is_windows && &shell =~ 'cmd'
let cmd = a:cmd. shellescape(a:file)
else
let cmd = ['sh', '-c', a:cmd]
endif
let options = {'cmd': a:cmd, 'buf': '', 'file': a:file}
if has_key(s:jobs, a:file)
if job_status(get(s:jobs, a:file)) == 'run'
return
elseif has_key(s:jobs, a:file)
call remove(s:jobs, a:file)
endif
endif
let id = job_start(cmd, {
\ 'err_io': 'out',
\ 'out_cb': function('s:on_stdout', options),
\ 'close_cb': function('s:on_exit_mq', options)})
let s:jobs[a:file] = id
endfu
endif
function! airline#extensions#branch#head()
if exists('b:airline_head') && !empty(b:airline_head)
return b:airline_head
endif
let b:airline_head = ''
let l:heads = {}
let l:vcs_priority = get(g:, "airline#extensions#branch#vcs_priority", ["git", "mercurial"])
let found_fugitive_head = 0
if exists("*fnamemodify")
let l:git_head = s:get_git_branch(fnamemodify(resolve(@%), ":p:h"))
else
let l:git_head = s:get_git_branch(expand("%:p:h"))
endif
let l:hg_head = s:get_hg_branch()
let l:file = expand("%:p")
if !empty(l:git_head)
let found_fugitive_head = 1
let l:heads.git = (!empty(l:hg_head) ? "git:" : '') . s:format_name(l:git_head)
call s:get_git_untracked(l:file)
let l:heads.git .= get(s:untracked_git, l:file, '')
endif
if !empty(l:hg_head)
let l:heads.mercurial = (!empty(l:git_head) ? "hg:" : '') . s:format_name(l:hg_head)
call s:get_hg_untracked(l:file)
let l:heads.mercurial.= get(s:untracked_hg, l:file, '')
endif
if empty(l:heads)
if s:has_vcscommand
call VCSCommandEnableBufferSetup()
if exists('b:VCSCommandBufferInfo')
let b:airline_head = s:format_name(get(b:VCSCommandBufferInfo, 0, ''))
endif
endif
else
let b:airline_head = get(b:, 'airline_head', '')
for vcs in l:vcs_priority
if has_key(l:heads, vcs)
if !empty(b:airline_head)
let b:airline_head = b:airline_head . " | "
endif
let b:airline_head = b:airline_head . l:heads[vcs]
endif
endfor
endif
if exists("g:airline#extensions#branch#displayed_head_limit")
let w:displayed_head_limit = g:airline#extensions#branch#displayed_head_limit
if len(b:airline_head) > w:displayed_head_limit - 1
let b:airline_head = b:airline_head[0:(w:displayed_head_limit - 1)].(&encoding ==? 'utf-8' ? '…' : '.')
endif
endif
if empty(b:airline_head) || !found_fugitive_head && !s:check_in_path()
let b:airline_head = ''
endif
let minwidth = empty(get(b:, 'airline_hunks', '')) ? 14 : 7
let b:airline_head = airline#util#shorten(b:airline_head, 120, minwidth)
return b:airline_head
endfunction
function! airline#extensions#branch#get_head()
let head = airline#extensions#branch#head()
let empty_message = get(g:, 'airline#extensions#branch#empty_message', '')
let symbol = get(g:, 'airline#extensions#branch#symbol', g:airline_symbols.branch)
return empty(head)
\ ? empty_message
\ : printf('%s%s', empty(symbol) ? '' : symbol.(g:airline_symbols.space), head)
endfunction
function! s:check_in_path()
if !exists('b:airline_branch_path')
let root = get(b:, 'git_dir', get(b:, 'mercurial_dir', ''))
let bufferpath = resolve(fnamemodify(expand('%'), ':p'))
if !filereadable(root) "not a file
" if .git is a directory, it's the old submodule format
if match(root, '\.git$') >= 0
let root = expand(fnamemodify(root, ':h'))
else
" else it's the newer format, and we need to guesstimate
let pattern = '\.git\(\\\|\/\)modules\(\\\|\/\)'
if match(root, pattern) >= 0
let root = substitute(root, pattern, '', '')
endif
endif
endif
let b:airline_file_in_root = stridx(bufferpath, root) > -1
endif
return b:airline_file_in_root
endfunction
function! s:reset_untracked_cache(shellcmdpost)
" shellcmdpost - whether function was called as a result of ShellCmdPost hook
if !s:has_async
if a:shellcmdpost
" Clear cache only if there was no error or the script uses an
" asynchronous interface. Otherwise, cache clearing would overwrite
" v:shell_error with a system() call inside get_*_untracked.
if v:shell_error
return
endif
endif
endif
if exists("s:untracked_git")
let s:untracked_git={}
endif
if exists("s:untracked_hg")
let s:untracked_hg={}
endif
endfunction
function! airline#extensions#branch#init(ext)
call airline#parts#define_function('branch', 'airline#extensions#branch#get_head')
autocmd BufReadPost * unlet! b:airline_file_in_root
autocmd CursorHold,ShellCmdPost,CmdwinLeave * unlet! b:airline_head
autocmd User AirlineBeforeRefresh unlet! b:airline_head
autocmd BufWritePost * call s:reset_untracked_cache(0)
autocmd ShellCmdPost * call s:reset_untracked_cache(1)
endfunction