Neovim 0.12の標準補完(autocompleteとLSP補完)
Vimの補完は昔から強力でしたが、必ずCtrl-nやCtrl-x Ctrl-oを自分で押す必要がありました。VSCodeのように打っているそばから候補が出てほしい、という理由でnvim-cmpのような補完プラグインを入れていた人は多いはずです。Neovim 0.12で追加された'autocomplete'オプションは、この「自動でポップアップを出す」部分だけを本体に取り込んだものです。1行の設定で、プラグインなしに入力中の補完メニューが出るようになります。
検証環境:macOS上のNeovim 0.12.4で確認しました。オプションの既定値・LSPから返ってきた候補はすべて実行結果の実物で、LSPの検証にはgopls v0.16.2を使っています。補完メニューの見た目や操作感は対話的な要素なので、この記事ではオプションの効き方と候補が実際に集まっているかまでを機械的に確認し、体感の部分は使用感として分けて書いています。
| 設定 | これだけ覚える |
|---|---|
set autocomplete | 入力中に補完メニューを自動で出す |
set complete=... | 候補をどこから集めるかを決める |
set complete+=o | LSPの候補(omnifunc)を混ぜる |
set autocompletedelay=200 | 出るまでの待ち時間(ミリ秒) |
1行で自動補完になる
まず既定値を確認しておきます。素のnvim --cleanで読み取った値がこれです。
autocomplete=false
autocompletedelay=0
autocompletetimeout=80
completeopt=menu,popup
complete=.,w,b,u,t
'autocomplete'は既定で無効なので、有効にするだけで挙動が変わります。
-- init.lua
vim.o.autocomplete = true
これで挿入モードで文字を打った時点で候補のポップアップが出ます。Ctrl-nを押したときと同じ仕組みが自動で走っているだけなので、候補の選択は今までどおりCtrl-nとCtrl-p、確定はCtrl-y、取り消しはCtrl-eです。Vim標準の入力補完を使ったことがあれば、覚え直すことはほとんどありません。
出るのが早すぎて邪魔なときは'autocompletedelay'を足します。既定は0ミリ秒、つまり即座に出ます。
vim.o.autocompletedelay = 200 -- 打鍵が止まって0.2秒後に出す
ヘルプは、自分のタイピング速度より少し長めの値にすると開きっぱなしにならないと案内しています。'autocompletetimeout'のほうは候補集めに使う時間の初期値で、既定は80ミリ秒です。こちらは基本的に触らなくて構いません。
候補はどこから来るのか
自動補完が何を候補にするかは'complete'オプションで決まります。これはCtrl-nと共通の設定で、既定の.,w,b,u,tは次の意味です。
| フラグ | 探す場所 |
|---|---|
| . | 今のバッファ |
| w | 他のウインドウのバッファ |
| b | バッファリストにある読み込み済みバッファ |
| u | バッファリストにある未読み込みバッファ |
| t | タグ |
| o | 'omnifunc'(LSPが接続していればLSPの候補) |
| k | 'dictionary'に指定した辞書ファイル |
並べた順序には意味があります。ヘルプによると、候補集めには時間制限があり、リストの前にあるソースほど多くの時間が割り当てられます。全部のソースが少なくとも短い時間は確保されるので、遅いソースを混ぜても入力が固まらない作りです。
さらに、フラグの後ろに^と数字を付けると、そのソースから取る候補の数に上限を掛けられます。ヘルプが例として挙げている設定はこの形です。
set autocomplete
set complete=.^5,w^5,b^5,u^5
set completeopt=popup
各ソースから5件までに絞る、という指定です。自動補完は打つたびに走るので、候補が数十件出てくると視界を塞いでしまいます。件数を絞ると実用的になります。なお、この上限が効くのは前方向の補完(Ctrl-n)だけで、Ctrl-pでは無視されます。
LSPの候補を混ぜる
バッファ内の単語だけでは補完として物足りません。Neovimの標準LSPを有効にしていれば、言語サーバーが返す候補も同じ仕組みに流し込めます。LSPが接続したバッファでは'omnifunc'が差し替えられるので、'complete'にoを足すだけです。
vim.o.autocomplete = true
vim.o.complete = '.,o' -- 現在のバッファ + LSP
goplsを接続したGoのバッファで実際に設定を読み直したところ、次の状態になっていました。
complete=.,o autocomplete=true omnifunc=v:lua.vim.lsp.omnifunc
候補そのものが返ってきているかも確かめました。strings.まで打った位置でLSPに補完を要求すると、goplsから次の候補が返っています。
補完候補 5 件
- strings
- strings.Split
- strings.SplitAfter
- strings.SplitAfterN
- stringslite
もうひとつ、LSP側から補完を有効にするvim.lsp.completion.enable()という入口もあります。autotriggerを有効にすると、言語サーバーが「ここで補完を出すべき」と申告した文字(Goなら.など)に反応して候補が出ます。
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
vim.lsp.completion.enable(true, args.data.client_id, args.buf, {
autotrigger = true,
})
end,
})
2つは目的が違います。'autocomplete'は入力そのものに反応して'complete'の全ソースから集める仕組み、vim.lsp.completionはLSPのトリガー文字に反応してLSPの候補だけを出す仕組みです。両方入れても競合はしませんが、まずは'autocomplete'とcomplete+=oだけで様子を見るのが分かりやすいと思います。
nvim-cmpの代わりになるか
ここは正直に線を引いておきます。標準の自動補完でまかなえるのは「入力に応じて候補を出し、選んで確定する」という中心部分だけです。補完プラグインが持っている周辺機能は入っていません。
| 機能 | 標準のautocomplete | nvim-cmp等 |
|---|---|---|
| 入力中のポップアップ | できる | できる |
| バッファ・タグ・LSPの候補 | できる | できる |
| ソースごとの並び替えルール | 順序と件数上限のみ | 細かく制御できる |
| スニペット展開との統合 | 持たない | プラグインで組める |
| 候補のアイコン・整形 | 'completeitemalign'程度 | 自由に書ける |
スニペットを多用している人や、候補の見た目を作り込んでいる人は、標準機能だけに移ると物足りなさを感じるはずです。逆に「候補が自動で出ればよく、設定ファイルは短いほうがいい」という使い方なら、プラグイン1つと設定数十行がオプション3つに置き換わります。vim.packで入れるプラグインを減らしたいときの候補としても現実的です。
切り替えの手間が小さいのも利点です。'autocomplete'を無効に戻せば挙動は元どおりで、Ctrl-nやCtrl-x Ctrl-oはこれまでと同じように使えます。自動補完中でもCtrl-xを押せば自動補完が一時的に止まり、任意の補完モードに入れる作りになっています。
最初にset autocompleteだけ入れて書き始めたら、既定のautocompletedelay=0のせいで2文字打つたびにメニューが出て、正直うるさく感じました。200ミリ秒にして、completeを.^5,oに絞ったところでようやく「邪魔にならないが必要なときは出ている」状態になりました。既定値のまま試して合わないと判断してしまうのはもったいないので、この2つは最初に触ることをおすすめします。あとは候補の確定がCtrl-yのままなのを忘れがちで、Enterを押して改行してしまう癖がしばらく抜けませんでした。
まとめ
'autocomplete'を有効にすると、Neovimはプラグインなしで入力中の補完メニューを出します。候補の集め先はCtrl-nと同じ'complete'で決まり、oを足せばLSPの候補も同じメニューに混ざります。既定のautocompletedelay=0は反応が速すぎて煩わしく感じやすいので、実際に使うなら遅延と件数上限を調整するところまでを1セットと考えるのが現実的です。スニペットや候補表示の作り込みが必要なら補完プラグインに分がありますが、設定を小さく保ちたいなら標準機能だけで十分に成立します。