Sau khi điều tra thêm (và một số kỹ thuật đảo ngược).
Tôi không thể giải thích tại sao hoàn thành không tuân thủ nghiêm ngặt các tài liệu. Tôi sẽ phải hỏi trên vim_dev tôi đoán.
Dù sao, có vẻ như cách để thực hiện nó bao gồm việc đăng ký một người nghe trên CursorMovedI
đó sẽ kích hoạt hoàn thành một lần nữa mỗi khi một ký tự được chèn vào.
Khó khăn là sau đó biết khi nào nên dừng lại.
CompletionDone
không được sử dụng vì nó sẽ được kích hoạt sau mỗi lần nhấn phím.
InsertLeave
là một khởi đầu tốt, nhưng nó không bao gồm tất cả các trường hợp, tức là
- Khi một ký tự không còn khớp nào được nhập, chúng ta phải dừng lại
- Khi người dùng cuối chọn một mục menu, chúng ta cũng phải dừng lại.
Tôi không tìm thấy cách nào khác ngoài việc ghi đè <cr>
, và <c-y>
.
Những khó khăn khác bao gồm phát hiện khi không có gì thay đổi để tránh các vòng lặp vô hạn, v.v.
Dù sao đi nữa, đây là mã hiện tại của tôi (sẽ được sử dụng trong các plugin khác). Các phiên bản cuối cùng sẽ được duy trì ở đây . Nó khá dài, nhưng đây là:
" ## 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
Mà có thể được sử dụng với:
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>
Bạn sẽ có thể quan sát (một cách gián tiếp) kết quả được áp dụng cho lựa chọn đoạn trích C ++ cho plugin mở rộng mẫu của tôi trên screencast này .