dotfiles/vim/plugins/deoplete.nvim/autoload/deoplete/handler.vim
2018-04-05 13:06:54 +02:00

287 lines
7.9 KiB
VimL

"=============================================================================
" FILE: handler.vim
" AUTHOR: Shougo Matsushita <Shougo.Matsu at gmail.com>
" License: MIT license
"=============================================================================
function! deoplete#handler#_init() abort
augroup deoplete
autocmd!
autocmd InsertLeave * call s:on_insert_leave()
autocmd CompleteDone * call s:on_complete_done()
autocmd InsertLeave * call s:completion_timer_stop()
augroup END
for event in ['InsertEnter', 'BufWritePost', 'DirChanged']
call s:define_on_event(event)
endfor
call s:define_completion_via_timer('TextChangedI')
if g:deoplete#enable_on_insert_enter
call s:define_completion_via_timer('InsertEnter')
endif
if g:deoplete#enable_refresh_always
call s:define_completion_via_timer('InsertCharPre')
endif
" Note: Vim 8 GUI is broken
" dummy timer call is needed before complete()
if !has('nvim') && has('gui_running')
let s:dummy_timer = timer_start(200, {timer -> 0}, {'repeat': -1})
endif
if deoplete#util#has_yarp()
" To fix "RuntimeError: Event loop is closed" issue
" Note: Workaround
autocmd deoplete VimLeavePre * call s:kill_yarp()
endif
endfunction
function! deoplete#handler#_do_complete() abort
let context = g:deoplete#_context
let event = get(context, 'event', '')
let modes = (event ==# 'InsertEnter') ? ['n', 'i'] : ['i']
if s:is_exiting() || index(modes, mode()) < 0
call s:completion_timer_stop()
return
endif
if empty(get(context, 'candidates', []))
\ || deoplete#util#get_input(context.event) !=# context.input
return
endif
let prev = g:deoplete#_prev_completion
let prev.event = context.event
let prev.candidates = context.candidates
let prev.complete_position = getpos('.')
if context.event ==# 'Manual'
let context.event = ''
elseif !exists('g:deoplete#_saved_completeopt')
call deoplete#mapping#_set_completeopt()
endif
if g:deoplete#complete_method ==# 'complete'
call feedkeys("\<Plug>_", 'i')
elseif g:deoplete#complete_method ==# 'completefunc'
let &l:completefunc = 'deoplete#mapping#_completefunc'
call feedkeys("\<C-x>\<C-u>", 'in')
elseif g:deoplete#complete_method ==# 'omnifunc'
let &l:omnifunc = 'deoplete#mapping#_completefunc'
call feedkeys("\<C-x>\<C-o>", 'in')
endif
endfunction
function! s:completion_timer_start(event) abort
if exists('s:completion_timer')
call s:completion_timer_stop()
endif
let delay = max([20, g:deoplete#auto_complete_delay])
let s:completion_timer = timer_start(
\ delay, {-> s:completion_begin(a:event)})
endfunction
function! s:completion_timer_stop() abort
if !exists('s:completion_timer')
return
endif
call timer_stop(s:completion_timer)
unlet s:completion_timer
endfunction
function! deoplete#handler#_async_timer_start() abort
if exists('s:async_timer')
call deoplete#handler#_async_timer_stop()
endif
let s:async_timer = { 'event': 'Async', 'changedtick': b:changedtick }
let s:async_timer.id = timer_start(
\ max([20, g:deoplete#auto_refresh_delay]),
\ function('s:completion_async'), {'repeat': -1})
endfunction
function! deoplete#handler#_async_timer_stop() abort
if exists('s:async_timer')
call timer_stop(s:async_timer.id)
unlet s:async_timer
endif
endfunction
function! s:completion_async(timer) abort
if mode() !=# 'i' || s:is_exiting()
call deoplete#handler#_async_timer_stop()
return
endif
call s:completion_begin(s:async_timer.event)
endfunction
function! s:completion_begin(event) abort
if s:is_skip(a:event)
call deoplete#mapping#_restore_completeopt()
let g:deoplete#_context.candidates = []
return
endif
let context = deoplete#init#_context(a:event, [])
if s:check_omnifunc(context)
return
endif
call deoplete#util#rpcnotify(
\ 'deoplete_auto_completion_begin', context)
endfunction
function! s:is_skip(event) abort
if s:is_skip_text(a:event)
return 1
endif
let disable_auto_complete =
\ deoplete#util#get_simple_buffer_config(
\ 'b:deoplete_disable_auto_complete',
\ 'g:deoplete#disable_auto_complete')
if &paste
\ || (a:event !=# 'Manual' && a:event !=# 'Async'
\ && disable_auto_complete)
\ || (&l:completefunc !=# '' && &l:buftype =~# 'nofile')
\ || (a:event !=# 'InsertEnter' && mode() !=# 'i')
return 1
endif
return 0
endfunction
function! s:is_skip_text(event) abort
let context = g:deoplete#_context
let input = deoplete#util#get_input(a:event)
if has_key(context, 'input')
\ && a:event !=# 'Manual'
\ && a:event !=# 'Async'
\ && input ==# context.input
return 1
endif
let displaywidth = strdisplaywidth(input) + 1
if &l:formatoptions =~# '[tca]' && &l:textwidth > 0
\ && displaywidth >= &l:textwidth
if &l:formatoptions =~# '[ta]'
\ || !empty(filter(deoplete#util#get_syn_names(),
\ "v:val ==# 'Comment'"))
return 1
endif
endif
let skip_chars = deoplete#util#get_simple_buffer_config(
\ 'b:deoplete_skip_chars', 'g:deoplete#skip_chars')
return (!pumvisible() && virtcol('.') != displaywidth)
\ || (a:event !=# 'Manual' && input !=# ''
\ && index(skip_chars, input[-1:]) >= 0)
endfunction
function! s:check_omnifunc(context) abort
let prev = g:deoplete#_prev_completion
let blacklist = ['LanguageClient#complete']
if prev.event ==# 'Manual'
\ || &l:omnifunc ==# ''
\ || index(blacklist, &l:omnifunc) >= 0
\ || prev.complete_position ==# getpos('.')
return
endif
for filetype in a:context.filetypes
for pattern in deoplete#util#convert2list(
\ deoplete#util#get_buffer_config(filetype,
\ 'b:deoplete_omni_patterns',
\ 'g:deoplete#omni_patterns',
\ 'g:deoplete#_omni_patterns'))
if pattern !=# '' && a:context.input =~# '\%('.pattern.'\)$'
let g:deoplete#_context.candidates = []
let prev.event = a:context.event
let prev.candidates = []
let prev.complete_position = getpos('.')
call deoplete#mapping#_set_completeopt()
call feedkeys("\<C-x>\<C-o>", 'in')
return 1
endif
endfor
endfor
endfunction
function! s:define_on_event(event) abort
if !exists('##' . a:event)
return
endif
execute 'autocmd deoplete' a:event
\ '* call deoplete#send_event('.string(a:event).')'
endfunction
function! s:define_completion_via_timer(event) abort
if !exists('##' . a:event)
return
endif
execute 'autocmd deoplete' a:event
\ '* call s:completion_timer_start('.string(a:event).')'
endfunction
function! s:on_insert_leave() abort
call deoplete#mapping#_restore_completeopt()
let g:deoplete#_context = {}
let g:deoplete#_prev_completion = {
\ 'complete_position': [],
\ 'candidates': [],
\ 'event': '',
\ }
endfunction
function! s:on_complete_done() abort
if get(v:completed_item, 'word', '') ==# ''
return
endif
let word = v:completed_item.word
if !has_key(g:deoplete#_rank, word)
let g:deoplete#_rank[word] = 1
else
let g:deoplete#_rank[word] += 1
endif
endfunction
function! deoplete#handler#_skip_next_completion() abort
if !exists('g:deoplete#_context')
return
endif
let input = deoplete#util#get_input('CompleteDone')
if input[-1:] !=# '/'
let g:deoplete#_context.input = input
endif
endfunction
function! s:is_exiting() abort
return exists('v:exiting') && v:exiting != v:null
endfunction
function! s:kill_yarp() abort
if g:deoplete#_yarp.job_is_dead
return
endif
let job = g:deoplete#_yarp.job
if !has('nvim') && !exists('g:yarp_jobstart')
" Get job object from vim-hug-neovim-rpc
let job = g:_neovim_rpc_jobs[job].job
endif
if has('nvim')
call jobstop(job)
else
call job_stop(job, 'kill')
endif
let g:deoplete#_yarp.job_is_dead = 1
endfunction