Is it possible to call a vim script function with the help of job_start?
Not directly. I guess you would need Vim to be multithreaded, which might be possible in the future.
In fact I've used the command :vimgrep to make my custom grep. It worked. Now I starts to use vim 8 so I'm thinking if it's possible to make my custom grep async.
FWIW, I wrote some code with the same purpose a long time ago:
com -nargs=* Vim call s:vimgrep(<q-args>)
com -nargs=* Lvim call s:vimgrep(<q-args>, v:true)
fu s:vimgrep(args, loclist = v:false) abort
let tempqfl = tempname()
let tempvimrc = tempname()
let get_tempfile =<< trim END
let tempqfl = expand('%:p')
if tempqfl !~# '^/tmp/'
finish
endif
END
let cdcmd = 'cd ' .. getcwd()->fnameescape()
let setcmd = printf('set wildignore=%s suffixes=%s %signorecase %ssmartcase',
\ &wildignore, &suffixes, &ignorecase ? '' : 'no', &smartcase ? '' : 'no')
let args = s:expandargs(a:args)
let vimgrepcmd = 'noa vim ' .. args
let getqfl =<< trim END
eval getqflist()
\ ->map({_, v -> printf('%s:%d:%d:%s',
\ bufname(v.bufnr)->fnamemodify(':p'),
\ v.lnum,
\ v.col,
\ substitute(v.text, '[^[:print:]]', {m -> strtrans(m[0])}, 'g')
\ )})
\ ->writefile(tempqfl, 's')
qa!
END
call writefile(['vim9script']
\ + get_tempfile
\ + [cdcmd, setcmd, vimgrepcmd]
\ + getqfl,
\ tempvimrc, 's')
let vimcmd = printf('vim -es -Nu NONE -U NONE -i NONE -S %s %s', tempvimrc, tempqfl)
let title = (a:loclist ? ':Lvim ' : ':Vim ') .. args
let arglist = [a:loclist, tempqfl, title]
let opts = #{exit_cb: function('s:callback', arglist)}
call split(vimcmd)->job_start(opts)
endfu
fu s:callback(loclist, tempqfl, title, _j, _e) abort
let efm_save = &l:efm
let bufnr = bufnr('%')
try
setl efm=%f:%l:%c:%m
if a:loclist
exe 'lgetfile ' .. a:tempqfl
lw
call setloclist(0, [], 'a', #{title: a:title})
else
exe 'cgetfile ' .. a:tempqfl
cw
call setqflist([], 'a', #{title: a:title})
endif
finally
call setbufvar(bufnr, '&efm', efm_save)
endtry
redraw!
endfu
fu s:expandargs(args) abort
let pat = '^\(\i\@!.\)\1\ze[gj]\{,2}\s\+'
let rep = '/' .. escape(@/, '\/') .. '/'
return substitute(a:args, pat, rep, '')
\ ->substitute('\s\+\zs%\s*$', expand('%:p')->fnameescape(), '')
\ ->substitute('\s\+\zs##\s*$', argv()
\ ->map({_, v -> fnamemodify(v, ':p')->fnameescape()})
\ ->join(), '')
endfu
The code installs 2 custom commands :Vim
and :Lvim
which are some kind of asynchronous wrappers around the builtin :vimgrep
and :lvimgrep
. As an example, you can test it by running this command:
:Vim /pattern/gj $VIMRUNTIME/**
This should asynchronously populate a quickfix list with all the lines where pattern
is written in any file in your $VIMRUNTIME
directory.
It seems to work, but I don't use it regularly, so I don't know how reliable it is.
Re-written in Vim9 script:
vim9script
com -nargs=* Vim Vimgrep(<q-args>)
com -nargs=* Lvim Vimgrep(<q-args>, true)
def Vimgrep(args: string, loclist = false)
var tempvimrc: string = tempname()
var tempqfl: string = tempname()
var get_tempfile: list<string> =<< trim END
var tempqfl = expand('%:p')
if tempqfl !~ '^/tmp/'
finish
endif
END
var cdcmd: string = 'cd ' .. getcwd()->fnameescape()
var setcmd: string = printf('set wildignore=%s suffixes=%s %signorecase %ssmartcase',
&wildignore, &suffixes, &ignorecase ? '' : 'no', &smartcase ? '' : 'no')
var _args: string = Expandargs(args)
var vimgrepcmd: string = 'noa vim ' .. _args
var getqfl: list<string> =<< trim END
getqflist()
->mapnew((_, v: dict<any>): string => printf('%s:%d:%d:%s',
bufname(v.bufnr)->fnamemodify(':p'),
v.lnum,
v.col,
substitute(v.text, '[^[:print:]]', (m: list<string>): string => strtrans(m[0]), 'g')
))
->writefile(tempqfl, 's')
qa!
END
writefile(['vim9script']
+ get_tempfile
+ [cdcmd, setcmd, vimgrepcmd]
+ getqfl,
tempvimrc, 's')
var vimcmd: string = printf('vim -es -Nu NONE -U NONE -i NONE -S %s %s', tempvimrc, tempqfl)
var title: string = (loclist ? ':Lvim ' : ':Vim ') .. _args
var arglist: list<any> = [loclist, tempqfl, title]
var opts: dict<func> = {exit_cb: function(Callback, arglist)}
split(vimcmd)->job_start(opts)
enddef
def Callback(loclist: bool, tempqfl: string, title: string, _j: job, _e: number)
var efm_save: string = &l:efm
var bufnr: number = bufnr('%')
try
setl efm=%f:%l:%c:%m
if loclist
exe 'lgetfile ' .. tempqfl
lw
setloclist(0, [], 'a', {title: title})
else
exe 'cgetfile ' .. tempqfl
cw
setqflist([], 'a', {title: title})
endif
finally
setbufvar(bufnr, '&efm', efm_save)
endtry
redraw!
enddef
def Expandargs(args: string): string
var pat: string = '^\(\i\@!.\)\1\ze[gj]\{,2}\s\+'
var rep: string = '/' .. escape(@/, '\/') .. '/'
return substitute(args, pat, rep, '')
->substitute('\s\+\zs%\s*$', expand('%:p')->fnameescape(), '')
->substitute('\s\+\zs##\s*$', argv()
->map((_, v: string): string => fnamemodify(v, ':p')->fnameescape())
->join(), '')
enddef
This requires a recent Vim version. It works on 8.2.2332.
:vimgrep
to make my customgrep
. It worked. Now I starts to use vim 8 so I'm thinking if it's possible to make my customgrep
async.grep
.