Vim駅伝2025-08-29の記事です。前回の記事はnil2さんによる左手デバイスからVimを操作するでした。マクロパッドらしい痒い操作の実現が可能になる良いアイデアですね。
さてさて、Neovim 0.12(開発版)にvim.lsp.inline_completion
が実装されました。
これと言語サーバーのcopilot-language-serverを組み合わせると、GitHub Copilotによる補完を、LSPクライアント経由で利用できます。プラグインのcopilot.vimやcopilot.luaがいらなくなるのです!
local function fizzbuzz
と打つと以下のような内容がグレーアウトで補完されるやつですね。
copilot-language-serverに限らず、lsp-aiなどの他の言語サーバーもも使えるはずなので、ローカルLLMを使いたい人にも有用です。
local function fizzbuzz(n)
if n % 15 == 0 then
return "FizzBuzz"
elseif n % 3 == 0 then
return "Fizz"
elseif n % 5 == 0 then
return "Buzz"
else
return tostring(n)
end
end
設定方法
- copilot-language-serverをインストールする
- Neovim 0.12(開発版)を設定する
nvim-lspconfigプラグインを導入
以下のような設定を自身のLSP関連の設定に追記
require("nvim-lspconfig") -- copilot language serverの設定を提供 vim.lsp.enable("copilot") vim.lsp.inline_completion.enable(true) -- マッピングのLHSはお好みで vim.keymap.set('i', '<c-cr>', '<cmd>lua vim.lsp.inline_completion.get()<cr>', { silent = true })
:LspCopilotSignIn
コマンドを実行して、認証する
Tip: インライン補完の候補の有無でマッピングの挙動を変える
vim.lsp.inline_completion.get()
は補完する内容がない場合に、false
を返します。この挙動を利用して、<tab>
を入力した時に、補完候補が表示されていれば採用し、そうでなければ通常の<tab>
を入力するといった動作を実現できます。
vim.keymap.set('i', '<tab>', function()
if not vim.lsp.inline_completion.get() then
return '<tab>'
end
end, { expr = true })
Tip: インライン補完の有効・無効を細かく制御する
先の例では言語サーバーによらずインライン補完を有効化していました。有効化に使うvim.lsp.inline_completion.enable
関数の第二引数を使うと、有効・無効を細かく制御できます。
https://neovim.io/doc/user/lsp.html#vim.lsp.inline_completion.enable()
enable({enable}, {filter}) *vim.lsp.inline_completion.enable()*
Enables or disables inline completion for the {filter}ed scope, inline
completion will automatically be refreshed when you are in insert mode.
To "toggle", pass the inverse of `is_enabled()`: >lua
vim.lsp.inline_completion.enable(not vim.lsp.inline_completion.is_enabled())
<
Parameters: ~
• {enable} (`boolean?`) true/nil to enable, false to disable
• {filter} (`table?`) Optional filters |kwargs|,
• {bufnr}? (`integer`, default: all) Buffer number, or 0 for
current buffer, or nil for all.
• {client_id}? (`integer`, default: all) Client ID, or nil
for all.
たとえばLSPクライアントがcopilotのときだけ有効にしたい場合は、以下のようにします。
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(ev)
local client = vim.lsp.get_client_by_id(ev.data.client_id)
if client.name == "copilot" then
vim.lsp.inline_completion.enable(true, { client_id = client.id, bufnr = ev.buf })
end
end
})
Tip: nvim-lspconfigに依存しない方法
nvim-lspconfigプラグインに依存せず設定したい方もいるかもしれません。その場合
copilot-language-serverの設定を自前で書く
kawarimidollさんの記事が参考になります
Neovim0.11用のLSP設定 https://zenn.dev/kawarimidoll/articles/b202e546bca344
copilot-language-serverがカレントバッファにアタッチされている状態で以下を実行する
:= vim.lsp.get_clients({ name = "copilot" })[1].request_sync("signIn", vim.empty_dict(), 1000, 0)
といった流れになると思われます。
認証方法についてはvim-jp SlackでGen Fさんが教えてくれました。
ENJOY!!
数日使ってみた感じだと、補完の速度がcopilot.luaプラグインよりもかなり速い気がします。
ただし、現時点ではcopilot.luaプラグインと違って、補完を行単位や単語単位で確定できません。私は滅多に利用していなかったので、まあいいかなと思ってます。対応については以下のIssueで議論されているので、近く改善されるかもしれません。
ところでVim駅伝は執筆者を随時募集しています。なんと次回の2025-09-01の枠も空いています。自分なりのVimの使い方からVimとの出会い、Vim友達のこと、Vimにまつわることなら技術記事じゃなくてもOKです。ぜひ気軽に書いてみてください。まだ見ぬ記事と出会えるの楽しみにしてます!