Penyelesaian yang dinamis


12

Saya mencoba untuk meningkatkan fungsi penyelesaian lama. Saya ingin memperbarui pilihan yang ditampilkan di menu sembulan saat saya mengetik karakter baru

Fungsi penyelesaian saya adalah

function! lh#icomplete#ecm(findstart, base) abort
  if a:findstart
    let l = getline('.')
    let startcol = match(l[0:col('.')-1], '\v\S+$')
    if startcol == -1
      let startcol = col('.')-1
    endif
    " let g:debug+= ["findstart(".a:base.") -> ".(startcol)]
    return startcol
  else
    " let g:debug += ["matching(".a:base.")"]
    let words = ['un', 'deux', 'trois', 'trente-deux', 'unité']
    call filter(words, 'v:val =~ a:base')
    " return { 'words' : words}
    return { 'words' : words, 'refresh' : 'always'}
  endif
endfunction

Yang saya gunakan dengan

:set completefunc=lh#icomplete#ecm
:inoremap µ <c-x><c-u><c-p>

Dari pemahaman saya tentang dokumentasi, fakta yang saya gunakan <c-p>, saya masuk ke status ketiga (sesuai dengan | ins-completion-menu |), dan ketika saya mengetik "karakter apa pun yang dapat dicetak dan tidak putih" saya harus bisa "Tambah karakter ini dan kurangi jumlah kecocokan. "

Ketika saya mengetik dalam mode sisipkan , menu penyelesaian muncul seperti yang diharapkan. Sayangnya ketika saya mengetik x(tepat setelah µ), saya keluar dari mode penyelesaian dan uxapa yang saya dapatkan di buffer saya.

Apa yang saya lakukan salah atau terlewatkan dalam dokumentasi?

NB: Saya sudah melihat bahwa tanpa refresh=always, hasilnya disaring, kecuali saya ingin memanggil fungsi lagi untuk menerapkan filter khusus.

(Untuk berjaga-jaga, saya menggunakan gvim 7.4-908)


Ini seperti bug. Ini juga tidak bekerja untuk saya (saya di Vim 7.4.944).
Karl Yngve Lervåg

Haruskah ini berhasil? Itu yang saya tidak tahu.
Luc Hermitte

Dokumentasi tampaknya menyiratkan bahwa itu harus berfungsi.
Karl Yngve Lervåg

Jawaban:


3

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 CursorMovedIyang 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 .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.