Skip to content
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. ####

Expand Down Expand Up @@ -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
122 changes: 108 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
Copy Markdown
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,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
Expand Down Expand Up @@ -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
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