2019-03-25 19:27:10 +08:00
|
|
|
" MIT License. Copyright (c) 2013-2019 Bailey Ling et al.
|
2013-08-19 02:34:02 +08:00
|
|
|
" vim: et ts=2 sts=2 sw=2
|
|
|
|
|
2016-09-24 08:16:30 +08:00
|
|
|
scriptencoding utf-8
|
|
|
|
|
2016-10-30 16:48:24 +08:00
|
|
|
" s:vcs_config contains static configuration of VCSes and their status relative
|
|
|
|
" to the active file.
|
|
|
|
" 'branch' - The name of currently active branch. This field is empty iff it
|
|
|
|
" has not been initialized yet or the current file is not in
|
|
|
|
" an active branch.
|
|
|
|
" 'untracked' - Cache of untracked files represented as a dictionary with files
|
|
|
|
" as keys. A file has a not exists symbol set as its value if it
|
|
|
|
" is untracked. A file is present in this dictionary iff its
|
|
|
|
" status is considered up to date.
|
|
|
|
" 'untracked_mark' - used as regexp to test against the output of 'cmd'
|
|
|
|
let s:vcs_config = {
|
|
|
|
\ 'git': {
|
|
|
|
\ 'exe': 'git',
|
|
|
|
\ 'cmd': 'git status --porcelain -- ',
|
2019-04-25 14:47:05 +08:00
|
|
|
\ 'dirty': 'git status -uno --porcelain --ignore-submodules',
|
2016-10-30 16:48:24 +08:00
|
|
|
\ 'untracked_mark': '??',
|
2017-06-24 20:03:40 +08:00
|
|
|
\ 'exclude': '\.git',
|
2017-12-26 20:10:41 +08:00
|
|
|
\ 'update_branch': 's:update_git_branch',
|
|
|
|
\ 'display_branch': 's:display_git_branch',
|
2016-10-30 16:48:24 +08:00
|
|
|
\ 'branch': '',
|
|
|
|
\ 'untracked': {},
|
|
|
|
\ },
|
|
|
|
\ 'mercurial': {
|
|
|
|
\ 'exe': 'hg',
|
|
|
|
\ 'cmd': 'hg status -u -- ',
|
2019-04-25 13:35:41 +08:00
|
|
|
\ 'dirty': 'hg status -mard',
|
2016-10-30 16:48:24 +08:00
|
|
|
\ 'untracked_mark': '?',
|
2017-06-24 20:03:40 +08:00
|
|
|
\ 'exclude': '\.hg',
|
2016-10-30 16:48:24 +08:00
|
|
|
\ 'update_branch': 's:update_hg_branch',
|
2017-12-26 20:10:41 +08:00
|
|
|
\ 'display_branch': 's:display_hg_branch',
|
2016-10-30 16:48:24 +08:00
|
|
|
\ 'branch': '',
|
|
|
|
\ 'untracked': {},
|
|
|
|
\ },
|
|
|
|
\}
|
|
|
|
|
|
|
|
" Initializes b:buffer_vcs_config. b:buffer_vcs_config caches the branch and
|
|
|
|
" untracked status of the file in the buffer. Caching those fields is necessary,
|
|
|
|
" because s:vcs_config may be updated asynchronously and s:vcs_config fields may
|
|
|
|
" be invalid during those updates. b:buffer_vcs_config fields are updated
|
|
|
|
" whenever corresponding fields in s:vcs_config are updated or an inconsistency
|
|
|
|
" is detected during update_* operation.
|
|
|
|
"
|
|
|
|
" b:airline_head caches the head string it is empty iff it needs to be
|
|
|
|
" recalculated. b:airline_head is recalculated based on b:buffer_vcs_config.
|
|
|
|
function! s:init_buffer()
|
|
|
|
let b:buffer_vcs_config = {}
|
|
|
|
for vcs in keys(s:vcs_config)
|
|
|
|
let b:buffer_vcs_config[vcs] = {
|
|
|
|
\ 'branch': '',
|
|
|
|
\ 'untracked': '',
|
2019-04-24 21:15:29 +08:00
|
|
|
\ 'dirty': 0,
|
2016-10-30 16:48:24 +08:00
|
|
|
\ }
|
|
|
|
endfor
|
|
|
|
unlet! b:airline_head
|
|
|
|
endfunction
|
2016-02-03 05:45:47 +08:00
|
|
|
|
2015-02-28 11:04:13 +08:00
|
|
|
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
|
2015-11-19 18:03:54 +08:00
|
|
|
elseif s:head_format == 2
|
|
|
|
function! s:format_name(name)
|
|
|
|
return pathshorten(a:name)
|
|
|
|
endfunction
|
2015-02-28 11:04:13 +08:00
|
|
|
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
|
|
|
|
|
2017-11-23 01:54:29 +08:00
|
|
|
|
|
|
|
" Fugitive special revisions. call '0' "staging" ?
|
2017-12-23 22:05:59 +08:00
|
|
|
let s:names = {'0': 'index', '1': 'orig', '2':'fetch', '3':'merge'}
|
2017-11-23 05:41:00 +08:00
|
|
|
let s:sha1size = get(g:, 'airline#extensions#branch#sha1_len', 7)
|
2016-10-30 16:48:24 +08:00
|
|
|
|
2017-11-23 02:12:29 +08:00
|
|
|
function! s:update_git_branch()
|
2018-06-04 23:31:13 +08:00
|
|
|
if !airline#util#has_fugitive()
|
2016-10-30 16:48:24 +08:00
|
|
|
let s:vcs_config['git'].branch = ''
|
|
|
|
return
|
2014-04-03 08:54:43 +08:00
|
|
|
endif
|
|
|
|
|
2018-06-05 00:32:30 +08:00
|
|
|
let s:vcs_config['git'].branch = exists("*FugitiveHead") ?
|
|
|
|
\ FugitiveHead(s:sha1size) : fugitive#head(s:sha1size)
|
2019-02-04 00:30:55 +08:00
|
|
|
if s:vcs_config['git'].branch is# 'master' &&
|
|
|
|
\ airline#util#winwidth() < 81
|
2018-01-05 15:12:10 +08:00
|
|
|
" Shorten default a bit
|
|
|
|
let s:vcs_config['git'].branch='mas'
|
|
|
|
endif
|
2017-12-26 20:10:41 +08:00
|
|
|
endfunction
|
2014-04-03 08:54:43 +08:00
|
|
|
|
2017-12-26 20:10:41 +08:00
|
|
|
function! s:display_git_branch()
|
|
|
|
let name = b:buffer_vcs_config['git'].branch
|
2017-11-23 01:54:29 +08:00
|
|
|
try
|
2019-04-24 21:29:51 +08:00
|
|
|
let commit = matchstr(FugitiveParse()[0], '^\x\+')
|
2017-11-23 01:54:29 +08:00
|
|
|
|
|
|
|
if has_key(s:names, commit)
|
|
|
|
let name = get(s:names, commit)."(".name.")"
|
|
|
|
elseif !empty(commit)
|
|
|
|
let ref = fugitive#repo().git_chomp('describe', '--all', '--exact-match', commit)
|
|
|
|
if ref !~ "^fatal: no tag exactly matches"
|
|
|
|
let name = s:format_name(substitute(ref, '\v\C^%(heads/|remotes/|tags/)=','',''))."(".name.")"
|
|
|
|
else
|
2018-03-15 04:33:25 +08:00
|
|
|
let name = matchstr(commit, '.\{'.s:sha1size.'}')."(".name.")"
|
2017-11-23 01:54:29 +08:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
catch
|
|
|
|
endtry
|
|
|
|
|
2017-12-26 20:10:41 +08:00
|
|
|
return name
|
2014-04-03 08:54:43 +08:00
|
|
|
endfunction
|
|
|
|
|
2017-11-23 02:12:29 +08:00
|
|
|
function! s:update_hg_branch()
|
2018-06-04 23:31:13 +08:00
|
|
|
if airline#util#has_lawrencium()
|
2017-08-23 05:21:19 +08:00
|
|
|
let cmd='LC_ALL=C hg qtop'
|
2016-10-21 04:23:01 +08:00
|
|
|
let stl=lawrencium#statusline()
|
2017-09-23 03:37:28 +08:00
|
|
|
let file=expand('%:p')
|
2017-08-23 05:21:19 +08:00
|
|
|
if !empty(stl) && get(b:, 'airline_do_mq_check', 1)
|
2017-08-24 00:10:59 +08:00
|
|
|
if g:airline#init#vim_async
|
2018-06-11 20:45:29 +08:00
|
|
|
noa call airline#async#get_mq_async(cmd, file)
|
2017-08-23 05:21:19 +08:00
|
|
|
elseif has("nvim")
|
2018-06-11 20:45:29 +08:00
|
|
|
noa call airline#async#nvim_get_mq_async(cmd, file)
|
2017-08-23 05:21:19 +08:00
|
|
|
else
|
2017-09-14 17:43:53 +08:00
|
|
|
" remove \n at the end of the command
|
|
|
|
let output=system(cmd)[0:-2]
|
2018-06-11 20:45:29 +08:00
|
|
|
noa call airline#async#mq_output(output, file)
|
2017-08-23 05:21:19 +08:00
|
|
|
endif
|
2016-10-21 04:23:01 +08:00
|
|
|
endif
|
2017-08-23 05:21:19 +08:00
|
|
|
" do not do mq check anymore
|
|
|
|
let b:airline_do_mq_check = 0
|
|
|
|
if exists("b:mq") && !empty(b:mq)
|
2016-10-21 04:23:01 +08:00
|
|
|
if stl is# 'default'
|
|
|
|
" Shorten default a bit
|
|
|
|
let stl='def'
|
|
|
|
endif
|
2017-08-23 05:21:19 +08:00
|
|
|
let stl.=' ['.b:mq.']'
|
2016-10-21 04:23:01 +08:00
|
|
|
endif
|
2016-11-04 09:54:39 +08:00
|
|
|
let s:vcs_config['mercurial'].branch = stl
|
2016-10-30 16:48:24 +08:00
|
|
|
else
|
|
|
|
let s:vcs_config['mercurial'].branch = ''
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
2017-12-26 20:10:41 +08:00
|
|
|
function! s:display_hg_branch()
|
|
|
|
return b:buffer_vcs_config['mercurial'].branch
|
|
|
|
endfunction
|
|
|
|
|
2016-10-30 16:48:24 +08:00
|
|
|
function! s:update_branch()
|
|
|
|
for vcs in keys(s:vcs_config)
|
2017-11-23 02:12:29 +08:00
|
|
|
call {s:vcs_config[vcs].update_branch}()
|
2016-10-30 16:48:24 +08:00
|
|
|
if b:buffer_vcs_config[vcs].branch != s:vcs_config[vcs].branch
|
|
|
|
let b:buffer_vcs_config[vcs].branch = s:vcs_config[vcs].branch
|
|
|
|
unlet! b:airline_head
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
endfunction
|
|
|
|
|
2017-08-23 05:21:19 +08:00
|
|
|
function! airline#extensions#branch#update_untracked_config(file, vcs)
|
2016-10-30 16:48:24 +08:00
|
|
|
if !has_key(s:vcs_config[a:vcs].untracked, a:file)
|
|
|
|
return
|
|
|
|
elseif s:vcs_config[a:vcs].untracked[a:file] != b:buffer_vcs_config[a:vcs].untracked
|
|
|
|
let b:buffer_vcs_config[a:vcs].untracked = s:vcs_config[a:vcs].untracked[a:file]
|
|
|
|
unlet! b:airline_head
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:update_untracked()
|
2017-08-23 05:21:19 +08:00
|
|
|
let file = expand("%:p")
|
2019-04-25 13:32:13 +08:00
|
|
|
if empty(file) || isdirectory(file) || !empty(&buftype)
|
2016-10-30 16:48:24 +08:00
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
2017-08-23 05:21:19 +08:00
|
|
|
let needs_update = 1
|
2019-05-02 14:14:57 +08:00
|
|
|
let vcs_checks = get(g:, "airline#extensions#branch#vcs_checks", ["untracked", "dirty"])
|
2016-10-30 16:48:24 +08:00
|
|
|
for vcs in keys(s:vcs_config)
|
2017-08-23 05:21:19 +08:00
|
|
|
if file =~ s:vcs_config[vcs].exclude
|
2017-06-24 20:03:40 +08:00
|
|
|
" Skip check for files that live in the exclude directory
|
2017-08-23 05:21:19 +08:00
|
|
|
let needs_update = 0
|
2017-06-24 20:03:40 +08:00
|
|
|
endif
|
2017-08-23 05:21:19 +08:00
|
|
|
if has_key(s:vcs_config[vcs].untracked, file)
|
|
|
|
let needs_update = 0
|
|
|
|
call airline#extensions#branch#update_untracked_config(file, vcs)
|
2016-10-30 16:48:24 +08:00
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
|
2017-08-23 05:21:19 +08:00
|
|
|
if !needs_update
|
2016-10-30 16:48:24 +08:00
|
|
|
return
|
2016-01-29 20:11:14 +08:00
|
|
|
endif
|
2016-10-30 16:48:24 +08:00
|
|
|
|
|
|
|
for vcs in keys(s:vcs_config)
|
2017-08-23 05:21:19 +08:00
|
|
|
let config = s:vcs_config[vcs]
|
2019-04-24 19:25:31 +08:00
|
|
|
" Note that asynchronous update updates s:vcs_config only, and only
|
|
|
|
" s:update_untracked updates b:buffer_vcs_config. If s:vcs_config is
|
|
|
|
" invalidated again before s:update_untracked is called, then we lose the
|
|
|
|
" result of the previous call, i.e. the head string is not updated. It
|
|
|
|
" doesn't happen often in practice, so we let it be.
|
2019-05-02 14:14:57 +08:00
|
|
|
if index(vcs_checks, 'untracked') > -1
|
|
|
|
call airline#async#vcs_untracked(config, file, vcs)
|
|
|
|
endif
|
2019-04-24 21:15:29 +08:00
|
|
|
" Check clean state of repo
|
2019-05-02 14:14:57 +08:00
|
|
|
if index(vcs_checks, 'dirty') > -1
|
|
|
|
call airline#async#vcs_clean(config.dirty, file, vcs)
|
|
|
|
endif
|
2016-10-30 16:48:24 +08:00
|
|
|
endfor
|
2016-01-29 20:11:14 +08:00
|
|
|
endfunction
|
|
|
|
|
2013-12-03 13:32:54 +08:00
|
|
|
function! airline#extensions#branch#head()
|
2016-10-30 16:48:24 +08:00
|
|
|
if !exists('b:buffer_vcs_config')
|
|
|
|
call s:init_buffer()
|
|
|
|
endif
|
|
|
|
|
|
|
|
call s:update_branch()
|
|
|
|
call s:update_untracked()
|
|
|
|
|
2014-04-03 08:54:43 +08:00
|
|
|
if exists('b:airline_head') && !empty(b:airline_head)
|
2014-03-25 02:01:31 +08:00
|
|
|
return b:airline_head
|
|
|
|
endif
|
|
|
|
|
|
|
|
let b:airline_head = ''
|
2017-08-23 05:21:19 +08:00
|
|
|
let vcs_priority = get(g:, "airline#extensions#branch#vcs_priority", ["git", "mercurial"])
|
2013-08-19 01:22:35 +08:00
|
|
|
|
2017-12-26 20:10:41 +08:00
|
|
|
let heads = []
|
2017-08-23 05:21:19 +08:00
|
|
|
for vcs in vcs_priority
|
2016-10-30 16:48:24 +08:00
|
|
|
if !empty(b:buffer_vcs_config[vcs].branch)
|
2017-12-26 20:10:41 +08:00
|
|
|
let heads += [vcs]
|
2016-10-23 22:33:31 +08:00
|
|
|
endif
|
|
|
|
endfor
|
2013-08-19 02:34:02 +08:00
|
|
|
|
2017-12-26 20:10:41 +08:00
|
|
|
for vcs in heads
|
2016-10-23 22:33:31 +08:00
|
|
|
if !empty(b:airline_head)
|
|
|
|
let b:airline_head .= ' | '
|
2016-10-23 18:05:46 +08:00
|
|
|
endif
|
2017-12-26 20:10:41 +08:00
|
|
|
if len(heads) > 1
|
|
|
|
let b:airline_head .= s:vcs_config[vcs].exe .':'
|
|
|
|
endif
|
|
|
|
let b:airline_head .= s:format_name({s:vcs_config[vcs].display_branch}())
|
2019-04-24 21:15:29 +08:00
|
|
|
let additional = b:buffer_vcs_config[vcs].untracked
|
2019-04-30 22:33:05 +08:00
|
|
|
if empty(additional) &&
|
|
|
|
\ has_key(b:buffer_vcs_config[vcs], 'dirty') &&
|
|
|
|
\ b:buffer_vcs_config[vcs].dirty
|
2019-04-24 21:15:29 +08:00
|
|
|
let additional = g:airline_symbols['dirty']
|
|
|
|
endif
|
|
|
|
let b:airline_head .= additional
|
2016-11-04 00:41:55 +08:00
|
|
|
endfor
|
2013-08-19 01:22:35 +08:00
|
|
|
|
2017-08-23 05:21:19 +08:00
|
|
|
if empty(heads)
|
2018-06-04 23:31:13 +08:00
|
|
|
if airline#util#has_vcscommand()
|
2018-06-11 20:45:29 +08:00
|
|
|
noa call VCSCommandEnableBufferSetup()
|
2013-11-08 07:36:59 +08:00
|
|
|
if exists('b:VCSCommandBufferInfo')
|
2016-01-29 20:11:14 +08:00
|
|
|
let b:airline_head = s:format_name(get(b:VCSCommandBufferInfo, 0, ''))
|
2013-11-08 07:36:59 +08:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2018-03-10 03:10:45 +08:00
|
|
|
if empty(heads)
|
2018-06-04 23:31:13 +08:00
|
|
|
if airline#util#has_custom_scm()
|
2018-03-10 03:10:45 +08:00
|
|
|
try
|
|
|
|
let Fn = function(g:airline#extensions#branch#custom_head)
|
|
|
|
let b:airline_head = Fn()
|
|
|
|
endtry
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2014-05-22 01:41:53 +08:00
|
|
|
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
|
2016-02-25 18:30:02 +08:00
|
|
|
let b:airline_head = b:airline_head[0:(w:displayed_head_limit - 1)].(&encoding ==? 'utf-8' ? '…' : '.')
|
2014-05-22 01:41:53 +08:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2014-03-25 02:01:31 +08:00
|
|
|
return b:airline_head
|
2013-12-03 13:32:54 +08:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! airline#extensions#branch#get_head()
|
|
|
|
let head = airline#extensions#branch#head()
|
2019-02-20 19:32:55 +08:00
|
|
|
let winwidth = get(airline#parts#get('branch'), 'minwidth', 120)
|
|
|
|
let minwidth = empty(get(b:, 'airline_hunks', '')) ? 14 : 7
|
|
|
|
let head = airline#util#shorten(head, winwidth, minwidth)
|
2014-01-16 12:31:07 +08:00
|
|
|
let symbol = get(g:, 'airline#extensions#branch#symbol', g:airline_symbols.branch)
|
2013-12-03 13:32:54 +08:00
|
|
|
return empty(head)
|
2019-04-24 21:15:29 +08:00
|
|
|
\ ? get(g:, 'airline#extensions#branch#empty_message', '')
|
2014-01-16 12:31:07 +08:00
|
|
|
\ : printf('%s%s', empty(symbol) ? '' : symbol.(g:airline_symbols.space), head)
|
2013-08-06 12:42:59 +08:00
|
|
|
endfunction
|
|
|
|
|
2016-09-29 03:20:37 +08:00
|
|
|
function! s:reset_untracked_cache(shellcmdpost)
|
2016-10-09 21:47:23 +08:00
|
|
|
" shellcmdpost - whether function was called as a result of ShellCmdPost hook
|
2017-08-24 00:10:59 +08:00
|
|
|
if !g:airline#init#vim_async && !has('nvim')
|
2016-09-29 03:20:37 +08:00
|
|
|
if a:shellcmdpost
|
2016-10-09 21:47:23 +08:00
|
|
|
" 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.
|
2016-09-29 03:20:37 +08:00
|
|
|
if v:shell_error
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endif
|
2016-10-30 16:48:24 +08:00
|
|
|
|
2017-08-23 05:21:19 +08:00
|
|
|
let file = expand("%:p")
|
2016-10-30 16:48:24 +08:00
|
|
|
for vcs in keys(s:vcs_config)
|
|
|
|
" Dump the value of the cache for the current file. Partially mitigates the
|
|
|
|
" issue of cache invalidation happening before a call to
|
|
|
|
" s:update_untracked()
|
2017-08-23 05:21:19 +08:00
|
|
|
call airline#extensions#branch#update_untracked_config(file, vcs)
|
2016-10-30 16:48:24 +08:00
|
|
|
let s:vcs_config[vcs].untracked = {}
|
2016-10-23 22:33:31 +08:00
|
|
|
endfor
|
2016-02-03 05:45:47 +08:00
|
|
|
endfunction
|
|
|
|
|
2013-08-06 11:07:01 +08:00
|
|
|
function! airline#extensions#branch#init(ext)
|
2013-08-31 05:51:10 +08:00
|
|
|
call airline#parts#define_function('branch', 'airline#extensions#branch#get_head')
|
2013-09-08 22:03:49 +08:00
|
|
|
|
2017-11-23 02:12:29 +08:00
|
|
|
autocmd ShellCmdPost,CmdwinLeave * unlet! b:airline_head b:airline_do_mq_check
|
|
|
|
autocmd User AirlineBeforeRefresh unlet! b:airline_head b:airline_do_mq_check
|
2016-09-29 03:20:37 +08:00
|
|
|
autocmd BufWritePost * call s:reset_untracked_cache(0)
|
|
|
|
autocmd ShellCmdPost * call s:reset_untracked_cache(1)
|
2013-08-06 11:07:01 +08:00
|
|
|
endfunction
|