autoload/esearch/backend/nvim.vim
if !exists('g:esearch#backend#nvim#ticks')
let g:esearch#backend#nvim#ticks = 3
endif
" Rg 13 requires stdin to be closed, but g:esearch.paths can contain #xargs
" where a pipe is used. TODO improve esearch#backend#{}#init interface
let s:CLOSE_STDIN = g:esearch#has#posix_shell ? ' 0<&-' : ''
let s:NVIM_JOB_IS_INVALID = -3
let s:jobs = {}
fu! esearch#backend#nvim#init(cwd, adapter, command, ...) abort
let is_xargs = get(a:000, 0)
let request = {
\ 'jobstart_args': {
\ 'command': split(&shell) + split(&shellcmdflag) + [a:command . (is_xargs ? '' : s:CLOSE_STDIN)],
\ 'opts': {
\ 'on_stdout': function('s:stdout'),
\ 'on_stderr': function('s:stderr'),
\ 'on_exit': function('s:exit'),
\ 'pty': 0,
\ 'tick': 0,
\ 'ticks': g:esearch#backend#nvim#ticks,
\ },
\ },
\ 'backend': 'nvim',
\ 'adapter': a:adapter,
\ 'command': a:command,
\ 'cwd': a:cwd,
\ 'data': [],
\ 'intermediate': '',
\ 'is_consumed': function('<SID>is_consumed'),
\ 'errors': [],
\ 'finished': 0,
\ 'status': 0,
\ 'cursor': 0,
\ 'async': 1,
\ 'aborted': 0,
\ 'cb': {
\ 'finish': 0,
\ 'update': 0
\ }
\}
return request
endfu
fu! esearch#backend#nvim#exec(request) abort
let cwd = esearch#win#lcd(a:request.cwd)
try
let job_id = jobstart(a:request.jobstart_args.command, a:request.jobstart_args.opts)
let a:request.job_id = job_id
let a:request.start_at = reltime()
call jobclose(job_id, 'stdin')
let s:jobs[job_id] = { 'data': [], 'request': a:request }
finally
call cwd.restore()
endtry
endfu
fu! s:is_consumed(wait) abort dict
let timeout = a:wait - float2nr(reltimefloat(reltime(self.start_at)) * 1000)
if timeout < 0.0 | return 0 | endif
return jobwait([self.job_id], timeout)[0] ==# -1 && self.finished
endfu
" TODO encoding
fu! s:stdout(job_id, data, event) dict abort
let request = s:jobs[a:job_id].request
if !empty(request.intermediate)
let a:data[0] = request.intermediate . a:data[0]
let request.intermediate = ''
endif
let request.intermediate = remove(a:data, -1)
call extend(request.data, a:data)
" Reduce buffer updates to prevent long cursor lock
let self.tick = self.tick + 1
if self.tick % self.ticks == 1 && !empty(request.cb.update)
call request.cb.update()
endif
endfu
fu! s:stderr(job_id, data, event) dict abort
let job = s:jobs[a:job_id]
let data = a:data
if !has_key(job.request, 'errors')
let job.request.errors = []
endif
if !empty(data) && data[0] !=# "\n" && !empty(job.request.errors)
let job.request.errors[-1] .= data[0]
call remove(data, 0)
endif
let errors = filter(data, "'' !=# v:val")
let job.request.errors += errors
if empty(errors) | return | endif
call esearch#stderr#incremental(job.request.adapter, errors)
endfu
fu! s:exit(job_id, status, event) abort
let job = s:jobs[a:job_id]
let job.request.finished = 1
let job.request.status = a:status
if !job.request.aborted && !empty(job.request.cb.finish)
call job.request.cb.finish()
endif
endfu
fu! esearch#backend#nvim#abort(bufnr) abort
" FIXME unify with out#qflist
let esearch = getbufvar(a:bufnr, 'esearch', get(g:, 'esearch_qf', {'request': {}}))
if empty(esearch.request) || esearch.request.aborted | return | endif
let esearch.request.aborted = 1
if has_key(esearch.request, 'job_id') && jobwait([esearch.request.job_id], 0) != [s:NVIM_JOB_IS_INVALID]
try
call jobstop(esearch.request.job_id)
catch /E900:/
" E900: Invalid job id
endtry
endif
endfu