Bagaimana cara membuat fungsi autocomplete saya sendiri?

22

Bagaimana cara membuat daftar pelengkapan otomatis saya sendiri untuk tipe file tertentu?

Sebagai contoh saya ingin css dan html untuk melengkapi otomatis dari daftar kelas css di FontAwesome .

dipecat
sumber

Jawaban:

22

Penyelesaian kamus akan menjadi solusi yang murah dan tidak mengganggu:

  1. simpan fontawesome.txt di suatu tempat di mesin Anda,

  2. masukkan ini after/ftplugin/css.vim(buat file jika tidak ada):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. memulai sesi baru atau lakukan :edi buffer CSS untuk secara paksa mencari file di atas,

  4. tekan <C-n>atau <C-p>dalam mode sisipkan,

  5. Nikmati!

    penyelesaian kamus

Referensi:

:help ins-completion
:help 'complete'
:help 'dictionary'
:help 'iskeyword'
romainl
sumber
17

EDIT Terima kasih atas komentar romainl Saya telah mengedit jawaban saya untuk menunjukkan cara membuat fungsi penyelesaian yang ditentukan pengguna. Pada versi sebelumnya saya menimpa buil-in omni-completion yang tidak baik karena pengguna akan kehilangan penyelesaian default yang cukup kuat.


Solusi vimscript

Salah satu solusinya adalah menggunakan vimscript dan fakta bahwa vim memungkinkan Anda membuat fungsi penyelesaian yang disesuaikan.

Keuntungan dari solusi ini adalah Anda tidak memerlukan plugin tambahan. Anda cukup membuat fungsi penyelesaian yang ditentukan pengguna dan menggunakan fitur penyelesaian bawaan.

Saya akan menggunakan contoh Anda dari kelas fontAwesome dalam cssfile:

Buat file ~/.vim/autoload/csscomplete.vimdan letakkan baris berikut di dalamnya:

let s:matches=".fa-lg .fa-2x .fa-3x .fa-4x .fa-5x .fa-fw .fa-ul .fa-ul .fa-li .fa-li.fa-lg .fa-border .fa-pull-left .fa-pull-right .fa.fa-pull-left"

function! csscomplete#CompleteFA(findstart, base)
    if a:findstart
        " locate the start of the word
        let line = getline('.')
        let start = col('.') - 1
        while start > 0 && (line[start - 1] =~ '\a' || line[start - 1] =~ '.' || line[start - 1] =~ '-')
            let start -= 1
        endwhile
        return start
    else
        " find classes matching "a:base"
        let res = []
        for m in split(s:matches)
            if m =~ '^' . a:base
                call add(res, m)
            endif
        endfor
        return res
    endif
endfun

Kemudian buat file ~/.vim/after/ftplugin/css.vimdan letakkan di dalamnya baris berikut:

setlocal completefunc=csscomplete#CompleteFA

Kemudian Anda dapat memicu fungsi penyelesaian yang ditentukan pengguna dengan <C-x><C-u>. Ini akan mencoba menemukan kecocokan dengan kata yang diketik terakhir.

Di tangkapan layar saya mengetik .fa-ldan kemudian memicu fungsi dengan <C-x><C-u>:

masukkan deskripsi gambar di sini


Bagaimana cara kerjanya?

Pertama di sini adalah beberapa topik dokumentasi yang relevan:

Jika Anda ingin membuat penyelesaian kustom untuk jenis file tertentu, Anda harus memasukkannya ke dalam file $HOME/.vim/autoload/{FILETYPE}complete.vim.

Kemudian dalam file ini Anda harus membuat fungsi penyelesaian Anda yang disebut dua kali:

  • Pada panggilan pertama, argumen pertamanya adalah posisi kursor saat ini dan fungsinya akan menentukan kata yang harus diselesaikan. Dalam fungsi saya, saya menggunakan 3 perbandingan untuk mendapatkan kata yang harus diselesaikan karena kelas dapat terdiri dari huruf, .dan - (saya pikir mungkin untuk meningkatkan pencocokan ini, tetapi saya benar-benar buruk dengan regex)

  • Pada panggilan kedua argumen kedua adalah kata yang cocok dan kemudian fungsinya membandingkannya dengan daftar kelas yang mungkin cocok dengan 1 . Di sini Anda melihat bahwa saya mengembalikan kamus yang akan mengisi menu penyelesaian tetapi ketika Anda akan membaca dokumentasi Anda akan melihat bahwa Anda dapat membuat kamus yang jauh lebih kompleks untuk menyesuaikan lebih banyak lagi perilaku fungsi Anda.

