Як створити власну функцію автозаповнення?


22

Як створити власний список автоматичного заповнення для певних файлів?

Наприклад, я хотів би, щоб css і html автозаповнювалися зі списку класів css у FontAwesome .

Відповіді:


22

Завершення словника буде дешевим і не нав'язливим рішенням:

  1. збережіть fontawesome.txt десь на вашій машині,

  2. поставте це after/ftplugin/css.vim(створіть файл, якщо його не існує):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. запустити новий сеанс або зробити :eв буфері CSS, щоб насильно джерело файлу,

  4. натисніть <C-n>або <C-p>в режимі вставки,

  5. насолоджуйтесь!

    складання словника

Довідка:

:help ins-completion
:help 'complete'
:help 'dictionary'
:help 'iskeyword'

17

EDIT Завдяки коментарю romainl я відредагував свою відповідь, щоб показати, як створити визначену користувачем функцію завершення. У попередній версії я переосмислив вбудований омні-завершення, що було не дуже добре, оскільки користувач втратив би завершення за замовчуванням, яке досить потужне.


Вимскриптове рішення

Одне рішення - використовувати vimscript і те, що vim дозволить вам створити налаштовану функцію завершення.

Перевага цього рішення полягає в тому, що вам не потрібен додатковий плагін, ви можете просто створити визначену користувачем функцію завершення та використовувати вбудовану функцію завершення.

Я буду використовувати ваш приклад класів fontAwesome у cssфайлах:

Створіть файл ~/.vim/autoload/csscomplete.vimі помістіть у нього наступні рядки:

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

Потім створіть файл ~/.vim/after/ftplugin/css.vimі вставте в нього такий рядок:

setlocal completefunc=csscomplete#CompleteFA

Тоді ви можете запустити функцію завершення, визначену користувачем <C-x><C-u>. Він спробує знайти відповідність останньому набраному слову.

На скріншоті я набрав, .fa-lа потім запустив функцію за допомогою <C-x><C-u>:

введіть тут опис зображення


Як це працює?

Спочатку наведено декілька відповідних тем документації:

Якщо ви хочете створити спеціальне завершення для певного типу файлів, ви повинні помістити його у файл $HOME/.vim/autoload/{FILETYPE}complete.vim.

Потім у цьому файлі ви повинні створити функцію завершення, яка викликається двічі:

  • При першому виклику його першим аргументом є поточне положення курсору, і функція визначатиме слово, яке потрібно виконати. У своїй функції я використав 3 порівняння для того, щоб довести слово до кінця, тому що клас може складатися з літер, .і - (я думаю, що можна покращити цю відповідність, але я дуже поганий з регулярним виразом)

  • Під час другого виклику другим аргументом є слово, яке відповідає, а потім функція порівнює його зі списком можливих класів, які відповідають 1 . Тут ви бачите, що я повертаю словник, який заповнить меню завершення, але коли ви прочитаєте документацію, ви побачите, що ви можете створити набагато складніший словник, щоб ще більше налаштувати поведінку вашої функції.

Ви також повинні сказати Vim "для цього типу файлів, використовуйте цю повну функцію, яку я створив". Для цього вам потрібно встановити completefuncім'я створеної вами функції (тут csscomplete#CompleteFA), і це налаштування потрібно виконати у файлі $HOME/.vim/after/ftplugin/{FILETYPE}.vim.

1 У моїй функції змінна s:matchesмістить усі можливі збіги. Тут я вкладаю лише кілька пропозицій щодо читабельності, але ви можете розмістити всі класи, запропоновані FontAwesome (Повний список ви можете знайти в цьому файлі, створеному romainl).


Що робити, якщо мені не подобається Vimscript?

Однією з можливостей є використання плагіна YoucompleteMe, який пропонує API для відтворення з меню завершення. Ви можете створити функцію python, яка буде виконувати відповідні завдання та використовуватиме API для заповнення інтерфейсу Vim. Детальніше тут .


2
Доповнення omni за замовчуванням для CSS та HTML вже є досить корисним, і ви можете мати його лише один за одним, тому я б запропонував використовувати замість нього "визначене користувачем завершення". Див :help i_ctrl-x_ctrl-u.
romainl

@romainl: Ви абсолютно праві, я побачу, як адаптувати свою відповідь.
statox

5

Іноді потрібно налаштувати власне автоматичне завершення, яке взагалі не заважає вбудованому або визначеному користувачем автозавершенням. Це особливо корисно для сценаріїв або плагінів, які призначені для роботи для широкого спектру файлів.

Це можна зробити досить легко за допомогою complete() функції та простої обгортки; більшість процедур є такими ж, як описано у :help complete-functions відповіді Статокса, за винятком того, що вам потрібно визначити власні відображення та зателефонувати complete()собі, а не повертати значення.

Ось основний приклад, коментарі повинні пояснити, як це працює.

" 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", "daring@brave.com"],
        \ ["Eek the Cat", "doesnthurt@help.com"]
    \ ]

    " 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.