Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async support #239

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
118 changes: 104 additions & 14 deletions autoload/ack.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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.')
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was failing before as s:Warn was defined only at the end of the script, so I figured we could move this (and the additional check I added for async) inside s:Init()

let g:ack_use_dispatch = 0
endif
else
let g:ack_use_dispatch = 0
endif

"-----------------------------------------------------------------------------
" Public API
Expand Down Expand Up @@ -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) "{{{
Expand Down Expand Up @@ -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
Expand All @@ -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...')
Expand Down Expand Up @@ -204,6 +225,66 @@ 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) " {{{
let l:errorformat_bak = &l:errorformat

try
let &l: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 &l: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
Expand Down Expand Up @@ -238,9 +319,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
11 changes: 11 additions & 0 deletions doc/ack.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down