Anda juga harus memberi tahu Vim "untuk file jenis ini, gunakan fungsi lengkap yang saya buat ini". Untuk melakukannya, Anda harus mengatur completefuncke nama fungsi yang Anda buat (di sini csscomplete#CompleteFA) dan pengaturan ini harus dilakukan dalam file $HOME/.vim/after/ftplugin/{FILETYPE}.vim.

1 Dalam fungsi saya, variabel s:matchesberisi semua kemungkinan kecocokan. Di sini saya hanya menaruh beberapa saran untuk keterbacaan tetapi Anda dapat menempatkan semua kelas yang ditawarkan oleh FontAwesome (Anda dapat menemukan daftar lengkap dalam file ini yang dibuat oleh romainl).


Bagaimana jika saya tidak suka Vimscript?

Salah satu kemungkinan adalah menggunakan plugin YoucompleteMe yang menawarkan API untuk bermain dengan menu penyelesaian. Anda dapat membuat fungsi python yang akan melakukan pekerjaan yang cocok dan akan menggunakan API untuk mengisi antarmuka Vim. Lebih detail di sini .

statox
sumber
2
Kelengkapan omni default untuk CSS dan HTML sudah cukup bermanfaat dan Anda hanya dapat memilikinya satu per satu, jadi saya sarankan menggunakan "penyelesaian yang ditentukan pengguna". Lihat :help i_ctrl-x_ctrl-u.
romainl
@romainl: Anda benar sekali, saya akan melihat bagaimana menyesuaikan jawaban saya.
statox
5

Terkadang Anda menginginkan penyelesaian otomatis khusus yang tidak mengganggu pelengkapan otomatis bawaan atau yang ditentukan pengguna sama sekali. Ini sangat berguna untuk skrip atau plugin yang dimaksudkan untuk bekerja untuk berbagai jenis file.

Ini dapat dilakukan dengan cukup mudah dengan complete() fungsi dan pembungkus sederhana; sebagian besar prosedurnya sama dengan yang dijelaskan di dalam :help complete-functions dan jawaban Statox, kecuali bahwa Anda perlu menentukan pemetaan Anda sendiri dan harus menyebut complete()diri Anda alih-alih mengembalikan nilai.

Berikut adalah contoh dasar, komentar harus menjelaskan cara kerjanya.

" Map <C-x><C-m> for our custom completion
inoremap <C-x><C-m> <C-r>=MyComplete()<CR>

" Make subsequent <C-m> presses after <C-x><C-m> go to the next entry (just like
" other <C-x>* mappings)
inoremap <expr> <C-m> pumvisible() ?  "\<C-n>" : "\<C-m>"

" Complete function for addresses; we match the name & address
fun! MyComplete()
    " The data. In this example it's static, but you could read it from a file,
    " get it from a command, etc.
    let l:data = [
        \ ["Elmo the Elk", "[email protected]"],
        \ ["Eek the Cat", "[email protected]"]
    \ ]

    " Locate the start of the word and store the text we want to match in l:base
    let l:line = getline('.')
    let l:start = col('.') - 1
    while l:start > 0 && l:line[l:start - 1] =~ '\a'
        let l:start -= 1
    endwhile
    let l:base = l:line[l:start : col('.')-1]

    " Record what matches − we pass this to complete() later
    let l:res = []

    " Find matches
    for m in l:data
        " Check if it matches what we're trying to complete; in this case we
        " want to match against the start of both the first and second list
        " entries (i.e. the name and email address)
        if l:m[0] !~? '^' . l:base && l:m[1] !~? '^' . l:base
            " Doesn't match
            continue
        endif

        " It matches! See :help complete() for the full docs on the key names
        " for this dict.
        call add(l:res, {
            \ 'icase': 1,
            \ 'word': l:m[0] . ' <' . l:m[1] . '>, ',
            \ 'abbr': l:m[0],
            \ 'menu': l:m[1],
            \ 'info': len(l:m) > 2 ? join(l:m[2:], "\n") : '',
        \ })
    endfor

    " Now call the complete() function
    call complete(l:start + 1, l:res)
    return ''
endfun
Martin Tournoij
sumber