diff --git a/README.md b/README.md index 378fc3e3..a07d6b1f 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,8 @@ Present maintainer, yours truly, [kind of wishes they never forked][sadface], contributes to both, and wouldn't mind seeing them merged again. ag.vim got a nice code clean-up (which ack.vim is now hopefully getting), and ack.vim picked up a few features that haven't made their way to ag.vim, like `:AckWindow`, -optional background search execution with [vim-dispatch], and auto-previewing. +optional background search execution with [vim-dispatch] or [async.vim], and +auto-previewing. #### I don't want to jump to the first result automatically. #### @@ -160,4 +161,5 @@ And of course, where would we be without [ack]. And, you know, Vim. [ack]: http://beyondgrep.com/ [vim-dispatch]: https://github.com/tpope/vim-dispatch +[async.vim]: https://github.com/prabirshrestha/async.vim [releases]: https://github.com/mileszs/ack.vim/releases diff --git a/autoload/ack.vim b/autoload/ack.vim index b6afdba4..128a541e 100644 --- a/autoload/ack.vim +++ b/autoload/ack.vim @@ -2,14 +2,6 @@ if exists('g:autoloaded_ack') || &cp finish endif -if exists('g:ack_use_dispatch') - if g:ack_use_dispatch && !exists(':Dispatch') - call s:Warn('Dispatch not loaded! Falling back to g:ack_use_dispatch = 0.') - let g:ack_use_dispatch = 0 - endif -else - let g:ack_use_dispatch = 0 -endif "----------------------------------------------------------------------------- " Public API @@ -51,18 +43,23 @@ function! ack#Ack(cmd, args) "{{{ " allow for passing arguments etc let l:escaped_args = escape(l:grepargs, '|#%') + let ShowResults = function('s:ShowResults', [l:grepargs]) + echo "Searching ..." if g:ack_use_dispatch call s:SearchWithDispatch(l:grepprg, l:escaped_args, l:grepformat) + elseif g:ack_use_async + call s:SearchWithAsync(l:grepprg, l:escaped_args, l:grepformat, ShowResults) else call s:SearchWithGrep(a:cmd, l:grepprg, l:escaped_args, l:grepformat) endif - " Dispatch has no callback mechanism currently, we just have to display the - " list window early and wait for it to populate :-/ - call ack#ShowResults() - call s:Highlight(l:grepargs) + if !g:ack_use_async + " Dispatch has no callback mechanism currently, we just have to display the + " list window early and wait for it to populate :-/ + call ShowResults() + endif endfunction "}}} function! ack#AckFromSearch(cmd, args) "{{{ @@ -149,6 +146,11 @@ function! s:GetDocLocations() "{{{ return dp endfunction "}}} +function! s:ShowResults(grepargs) "{{{ + call ack#ShowResults() + call s:Highlight(a:grepargs) +endfunction "}}} + function! s:Highlight(args) "{{{ if !g:ackhighlight return @@ -160,8 +162,27 @@ endfunction "}}} " Initialize state for an :Ack* or :LAck* search function! s:Init(cmd) "{{{ - let s:searching_filepaths = (a:cmd =~# '-g$') ? 1 : 0 - let s:using_loclist = (a:cmd =~# '^l') ? 1 : 0 + let s:searching_filepaths = (a:cmd =~# '-g$') ? 1 : 0 + let s:using_loclist = (a:cmd =~# '^l') ? 1 : 0 + let s:using_existing_qfloc_list = (a:cmd =~# '^l\?grepadd') ? 1 : 0 + + if exists('g:ack_use_dispatch') + if g:ack_use_dispatch && !exists(':Dispatch') + call s:Warn('Dispatch not loaded! Falling back to g:ack_use_dispatch = 0.') + let g:ack_use_dispatch = 0 + endif + else + let g:ack_use_dispatch = 0 + endif + + if exists('g:ack_use_async') + if g:ack_use_async && !(&rtp =~ 'async.vim') + call s:Warn('async not loaded! Falling back to g:ack_use_async = 0.') + let g:ack_use_async = 0 + endif + else + let g:ack_use_async = 0 + endif if g:ack_use_dispatch && s:using_loclist call s:Warn('Dispatch does not support location lists! Proceeding with quickfix...') @@ -204,6 +225,70 @@ function! s:SearchWithDispatch(grepprg, grepargs, grepformat) "{{{ endtry endfunction "}}} +function! s:SearchWithAsync(grepprg, grepargs, grepformat, success_cb) "{{{ + " We don't execute a :grep command for Dispatch, so add -g here instead + if s:SearchingFilepaths() + let l:grepprg = a:grepprg . ' -g' + else + let l:grepprg = a:grepprg + endif + + let cmd = l:grepprg . ' ' . a:grepargs + let job_cmd = split(&shell) + split(&shellcmdflag) + [cmd] + let ctx = { + \ 'data': [], + \ 'grepformat': a:grepformat, + \ 'title': ':' . cmd, + \ 'success_cb': a:success_cb, + \} + + function! ctx.append(job_id, data, event_type) "{{{ + let self.ctx.data = self.ctx.data + a:data + endfunction "}}} + + function! ctx.complete(job_id, data, event_type) " {{{ + " Later on we are using {c,l}addexpr and {c,l}getexpr to populate + " the quickfix/location-list, and these use the global errorformat so + " that's what needs to be temporarily changed. + " https://github.com/vim/vim/issues/569 + let l:errorformat_bak = &errorformat + + try + let &errorformat = self.ctx.grepformat + let entries = filter(self.ctx.data, 'len(v:val)') + + if s:UsingLocList() + if s:UsingExistingQFLocList() + laddexpr entries + else + lgetexpr entries + endif + call setloclist(0, [], 'a', { 'title': self.ctx.title }) + else + if s:UsingExistingQFLocList() + caddexpr entries + else + cgetexpr entries + endif + call setqflist([], 'a', { 'title': self.ctx.title }) + endif + finally + let &errorformat = errorformat_bak + endtry + call self.ctx.success_cb() + endfunction "}}} + + let jobid = async#job#start(job_cmd, { + \ 'on_stdout': ctx.append, + \ 'on_exit': ctx.complete, + \ 'ctx': ctx, + \ }) + + if jobid <= 0 + call s:Error('Failed to run the following command: ' . string(job_cmd)) + endif +endfunction "}}} + function! s:SearchWithGrep(grepcmd, grepprg, grepargs, grepformat) "{{{ let l:grepprg_bak = &l:grepprg let l:grepformat_bak = &grepformat @@ -238,9 +323,18 @@ function! s:UsingLocList() "{{{ return get(s:, 'using_loclist', 0) endfunction "}}} +" Were we invoked with a :AckAdd or :LackAdd command? +function! s:UsingExistingQFLocList() " {{{ + return get(s:, 'using_existing_qfloc_list', 0) +endfunction "}}} + function! s:Warn(msg) "{{{ echohl WarningMsg | echomsg 'Ack: ' . a:msg | echohl None endf "}}} +function! s:Error(msg) "{{{ + echohl ErrorMsg | echomsg 'Ack: ' . a:msg | echohl None +endf "}}} + let g:autoloaded_ack = 1 " vim:set et sw=2 ts=2 tw=78 fdm=marker diff --git a/doc/ack.txt b/doc/ack.txt index 22e884bc..95245b5d 100644 --- a/doc/ack.txt +++ b/doc/ack.txt @@ -219,6 +219,17 @@ acceptable tradeoffs for very large projects where searches are slow. Example: > let g:ack_use_dispatch = 1 +< + *g:ack_use_async* +g:ack_use_async +Default: 0 + +Use this option to use async.vim to run searches in the background, leveraging +async job support added to VIM 8. + +Example: +> + let g:ack_use_async = 1 < *g:ack_use_cword_for_empty_search* g:ack_use_cword_for_empty_search