diff --git a/autoload/airline/async.vim b/autoload/airline/async.vim new file mode 100644 index 0000000..b7f9507 --- /dev/null +++ b/autoload/airline/async.vim @@ -0,0 +1,242 @@ +" MIT License. Copyright (c) 2013-2017 C.Brabandt +" vim: et ts=2 sts=2 sw=2 + +let s:untracked_jobs = {} +let s:mq_jobs = {} +let s:po_jobs = {} + +" Generic functions handling on exit event of the various async functions +function! s:untracked_output(dict, buf) + if a:buf =~? ('^'. a:dict.cfg['untracked_mark']) + let a:dict.cfg.untracked[a:dict.file] = get(g:, 'airline#extensions#branch#notexists', g:airline_symbols.notexists) + else + let a:dict.cfg.untracked[a:dict.file] = '' + endif +endfunction + +function! s:mq_output(buf, file) + let buf='' + if !empty(a:buf) + if a:buf is# 'no patches applied' || + \ a:buf =~# "unknown command 'qtop'" + let buf = '' + elseif exists("b:mq") && b:mq isnot# a:buf + " make sure, statusline is updated + unlet! b:airline_head + endif + let b:mq = a:buf + endif + if has_key(s:mq_jobs, a:file) + call remove(s:mq_jobs, a:file) + endif +endfunction + +function! s:po_output(buf, file) + if !empty(a:buf) + let b:airline_po_stats = printf("[%s]", a:buf) + else + let b:airline_po_stats = '' + endif + if has_key(s:po_jobs, a:file) + call remove(s:po_jobs, a:file) + endif +endfunction + +if v:version >= 800 && has("job") + " Vim 8.0 with Job feature + + function! s:on_stdout(channel, msg) dict abort + let self.buf .= a:msg + endfunction + + function! s:on_exit_mq(channel) dict abort + call s:mq_output(self.buf, self.file) + endfunction + + function! s:on_exit_untracked(channel) dict abort + call s:untracked_output(self, self.buf) + if has_key(s:untracked_jobs, self.file) + call remove(s:untracked_jobs, self.file) + endif + endfunction + + function! s:on_exit_po(channel) dict abort + call s:po_output(self.buf, self.file) + call airline#extensions#po#shorten() + endfunction + + function! airline#async#get_mq_async(cmd, file) + if g:airline#init#is_windows && &shell =~ 'cmd' + let cmd = a:cmd + else + let cmd = ['sh', '-c', a:cmd] + endif + + let options = {'cmd': a:cmd, 'buf': '', 'file': a:file} + if has_key(s:mq_jobs, a:file) + if job_status(get(s:mq_jobs, a:file)) == 'run' + return + elseif has_key(s:mq_jobs, a:file) + call remove(s:mq_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:mq_jobs[a:file] = id + endfunction + + function! airline#async#get_msgfmt_stat(cmd, file) + if g:airline#init#is_windows || !executable('msgfmt') + " no msgfmt on windows? + return + else + let cmd = ['sh', '-c', a:cmd. shellescape(a:file)] + endif + + let options = {'buf': '', 'file': a:file} + if has_key(s:po_jobs, a:file) + if job_status(get(s:po_jobs, a:file)) == 'run' + return + elseif has_key(s:po_jobs, a:file) + call remove(s:po_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_po', options)}) + let s:po_jobs[a:file] = id + endfunction + + function airline#async#vim_vcs_untracked(config, file) + if g:airline#init#is_windows && &shell =~ 'cmd' + let cmd = a:config['cmd'] . shellescape(a:file) + else + let cmd = ['sh', '-c', a:config['cmd'] . shellescape(a:file)] + endif + + let options = {'cfg': a:config, 'buf': '', 'file': a:file} + if has_key(s:untracked_jobs, a:file) + if job_status(get(s:untracked_jobs, a:file)) == 'run' + return + elseif has_key(s:untracked_jobs, a:file) + call remove(s:untracked_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_untracked', options)}) + let s:untracked_jobs[a:file] = id + endfunction + +elseif has("nvim") + " NVim specific functions + + function! s:nvim_untracked_job_handler(job_id, data, event) dict + if a:event == 'stdout' + let self.buf .= join(a:data) + else " on_exit handler + call s:untracked_output(self, self.buf) + if has_key(s:untracked_jobs, self.file) + call remove(s:untracked_jobs, self.file) + endif + endif + endfunction + + function! s:nvim_mq_job_handler(job_id, data, event) dict + if a:event == 'stdout' + let self.buf .= join(a:data) + else " on_exit handler + call s:mq_output(self.buf, self.file) + endif + endfunction + + function! s:nvim_po_job_handler(job_id, data, event) dict + if a:event == 'stdout' + let self.buf .= join(a:data) + elseif a:event == 'stderr' + let self.buf .= join(a:data) + else " on_exit handler + call s:po_output(self.buf, self.file) + call airline#extensions#po#shorten() + endif + endfunction + + function! airline#async#nvim_get_mq_async(cmd, file) + let config = { + \ 'buf': '', + \ 'file': a:file, + \ 'cwd': fnamemodify(a:file, ':p:h'), + \ 'on_stdout': function('s:nvim_mq_job_handler'), + \ 'on_exit': function('s:nvim_mq_job_handler') + \ } + if g:airline#init#is_windows && &shell =~ 'cmd' + let cmd = a:cmd + else + let cmd = ['sh', '-c', a:cmd] + endif + + if has_key(s:mq_jobs, a:file) + call remove(s:mq_jobs, a:file) + endif + let id = jobstart(cmd, config) + let s:mq_jobs[a:file] = id + endfunction + + function! airline#async#nvim_get_msgfmt_stat(cmd, file) + let config = { + \ 'buf': '', + \ 'file': a:file, + \ 'cwd': fnamemodify(a:file, ':p:h'), + \ 'on_stdout': function('s:nvim_po_job_handler'), + \ 'on_stderr': function('s:nvim_po_job_handler'), + \ 'on_exit': function('s:nvim_po_job_handler') + \ } + if g:airline#init#is_windows && &shell =~ 'cmd' + " no msgfmt on windows? + return + else + let cmd = ['sh', '-c', a:cmd. shellescape(a:file)] + endif + + if has_key(s:po_jobs, a:file) + call remove(s:po_jobs, a:file) + endif + let id = jobstart(cmd, config) + let s:po_jobs[a:file] = id + endfunction + +endif + +" Should work in either Vim pre 8 or Nvim +function! airline#async#nvim_vcs_untracked(cfg, file, vcs) + let config = { + \ 'buf': '', + \ 'vcs': a:vcs, + \ 'cfg': a:cfg, + \ 'file': a:file, + \ 'cwd': fnamemodify(a:file, ':p:h'), + \ 'on_stdout': function('s:nvim_untracked_job_handler'), + \ 'on_exit': function('s:nvim_untracked_job_handler') + \ } + let cmd = a:cfg.cmd . shellescape(a:file) + if !has("nvim") + let id = -1 + else + if has_key(s:untracked_jobs, config.file) + " still running + return + endif + let id = jobstart(cmd, config) + let s:untracked_jobs[a:file] = id + endif + " vim without job feature or nvim jobstart failed + if id < 1 + let output=system(cmd) + call s:untracked_output(config, output) + call airline#extensions#branch#update_untracked_config(a:file, a:vcs) + endif +endfunction diff --git a/autoload/airline/extensions/branch.vim b/autoload/airline/extensions/branch.vim index 81fe278..c681b99 100644 --- a/autoload/airline/extensions/branch.vim +++ b/autoload/airline/extensions/branch.vim @@ -122,16 +122,32 @@ function! s:update_hg_branch(...) " path argument is not actually used, so we don't actually care about a:1 " it is just needed, because update_git_branch needs it. if s:has_lawrencium + let cmd='LC_ALL=C hg qtop' let stl=lawrencium#statusline() - if !empty(stl) && g:airline#init#async && get(b:, 'airline_do_mq_check', 1) - call s:get_mq_async('LC_ALL=C hg qtop', expand('%:p')) + if !empty(stl) && get(b:, 'airline_do_mq_check', 1) + if g:airline#init#vim_async + call airline#async#get_mq_async(cmd, expand('%:p')) + elseif has("nvim") + call airline#async#nvim_get_mq_async(cmd, expand('%:p')) + else + let output=system(cmd) + if output is# 'no patches applied' || + \ output =~# "unknown command 'qtop'" + let b:mq='' + else + unlet! b:airline_head + let b:mq = output + endif + endif endif - if exists("s:mq") && !empty(s:mq) + " do not do mq check anymore + let b:airline_do_mq_check = 0 + if exists("b:mq") && !empty(b:mq) if stl is# 'default' " Shorten default a bit let stl='def' endif - let stl.=' ['.s:mq.']' + let stl.=' ['.b:mq.']' endif let s:vcs_config['mercurial'].branch = stl else @@ -151,7 +167,7 @@ function! s:update_branch() endfor endfunction -function! s:update_untracked_in_buffer_config(file, vcs) +function! airline#extensions#branch#update_untracked_config(file, vcs) 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 @@ -161,131 +177,43 @@ function! s:update_untracked_in_buffer_config(file, vcs) endfunction function! s:update_untracked() - let l:file = expand("%:p") - if empty(l:file) || isdirectory(l:file) + let file = expand("%:p") + if empty(file) || isdirectory(file) return endif - let l:needs_update = 1 + let needs_update = 1 for vcs in keys(s:vcs_config) - if l:file =~ s:vcs_config[vcs].exclude + if file =~ s:vcs_config[vcs].exclude " Skip check for files that live in the exclude directory - let l:needs_update = 0 + let needs_update = 0 endif - if has_key(s:vcs_config[vcs].untracked, l:file) - let l:needs_update = 0 - call s:update_untracked_in_buffer_config(l:file, vcs) + if has_key(s:vcs_config[vcs].untracked, file) + let needs_update = 0 + call airline#extensions#branch#update_untracked_config(file, vcs) endif endfor - if !l:needs_update + if !needs_update return endif for vcs in keys(s:vcs_config) - let l:config = s:vcs_config[vcs] - if g:airline#init#async + let config = s:vcs_config[vcs] + if g:airline#init#vim_async " 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. - call s:get_vcs_untracked_async(l:config, l:file) + call airline#async#vim_vcs_untracked(config, file) else - let output = airline#util#system(l:config.cmd . shellescape(l:file)) - if output =~? ('^' . l:config.untracked_mark) - let l:config.untracked[l:file] = get(g:, 'airline#extensions#branch#notexists', g:airline_symbols.notexists) - else - let l:config.untracked[l:file] = '' - endif - call s:update_untracked_in_buffer_config(l:file, vcs) + " nvim async or vim without job-feature + call airline#async#nvim_vcs_untracked(config, file, vcs) endif endfor endfunction -if g:airline#init#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 - if self.buf =~? ('^' . self.config['untracked_mark']) - let self.config.untracked[self.file] = get(g:, 'airline#extensions#branch#notexists', g:airline_symbols.notexists) - else - let self.config.untracked[self.file] = '' - endif - " b:buffer_vcs_config will be updated on next call of update_untracked if - " needed - if has_key(s:jobs, self.file) - call remove(s:jobs, self.file) - endif - endfunction - - function! s:get_vcs_untracked_async(config, file) - if g:airline#init#is_windows && &shell =~ 'cmd' - let cmd = a:config['cmd'] . shellescape(a:file) - else - let cmd = ['sh', '-c', a:config['cmd'] . shellescape(a:file)] - endif - - let options = {'config': a:config, '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 - " do not do mq check anymore - let b:airline_do_mq_check = 0 - 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#init#is_windows && &shell =~ 'cmd' - let cmd = a:cmd - 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:buffer_vcs_config') call s:init_buffer() @@ -299,24 +227,24 @@ function! airline#extensions#branch#head() endif let b:airline_head = '' - let l:vcs_priority = get(g:, "airline#extensions#branch#vcs_priority", ["git", "mercurial"]) + let vcs_priority = get(g:, "airline#extensions#branch#vcs_priority", ["git", "mercurial"]) - let l:heads = {} - for vcs in l:vcs_priority + let heads = {} + for vcs in vcs_priority if !empty(b:buffer_vcs_config[vcs].branch) - let l:heads[vcs] = b:buffer_vcs_config[vcs].branch + let heads[vcs] = b:buffer_vcs_config[vcs].branch endif endfor - for vcs in keys(l:heads) + for vcs in keys(heads) if !empty(b:airline_head) let b:airline_head .= ' | ' endif - let b:airline_head .= (len(l:heads) > 1 ? s:vcs_config[l:vcs].exe .':' : '') . s:format_name(l:heads[l:vcs]) + let b:airline_head .= (len(heads) > 1 ? s:vcs_config[vcs].exe .':' : '') . s:format_name(heads[vcs]) let b:airline_head .= b:buffer_vcs_config[vcs].untracked endfor - if empty(l:heads) + if empty(heads) if s:has_vcscommand call VCSCommandEnableBufferSetup() if exists('b:VCSCommandBufferInfo') @@ -332,7 +260,7 @@ function! airline#extensions#branch#head() endif endif - if has_key(l:heads, 'git') && !s:check_in_path() + if has_key(heads, 'git') && !s:check_in_path() let b:airline_head = '' endif let minwidth = empty(get(b:, 'airline_hunks', '')) ? 14 : 7 @@ -380,7 +308,7 @@ endfunction function! s:reset_untracked_cache(shellcmdpost) " shellcmdpost - whether function was called as a result of ShellCmdPost hook - if !g:airline#init#async && !has('nvim') + if !g:airline#init#vim_async && !has('nvim') if a:shellcmdpost " Clear cache only if there was no error or the script uses an " asynchronous interface. Otherwise, cache clearing would overwrite @@ -391,12 +319,12 @@ function! s:reset_untracked_cache(shellcmdpost) endif endif - let l:file = expand("%:p") + let file = expand("%:p") 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() - call s:update_untracked_in_buffer_config(l:file, l:vcs) + call airline#extensions#branch#update_untracked_config(file, vcs) let s:vcs_config[vcs].untracked = {} endfor endfunction diff --git a/autoload/airline/extensions/po.vim b/autoload/airline/extensions/po.vim index 1281d5a..65beda7 100644 --- a/autoload/airline/extensions/po.vim +++ b/autoload/airline/extensions/po.vim @@ -3,7 +3,7 @@ scriptencoding utf-8 -function! s:shorten() +function! airline#extensions#po#shorten() if exists("g:airline#extensions#po#displayed_limit") let w:displayed_po_limit = g:airline#extensions#po#displayed_limit if len(b:airline_po_stats) > w:displayed_po_limit - 1 @@ -12,49 +12,6 @@ function! s:shorten() endif endfunction -if g:airline#init#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 - if !empty(self.buf) - let b:airline_po_stats = printf("[%s]", self.buf) - else - let b:airline_po_stats = '' - endif - if has_key(s:jobs, self.file) - call remove(s:jobs, self.file) - endif - call s:shorten() - endfunction - - function! s:get_msgfmt_stat_async(cmd, file) - if g:airline#init#is_windows || !executable('msgfmt') - " no msgfmt on windows? - return - else - let cmd = ['sh', '-c', a:cmd. shellescape(a:file)] - endif - - let options = {'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#po#apply(...) if &ft ==# 'po' call airline#extensions#prepend_to_section('z', '%{airline#extensions#po#stats()}') @@ -68,8 +25,10 @@ function! airline#extensions#po#stats() endif let cmd = 'msgfmt --statistics -o /dev/null -- ' - if g:airline#init#async - call s:get_msgfmt_stat_async(cmd, expand('%:p')) + if g:airline#init#vim_async + call airline#async#get_msgfmt_stat(cmd, expand('%:p')) + elseif has("nvim") + call airline#async#nvim_get_msgfmt_stat(cmd, expand('%:p')) else let airline_po_stats = system(cmd. shellescape(expand('%:p'))) if v:shell_error @@ -81,7 +40,7 @@ function! airline#extensions#po#stats() catch let b:airline_po_stats = '' endtry - call s:shorten() + call airline#extensions#po#shorten() endif return get(b:, 'airline_po_stats', '') endfunction diff --git a/autoload/airline/init.vim b/autoload/airline/init.vim index fbace84..1343a70 100644 --- a/autoload/airline/init.vim +++ b/autoload/airline/init.vim @@ -18,7 +18,7 @@ function! airline#init#bootstrap() let g:airline#init#bootstrapping = 1 - let g:airline#init#async = (v:version >= 800 && has('job')) + let g:airline#init#vim_async = (v:version >= 800 && has('job')) let g:airline#init#is_windows = has('win32') || has('win64') call s:check_defined('g:airline_detect_modified', 1) diff --git a/autoload/airline/util.vim b/autoload/airline/util.vim index 3f94086..06cba7a 100644 --- a/autoload/airline/util.vim +++ b/autoload/airline/util.vim @@ -86,31 +86,3 @@ else return 0 endfunction endif - -" Define a wrapper over system() that uses nvim's async job control if -" available. This way we avoid overwriting v:shell_error, which might -" potentially disrupt other plugins. -if has('nvim') - function! s:system_job_handler(job_id, data, event) dict - if a:event == 'stdout' - let self.buf .= join(a:data) - endif - endfunction - - function! airline#util#system(cmd) - let l:config = { - \ 'buf': '', - \ 'on_stdout': function('s:system_job_handler'), - \ } - let l:id = jobstart(a:cmd, l:config) - if l:id < 1 - return system(a:cmd) - endif - call jobwait([l:id]) - return l:config.buf - endfunction -else - function! airline#util#system(cmd) - return system(a:cmd) - endfunction -endif