Setelah penyelidikan lebih lanjut (dan beberapa rekayasa balik).
Saya tidak bisa menjelaskan mengapa penyelesaian tidak sepenuhnya mengikuti dokumentasi. Saya harus bertanya pada vim_dev saya kira.
Bagaimanapun, sepertinya cara untuk melakukannya terdiri dari mendaftarkan pendengar CursorMovedI
yang akan memicu penyelesaian lagi setiap kali karakter dimasukkan.
Kesulitannya adalah untuk mengetahui kapan harus berhenti.
CompletionDone
tidak ada gunanya karena akan dipicu setelah setiap tombol hit.
InsertLeave
adalah awal yang baik, tetapi tidak mencakup semua kasus, yaitu
- Ketika karakter yang tidak ada lagi kecocokan diketik, kita harus berhenti
- Ketika pengguna akhir memilih satu item menu, kita juga harus berhenti.
Saya tidak menemukan cara lain selain mengganti <cr>
, dan <c-y>
.
Kesulitan lain terdiri dari mendeteksi ketika tidak ada yang berubah untuk menghindari jenis loop tak terbatas dan seterusnya.
Bagaimanapun, ini adalah kode saya saat ini (yang akan digunakan dalam plugin lain). Versi terakhir akan dipertahankan di sini . Cukup panjang, tapi ini dia:
" ## Smart completion {{{2
" Function: lh#icomplete#new(startcol, matches, hook) {{{3
function! lh#icomplete#new(startcol, matches, hook) abort
silent! unlet b:complete_data
let augroup = 'IComplete'.bufnr('%').'Done'
let b:complete_data = lh#on#exit()
\.restore('&completefunc')
\.restore('&complete')
\.restore('&omnifunc')
\.register('au! '.augroup)
\.register('call self.logger.log("finalized! (".getline(".").")")')
set complete=
let b:complete_data.startcol = a:startcol
let b:complete_data.all_matches = map(copy(a:matches), 'type(v:val)==type({}) ? v:val : {"word": v:val}')
let b:complete_data.matches = {'words': [], 'refresh': 'always'}
let b:complete_data.hook = a:hook
let b:complete_data.cursor_pos = []
let b:complete_data.last_content = [line('.'), getline('.')]
let b:complete_data.no_more_matches = 0
let b:complete_data.logger = s:logger.reset()
" keybindings {{{4
call b:complete_data
\.restore_buffer_mapping('<cr>', 'i')
\.restore_buffer_mapping('<c-y>', 'i')
\.restore_buffer_mapping('<esc>', 'i')
\.restore_buffer_mapping('<tab>', 'i')
inoremap <buffer> <silent> <cr> <c-y><c-\><c-n>:call b:complete_data.conclude()<cr>
inoremap <buffer> <silent> <c-y> <c-y><c-\><c-n>:call b:complete_data.conclude()<cr>
" Unlike usual <tab> behaviour, this time, <tab> inserts the next match
inoremap <buffer> <silent> <tab> <down><c-y><c-\><c-n>:call b:complete_data.conclude()<cr>
" <c-o><Nop> doesn't work as expected...
" To stay in INSERT-mode:
" inoremap <silent> <esc> <c-e><c-o>:<cr>
" To return into NORMAL-mode:
inoremap <buffer> <silent> <esc> <c-e><esc>
" TODO: see to have <Left>, <Right>, <Home>, <End> abort
" Group {{{4
exe 'augroup '.augroup
au!
" Emulate InsertCharPost
" au CompleteDone <buffer> call b:complete_data.logger.log("Completion done")
au InsertLeave <buffer> call b:complete_data.finalize()
au CursorMovedI <buffer> call b:complete_data.cursor_moved()
augroup END
function! s:cursor_moved() abort dict "{{{4
if self.no_more_matches
call self.finalize()
return
endif
if !self.has_text_changed_since_last_move()
call s:logger.log(lh#fmt#printf("cursor %1 just moved (text hasn't changed)", string(getpos('.'))))
return
endif
call s:logger.log(lh#fmt#printf('cursor moved %1 and text has changed -> relaunch completion', string(getpos('.'))))
call feedkeys( "\<C-X>\<C-O>\<C-P>\<Down>", 'n' )
endfunction
let b:complete_data.cursor_moved = function('s:cursor_moved')
function! s:has_text_changed_since_last_move() abort dict "{{{4
let l = line('.')
let line = getline('.')
try
if l != self.last_content[0] " moved vertically
let self.no_more_matches = 1
call s:logger.log("Vertical move => stop")
return 0
" We shall leave complete mode now!
endif
call s:logger.log(lh#fmt#printf("line was: %1, and becomes: %2; has_changed?%3", self.last_content[1], line, line != self.last_content[1]))
return line != self.last_content[1] " text changed
finally
let self.last_content = [l, line]
endtry
endfunction
let b:complete_data.has_text_changed_since_last_move = function('s:has_text_changed_since_last_move')
function! s:complete(findstart, base) abort dict "{{{4
call s:logger.log(lh#fmt#printf('findstart?%1 -> %2', a:findstart, a:base))
if a:findstart
if self.no_more_matches
call s:logger.log("no more matches -> -3")
return -3
call self.finalize()
endif
if self.cursor_pos == getcurpos()
call s:logger.log("cursor hasn't moved -> -2")
return -2
endif
let self.cursor_pos = getcurpos()
return self.startcol
else
return self.get_completions(a:base)
endif
endfunction
let b:complete_data.complete = function('s:complete')
function! s:get_completions(base) abort dict "{{{4
let matching = filter(copy(self.all_matches), 'v:val.word =~ join(split(a:base, ".\\zs"), ".*")')
let self.matches.words = matching
call s:logger.log(lh#fmt#printf("'%1' matches: %2", a:base, string(self.matches)))
if empty(self.matches.words)
call s:logger.log("No more matches...")
let self.no_more_matches = 1
endif
return self.matches
endfunction
let b:complete_data.get_completions = function('s:get_completions')
function! s:conclude() abort dict " {{{4
let selection = getline('.')[self.startcol : col('.')-1]
call s:logger.log("Successful selection of <".selection.">")
if !empty(self.hook)
call lh#function#execute(self.hook, selection)
endif
" call self.hook()
call self.finalize()
endfunction
let b:complete_data.conclude = function('s:conclude')
" Register {{{4
" call b:complete_data
" \.restore('b:complete_data')
" set completefunc=lh#icomplete#func
set omnifunc=lh#icomplete#func
endfunction
" Function: lh#icomplete#new_on(pattern, matches, hook) {{{3
function! lh#icomplete#new_on(pattern, matches, hook) abort
let l = getline('.')
let startcol = match(l[0:col('.')-1], '\v'.a:pattern.'+$')
if startcol == -1
let startcol = col('.')-1
endif
call lh#icomplete#new(startcol, a:matches, a:hook)
endfunction
" Function: lh#icomplete#func(startcol, base) {{{3
function! lh#icomplete#func(findstart, base) abort
return b:complete_data.complete(a:findstart, a:base)
endfunction
Yang dapat digunakan dengan:
let entries = [
\ {'word': 'un', 'menu': 1},
\ {'word': 'deux', 'menu': 2},
\ {'word': 'trois', 'menu': 3},
\ {'word': 'trentre-deux', 'menu': 32},
\ 'unité'
\ ]
inoremap <silent> <buffer> µ <c-o>:call lh#icomplete#new_on('\w', entries, 'lh#common#warning_msg("nominal: ".v:val)')<cr><c-x><c-O><c-p><down>
Anda harus dapat mengamati (secara tidak langsung) hasil yang diterapkan pada pilihan snippet C ++ untuk plugin expander template saya pada screencast ini .