Como criar minha própria função de preenchimento automático?

22

Como crio minha própria lista de preenchimento automático para certos tipos de arquivos?

Por exemplo, eu gostaria que o CSS e o HTML fossem preenchidos automaticamente a partir da lista de classes de CSS no FontAwesome .

firedev
fonte

Respostas:

22

A conclusão do dicionário seria uma solução barata e não intrusiva:

  1. salve o fontawesome.txt em algum lugar da sua máquina,

  2. coloque isso after/ftplugin/css.vim(crie o arquivo se ele não existir):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. inicie uma nova sessão ou faça :eum buffer CSS para forçar a origem do arquivo acima,

  4. pressione <C-n>ou <C-p>no modo de inserção,

  5. desfrutar!

    conclusão do dicionário

Referência:

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

EDIT Graças ao comentário de romainl, editei minha resposta para mostrar como criar uma função de conclusão definida pelo usuário. Na versão anterior, eu estava substituindo a omni-conclusão do buil-in, o que não era bom porque o usuário teria perdido a conclusão padrão, que é bastante poderosa.


Uma solução vimscript

Uma solução é usar o vimscript e o fato de o vim criar uma função de conclusão customizada.

A vantagem desta solução é que você não precisa de um plug-in adicional; basta criar uma função de conclusão definida pelo usuário e usar o recurso de conclusão interno.

Usarei seu exemplo das classes fontAwesome em cssarquivos:

Crie o arquivo ~/.vim/autoload/csscomplete.vime insira as seguintes linhas:

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

Em seguida, crie o arquivo ~/.vim/after/ftplugin/css.vime coloque nele a seguinte linha:

setlocal completefunc=csscomplete#CompleteFA

Em seguida, você pode ativar sua função de conclusão definida pelo usuário com <C-x><C-u>. Ele tentará encontrar uma correspondência com a última palavra digitada.

Na captura de tela, digitei .fa-le, em seguida, ativei a função com <C-x><C-u>:

insira a descrição da imagem aqui


Como funciona?

Primeiro, aqui estão alguns tópicos de documentação relevantes:

Se você deseja criar um preenchimento personalizado para um tipo de arquivo específico, é necessário colocá-lo no arquivo $HOME/.vim/autoload/{FILETYPE}complete.vim.

Nesse arquivo, você deve criar sua função de conclusão, chamada duas vezes:

  • Na primeira chamada, seu primeiro argumento é a posição atual do cursor e a função determinará a palavra a ser concluída. Na minha função, usei 3 comparações para concluir a palavra porque a classe pode ser composta de letras .e - (acho que é possível melhorar essa correspondência, mas sou muito ruim com a expressão regular)

  • Na segunda chamada, o segundo argumento é a palavra a ser correspondida e, em seguida, a função a compara com a lista de classes possíveis para corresponder a 1 . Aqui você vê que eu retorno um dicionário que preenche o menu de conclusão, mas quando você ler a documentação, poderá criar um dicionário muito mais complexo para personalizar ainda mais o comportamento de sua função.

Você também precisa informar ao Vim "para esse tipo de arquivo, use esta função completa que eu criei". Para isso, é necessário definir completefunco nome da função criada (aqui csscomplete#CompleteFA) e essa configuração deve ser feita no arquivo $HOME/.vim/after/ftplugin/{FILETYPE}.vim.

1 Na minha função, a variável s:matchescontém todas as correspondências possíveis. Aqui, apenas coloco algumas sugestões de legibilidade, mas você pode colocar todas as classes oferecidas pelo FontAwesome (Você pode encontrar a lista completa neste arquivo criado por romainl).


E se eu não gostar do Vimscript?

Uma possibilidade é usar o plugin YoucompleteMe, que oferece uma API para brincar com o menu de conclusão. Você pode criar a função python que fará o trabalho correspondente e usará a API para preencher a interface do Vim. Mais detalhes aqui .

statox
fonte
2
A conclusão omni padrão para CSS e HTML já é bastante útil e você pode ter apenas uma por vez, então sugiro usar "conclusão definida pelo usuário". Veja :help i_ctrl-x_ctrl-u.
Romainl 30/08/2015
@romainl: Você está absolutamente certo, vou ver como adaptar minha resposta.
statox
5

Às vezes, você deseja um preenchimento automático personalizado que não interfira em nenhum dos preenchimentos automáticos integrados ou definidos pelo usuário. Isso é especialmente útil para scripts ou plugins destinados a trabalhar para uma ampla variedade de tipos de arquivos.

Isso pode ser feito facilmente com a complete() função e um invólucro simples; a maior parte do procedimento é a mesma descrita na :help complete-functions resposta da Statox, exceto que você precisa definir seus próprios mapeamentos e precisa se chamar em complete()vez de retornar um valor.

Aqui está um exemplo básico, os comentários devem explicar como funciona.

" 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
fonte