独自のオートコンプリート機能を作成する方法は?


22

特定のファイルタイプのオートコンプリートの独自のリストを作成するにはどうすればよいですか?

たとえば、FontAwesomeのcssクラスのリストからcssとhtmlをオートコンプリートしたいです。

回答:


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

編集 romainlのコメントのおかげで、ユーザー定義の補完関数を作成する方法を示すために回答を編集しました。前のバージョンでは、組み込みのオムニ補完をオーバーライドしていましたが、ユーザーはかなり強力なデフォルトの補完を失うため、良くありませんでした。


vimscriptソリューション

1つの解決策は、vimscriptと、vimを使用してカスタマイズされた補完関数を作成できるという事実を使用することです。

このソリューションの利点は、追加のプラグインが必要ないことです。ユーザー定義の補完機能を作成し、組み込みの補完機能を使用するだけです。

cssファイル内のfontAwesomeクラスの例を使用します。

ファイル~/.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

次に、このファイルで、2回呼び出される完了関数を作成する必要があります。

  • 最初の呼び出しで、その最初の引数はカーソルの現在位置であり、関数は完了する単語を決定します。私の機能では、私はクラスは、文字で構成することができるので、完全に単語を取得するために3つの比較を使用し、.そして- (私はこのマッチングを向上させることが可能だと思うが、私は正規表現で本当に悪いんです)

  • 2回目の呼び出しで、2番目の引数は一致する単語であり、関数はそれを一致する可能性のあるクラスのリストと比較します1。ここでは、完了メニューに表示される辞書を返します、ドキュメントを読むと、はるかに複雑な辞書を作成して関数の動作をさらにカスタマイズできることがわかります。

また、Vimに「この種のファイルについては、私が作成したこの完全な関数を使用する」ように伝えなければなりません。そのためには、completefunc作成した関数の名前(ここcsscomplete#CompleteFA)を設定する必要があり、この設定はファイルで行う必要があります$HOME/.vim/after/ftplugin/{FILETYPE}.vim

1私の関数では、変数にs:matchesはすべての可能な一致が含まれています。ここでは読みやすくするためにいくつかの提案のみを掲載していますが、FontAwesome が提供するすべてのクラスを掲載することができます(romainlによって作成されたこのファイルで完全なリストを見つけることができます)。


Vimscriptが気に入らない場合はどうすればよいですか?

可能性の1つは、補完メニューで再生するAPIを提供するプラグインYoucompleteMeを使用することです。一致するジョブを実行し、APIを使用してVimインターフェイスを作成するPython関数を作成できます。詳細はこちら


2
CSSとHTMLのデフォルトのオムニ補完はすでに非常に便利で、一度に1つしか使用できないため、代わりに「ユーザー定義の補完」を使用することをお勧めします。をご覧ください:help i_ctrl-x_ctrl-u
ロメイン

@romainl:あなたは絶対に正しいです、私は私の答えを適応させる方法を見ていきます。
statox

5

組み込みまたはユーザー定義のオートコンプリートをまったく妨害しないカスタムのオートコンプリートが必要な場合があります。これは、広範囲のファイルタイプで機能することを目的としたスクリプトまたはプラグインに特に役立ちます。

これは、complete() 関数と単純なラッパーでかなり簡単に実行でき ます。手順の大部分は、:help complete-functions Statoxの回答で説明したものと同じ ですが、独自のマッピングを定義し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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.