Jak stworzyć własną funkcję autouzupełniania?

22

Jak utworzyć własną listę autouzupełniania dla niektórych typów plików?

Na przykład chciałbym, aby css i html uzupełniały się automatycznie z listy klas css w FontAwesome .

firedev
źródło

Odpowiedzi:

22

Uzupełnianie słownika byłoby tanim i nieinwazyjnym rozwiązaniem:

  1. zapisz fontawesome.txt gdzieś na swoim komputerze,

  2. wstaw to after/ftplugin/css.vim(utwórz plik, jeśli nie istnieje):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. rozpocznij nową sesję lub zrób :ew buforze CSS, aby wymusić źródło pliku powyżej,

  4. naciśnij <C-n>lub <C-p>w trybie wstawiania,

  5. cieszyć się!

    uzupełnianie słownika

Odniesienie:

:help ins-completion
:help 'complete'
:help 'dictionary'
:help 'iskeyword'
romainl
źródło
17

EDYCJA Dzięki komentarzowi romainla zredagowałem swoją odpowiedź, aby pokazać, jak utworzyć funkcję uzupełniania zdefiniowaną przez użytkownika. W poprzedniej wersji zastępowałem wbudowane omni-uzupełnianie, co nie było dobre, ponieważ użytkownik straciłby domyślne uzupełnienie, które jest dość potężne.


Rozwiązanie vimscript

Jednym z rozwiązań jest użycie vimscript i fakt, że vim pozwala ci stworzyć niestandardową funkcję uzupełniania.

Zaletą tego rozwiązania jest to, że nie potrzebujesz dodatkowej wtyczki, możesz po prostu utworzyć funkcję uzupełniania zdefiniowaną przez użytkownika i użyć wbudowanej funkcji uzupełniania.

Wykorzystam twój przykład klas fontAwesome w cssplikach:

Utwórz plik ~/.vim/autoload/csscomplete.vimi umieść w nim następujące linie:

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

Następnie utwórz plik ~/.vim/after/ftplugin/css.vimi umieść w nim następujący wiersz:

setlocal completefunc=csscomplete#CompleteFA

Następnie możesz uruchomić funkcję uzupełniania zdefiniowaną przez użytkownika za pomocą <C-x><C-u>. Spróbuje znaleźć dopasowanie do ostatniego wpisanego słowa.

Na zrzucie ekranu wpisałem, .fa-la następnie uruchomiłem funkcję za pomocą <C-x><C-u>:

wprowadź opis zdjęcia tutaj


Jak to działa?

Najpierw kilka istotnych tematów dokumentacji:

Jeśli chcesz utworzyć niestandardowe uzupełnienie dla określonego rodzaju pliku, musisz umieścić go w pliku $HOME/.vim/autoload/{FILETYPE}complete.vim.

Następnie w tym pliku musisz utworzyć funkcję uzupełniania, która jest wywoływana dwukrotnie:

  • Przy pierwszym wywołaniu jego pierwszym argumentem jest bieżąca pozycja kursora, a funkcja określi słowo do uzupełnienia. W mojej funkcji użyłem 3 porównań, aby uzupełnić słowo, ponieważ klasa może składać się z liter, .i - (myślę, że można poprawić to dopasowanie, ale naprawdę źle rozumiem wyrażenie regularne)

  • Przy drugim wywołaniu drugim argumentem jest słowo do dopasowania, a następnie funkcja porównuje je z listą możliwych klas do dopasowania 1 . Tutaj widzisz, że zwracam słownik, który zapełni menu uzupełniające, ale kiedy przeczytasz dokumentację, zobaczysz, że możesz stworzyć znacznie bardziej złożony słownik, aby jeszcze bardziej dostosować zachowanie twojej funkcji.

Musisz także powiedzieć Vimowi „dla tego rodzaju pliku, użyj tej pełnej funkcji, którą stworzyłem”. Aby to zrobić, musisz ustawić completefuncnazwę utworzonej funkcji (tutaj csscomplete#CompleteFA) i to ustawienie musi zostać wykonane w pliku $HOME/.vim/after/ftplugin/{FILETYPE}.vim.

1 W mojej funkcji zmienna s:matcheszawiera wszystkie możliwe dopasowania. Tutaj zamieszczam tylko kilka sugestii dotyczących czytelności, ale możesz umieścić wszystkie klasy oferowane przez FontAwesome (pełną listę można znaleźć w tym pliku utworzonym przez romainl).


Co jeśli nie podoba mi się Vimscript?

Jedną z możliwości jest użycie wtyczki YoucompleteMe, która oferuje API do zabawy z menu ukończenia. Możesz utworzyć funkcję python, która wykona pasujące zadanie i użyje interfejsu API do zapełnienia interfejsu Vima. Więcej informacji tutaj .

statox
źródło
2
Domyślne uzupełnianie omni dla CSS i HTML jest już całkiem przydatne i możesz mieć tylko jedno na raz, dlatego sugerowałbym użycie „uzupełniania zdefiniowanego przez użytkownika”. Zobaczyć :help i_ctrl-x_ctrl-u.
romainl
@romainl: Masz absolutną rację, zobaczę, jak dostosować moją odpowiedź.
statox
5

Czasami potrzebujesz niestandardowego autouzupełniania, które nie koliduje z żadnymi wbudowanymi lub zdefiniowanymi przez użytkownika autouzupełnianiami. Jest to szczególnie przydatne w przypadku skryptów lub wtyczek przeznaczonych do pracy z szeroką gamą typów plików.

Można to zrobić dość łatwo za pomocą complete() funkcji i prostego opakowania; większość procedur jest taka sama, jak opisano w :help complete-functions odpowiedzi Statox, z wyjątkiem tego, że musisz zdefiniować własne odwzorowania i musisz wywołać complete()siebie zamiast zwracać wartość.

Oto podstawowy przykład, w komentarzach należy wyjaśnić, jak to działa.

" 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
źródło