2af2bdb424
This commit fixes a bug, where untracked files in Git repos did not get the not exists symbol displayed. The fix works by replacing the previous check for whether currently edited file is a directory or not with a check based on `findfile`. More detailed explanation follows. VCS extension to vim-airline checks whether currently edited file is untracked. The previous `s:get_git_untracked` implementation, which was used for the aforementioned purpose for Git repos, was buggy, because it did not return the untracked symbol in most situations, i.e. the edited file is untracked, but it was treated as if it wasn't. The root cause was the second clause in boolean expression checking the output of `git status`: if output[0:1] is# '??' && output[3:-2] is? a:file It was added to make sure we are not checking a directory, but at this point in the program `a:file` is an absolute path, while output of `git status` is a relative path from the root of git repo. So the `is?` expression failed in most situations.
291 lines
8.6 KiB
VimL
291 lines
8.6 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# '??'
|
|
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
|
|
return lawrencium#statusline()
|
|
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
|
|
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")
|
|
" Do not get untracked flag if we are modifying a directory.
|
|
let l:is_file_and_not_dir = !isdirectory(l:file)
|
|
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)
|
|
if l:is_file_and_not_dir
|
|
call s:get_git_untracked(l:file)
|
|
let l:heads.git .= get(s:untracked_git, l:file, '')
|
|
endif
|
|
endif
|
|
|
|
if !empty(l:hg_head)
|
|
let l:heads.mercurial = (!empty(l:git_head) ? "hg:" : '') . s:format_name(l:hg_head)
|
|
if l:is_file_and_not_dir
|
|
call s:get_hg_untracked(l:file)
|
|
let l:heads.mercurial.= get(s:untracked_hg, l:file, '')
|
|
endif
|
|
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
|