Come creare la mia funzione di completamento automatico?


22

Come faccio a creare il mio elenco di completamento automatico per determinati tipi di file?

Ad esempio, vorrei che CSS e HTML completassero automaticamente dall'elenco delle classi CSS in FontAwesome .

Risposte:


22

Il completamento del dizionario sarebbe una soluzione economica e non intrusiva:

  1. salva fontawesome.txt da qualche parte sul tuo computer,

  2. inseriscilo after/ftplugin/css.vim(crea il file se non esiste):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. avviare una nuova sessione o eseguirla :ein un buffer CSS per eseguire forzatamente l'origine del file sopra,

  4. premere <C-n>o <C-p>in modalità inserimento,

  5. godere!

    completamento del dizionario

Riferimento:

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

17

MODIFICA Grazie al commento di Romainl ho modificato la mia risposta per mostrare come creare una funzione di completamento definita dall'utente. Nella versione precedente stavo scavalcando l'omni-completamento integrato che non era buono perché l'utente avrebbe perso il completamento predefinito che è piuttosto potente.


Una soluzione vimscript

Una soluzione è usare vimscript e il fatto che vim ti permetta di creare una funzione di completamento personalizzata.

Il vantaggio di questa soluzione è che non hai bisogno di un plug-in aggiuntivo, puoi semplicemente creare una funzione di completamento definita dall'utente e utilizzare la funzione di completamento integrata.

Userò il tuo esempio delle classi fontAwesome nei cssfile:

Crea il file ~/.vim/autoload/csscomplete.vime inserisci le seguenti righe:

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

Quindi creare il file ~/.vim/after/ftplugin/css.vime inserirvi la seguente riga:

setlocal completefunc=csscomplete#CompleteFA

Quindi è possibile attivare la funzione di completamento definita dall'utente con <C-x><C-u>. Tenterà di trovare una corrispondenza con l'ultima parola digitata.

Nello screenshot ho digitato .fa-le quindi attivato la funzione con <C-x><C-u>:

inserisci qui la descrizione dell'immagine


Come funziona?

Innanzitutto ecco alcuni argomenti di documentazione rilevanti:

Se si desidera creare un completamento personalizzato per un particolare tipo di file, è necessario inserirlo nel file $HOME/.vim/autoload/{FILETYPE}complete.vim.

Quindi in questo file devi creare la tua funzione di completamento che si chiama due volte:

  • Alla prima chiamata il suo primo argomento è la posizione corrente del cursore e la funzione determinerà la parola da completare. Nella mia funzione ho usato 3 confronti per ottenere il completamento della parola perché la classe può essere composta da lettere .e - (penso che sia possibile migliorare questa corrispondenza ma sto davvero male con regex)

  • Alla seconda chiamata il secondo argomento è la parola da abbinare e quindi la funzione la confronta con l'elenco di possibili classi da abbinare a 1 . Qui vedi che restituisco un dizionario che popolerà il menu di completamento, ma quando leggerai la documentazione vedrai che puoi creare un dizionario molto più complesso per personalizzare ancora di più il comportamento della tua funzione.

Devi anche dire a Vim "per questo tipo di file, usa questa funzione completa che ho creato". Per fare ciò devi impostare completefuncil nome della funzione che hai creato (qui csscomplete#CompleteFA) e questa impostazione deve essere fatta nel file $HOME/.vim/after/ftplugin/{FILETYPE}.vim.

1 Nella mia funzione la variabile s:matchescontiene tutte le possibili corrispondenze. Qui metto solo alcuni suggerimenti per la leggibilità, ma puoi mettere tutte le classi offerte da FontAwesome (puoi trovare l'elenco completo in questo file creato da romainl).


Cosa succede se non mi piace Vimscript?

Una possibilità è utilizzare il plug-in YoucompleteMe che offre un'API per giocare con il menu di completamento. Puoi creare una funzione Python che farà il lavoro corrispondente e utilizzerà l'API per popolare l'interfaccia di Vim. Maggiori dettagli qui .


2
Il completamento omni predefinito per CSS e HTML è già abbastanza utile e puoi averne solo uno alla volta, quindi suggerirei invece di utilizzare "completamento definito dall'utente". Vedere :help i_ctrl-x_ctrl-u.
romainl,

@romainl: hai perfettamente ragione, vedrò come adattare la mia risposta.
statox

5

A volte desideri un completamento automatico personalizzato che non interferisca con nessuno dei completamenti automatici predefiniti o definiti dall'utente. Ciò è particolarmente utile per script o plugin che funzionano con un'ampia gamma di tipi di file.

Questo può essere fatto abbastanza facilmente con la complete() funzione e un semplice wrapper; la maggior parte della procedura è la stessa descritta nella :help complete-functions risposta di Statox, tranne per il fatto che è necessario definire i propri mapping e chiamare complete()se stessi invece di restituire un valore.

Ecco un esempio di base, i commenti dovrebbero spiegare come funziona.

" 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.