Comment créer ma propre fonction de saisie semi-automatique?

22

Comment créer ma propre liste de saisie semi-automatique pour certains types de fichiers?

Par exemple, je voudrais que css et html se complètent automatiquement à partir de la liste des classes css dans FontAwesome .

firedev
la source

Réponses:

22

L'achèvement du dictionnaire serait une solution peu coûteuse et non intrusive:

  1. enregistrez fontawesome.txt quelque part sur votre machine,

  2. mettez-le after/ftplugin/css.vim(créez le fichier s'il n'existe pas):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. démarrer une nouvelle session ou faire :edans un tampon CSS pour forcer la source du fichier ci-dessus,

  4. appuyez sur <C-n>ou <C-p>en mode insertion,

  5. prendre plaisir!

    achèvement du dictionnaire

Référence:

:help ins-completion
:help 'complete'
:help 'dictionary'
:help 'iskeyword'
romainl
la source
17

EDIT Merci au commentaire de romainl J'ai modifié ma réponse à montrer comment créer une fonction d'achèvement définie par l' utilisateur. Dans la version précédente, je remplaçais l'omni-complétion intégrée, ce qui n'était pas bon car l'utilisateur aurait perdu la complétion par défaut qui est assez puissante.


Une solution vimscript

Une solution consiste à utiliser vimscript et le fait que vim vous permet de créer une fonction de complétion personnalisée.

L'avantage de cette solution est que vous n'avez pas besoin d'un plugin supplémentaire, vous pouvez simplement créer une fonction d'achèvement définie par l'utilisateur et utiliser la fonction d'achèvement intégrée.

Je vais utiliser votre exemple des classes fontAwesome dans les cssfichiers:

Créez le fichier ~/.vim/autoload/csscomplete.vimet insérez-y les lignes suivantes:

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

Créez ensuite le fichier ~/.vim/after/ftplugin/css.vimet insérez-y la ligne suivante:

setlocal completefunc=csscomplete#CompleteFA

Ensuite, vous pouvez déclencher votre fonction d'achèvement définie par l'utilisateur avec <C-x><C-u>. Il essaiera de trouver une correspondance avec le dernier mot tapé.

Dans la capture d'écran, j'ai tapé .fa-lpuis déclenché la fonction avec <C-x><C-u>:

entrez la description de l'image ici


Comment ça marche?

Voici d'abord quelques sujets de documentation pertinents:

Si vous souhaitez créer une complétion personnalisée pour un type de fichier particulier, vous devez le mettre dans le fichier $HOME/.vim/autoload/{FILETYPE}complete.vim.

Ensuite, dans ce fichier, vous devez créer votre fonction d'achèvement qui est appelée deux fois:

  • Au premier appel, son premier argument est la position actuelle du curseur et la fonction déterminera le mot à terminer. Dans ma fonction, j'ai utilisé 3 comparaisons pour terminer le mot parce que la classe peut être composée de lettres, .et - (je pense qu'il est possible d'améliorer cette correspondance mais je suis vraiment mauvais avec regex)

  • Au deuxième appel, le deuxième argument est le mot à faire correspondre, puis la fonction le compare à la liste des classes possibles pour correspondre à 1 . Ici, vous voyez que je retourne un dictionnaire qui remplira le menu d'achèvement, mais quand vous lirez la documentation, vous verrez que vous pouvez créer un dictionnaire beaucoup plus complexe pour personnaliser encore plus le comportement de votre fonction.

Vous devez également dire à Vim "pour ce type de fichier, utilisez cette fonction complète que j'ai créée". Pour ce faire, vous devez définir completefuncle nom de la fonction que vous avez créée (ici csscomplete#CompleteFA) et ce paramètre doit être effectué dans le fichier $HOME/.vim/after/ftplugin/{FILETYPE}.vim.

1 Dans ma fonction, la variable s:matchescontient toutes les correspondances possibles. Ici je ne mets que quelques suggestions de lisibilité mais vous pouvez mettre toutes les classes proposées par FontAwesome (Vous pouvez trouver la liste complète dans ce fichier créé par romainl).


Et si je n'aime pas Vimscript?

Une possibilité est d'utiliser le plugin YoucompleteMe qui propose une API pour jouer avec le menu de complétion. Vous pouvez créer une fonction python qui fera le travail de correspondance et utilisera l'API pour remplir l'interface Vim. Plus de détails ici .

statox
la source
2
L'achèvement omni par défaut pour CSS et HTML est déjà très utile et vous ne pouvez en avoir qu'un à la fois, donc je suggère d'utiliser à la place "l'achèvement défini par l'utilisateur". Tu vois :help i_ctrl-x_ctrl-u.
romainl
@romainl: Vous avez absolument raison, je vais voir comment adapter ma réponse.
statox
5

Parfois, vous voulez une auto-complétion personnalisée qui n'interfère pas du tout avec les auto-complétion intégrées ou définies par l'utilisateur. Ceci est particulièrement utile pour les scripts ou plugins qui sont destinés à fonctionner pour un large éventail de types de fichiers.

Cela peut être fait assez facilement avec la complete() fonction et un simple wrapper; la plupart de la procédure est la même que celle décrite dans :help complete-functions la réponse de Statox, sauf que vous devez définir vos propres mappages et devez complete()vous appeler au lieu de renvoyer une valeur.

Voici un exemple de base, les commentaires devraient expliquer comment cela fonctionne.

" 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
la source