Neovim 0.12(開発版)でcopilot-language-serverを設定してみたぞ(脱copilot.lua)
この記事はVim駅伝の2025-09-01の記事です。
前回の記事はatusyさんのNeovim 0.12(開発版)ではLSPクライアント経由でGitHub Copilotを使えるぞ(脱copilot.vim)です。
Vim駅伝は常に参加者を募集しています。詳しくはこちらのページをご覧ください。
Neovimの開発ブランチにlspのtextDocument/inlineCompletion
のサポートが入りました。まだstableには入っていませんが、最新のnightlyなどで使用できます。
名前の通り、インラインの補完を扱うもので、補完候補の表示・選択・確定をすることができます。
この設定が前回の駅伝記事↓で紹介されていたので、
筆者もこちらに感化されて、copilot.luaからcopilot-langugage-serverを用いた補完に切り替えてみようと思いたち、設定してみました。
lsp用の設定は自分ですべて書くこともできますが、nvim-lspconfigの力を借りるのが簡単でしょう。このへんは以下の記事に書いたとおりです。
こちらのPRでcopilot-language-serverの基本設定とサインイン用コマンドが定義されています。
筆者のlspの設定はこうなっています。詳しくは前掲の記事を見てください。
(config)/
├ init.lua # nvim-lspconfigの読み込み ①
└ lua/
│ └ lsp/
│ └ init.lua # copilot-language-serverの有効化 ②
└ after/
└ lsp/
└ copilot.lua # 今回のcopilot-language-serverの設定 ③
まず大本のinit.luaではnvim-lspconfigを読み込みます。筆者はmini.depsを使用しているので、こうなります。
add('https://github.com/neovim/nvim-lspconfig')
続いて、lua/lsp/init.lua
のほうではcopilot-language-serverを有効化します。名前はcopilot
です。
vim.lsp.enable('copilot')
設定本体はafter/lsp/copilot.lua
です。
return {
root_dir = function(bufnr, callback)
-- 特定の名前を持つファイルでは起動しないようにする
local fname = vim.fs.basename(vim.api.nvim_buf_get_name(bufnr))
local disable_patterns = { 'env', 'conf', 'local', 'private' }
local is_disabled = vim.iter(disable_patterns):any(function(pattern)
return string.match(fname, pattern)
end)
if is_disabled then
return
end
-- git管理下でのみ起動する
-- lspconfigで定義されているroot_markersが`{ '.git' }`なのを踏襲
local root_dir = vim.fs.root(bufnr, { '.git' })
if root_dir then
return callback(root_dir)
end
end,
on_init = function()
-- サジェストのハイライト
-- CommentやMoreMsgのハイライトを拝借しつつアンダーラインをつける
local hlc = vim.api.nvim_get_hl(0, { name = 'Comment' })
vim.api.nvim_set_hl(0, 'ComplHint', vim.tbl_extend('force', hlc, { underline = true }))
local hlm = vim.api.nvim_get_hl(0, { name = 'MoreMsg' })
vim.api.nvim_set_hl(0, 'ComplHintMore', vim.tbl_extend('force', hlm, { underline = true }))
-- キーマップの設定 アタッチされたバッファでのみ有効にする
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local bufnr = args.buf
-- インライン補完を有効に
vim.lsp.inline_completion.enable(true, { bufnr = bufnr })
-- <c-e>で確定
vim.keymap.set('i', '<c-e>', function()
vim.lsp.inline_completion.get()
-- 補完ウィンドウが開きっぱなしになるのを防止
if vim.fn.pumvisible() == 1 then
return '<c-e>'
end
end, { silent = true, expr = true, buffer = bufnr })
-- <c-f>/<c-b>で補完候補を選択
vim.keymap.set('i', '<c-f>', function()
vim.lsp.inline_completion.select()
end, { silent = true, buffer = bufnr })
vim.keymap.set('i', '<c-b>', function()
vim.lsp.inline_completion.select({ count = -1 * vim.v.count1 })
end, { silent = true, buffer = bufnr })
end,
})
end,
}
やっていることはコメントしたとおりです。
除外対象のファイルや、マッピングするキーはお好みで設定してください。
ひとつ注意するポイントがあります。lspconfigに入っているコードではon_attach
を使って認証コマンドを定義しているため、ユーザー側でon_attach
に処理を加えてしまうとこれらが上書きされて使えなくなってしまう点です。
したがって、上記の設定例ではon_init
にautocmd LspAttachを記述しています。これを書く位置はcopilot-language-server自体がアタッチされる前ならどこでも(lua/lsp/init.lua
など)良いのですが、copilotの設定とまとめたほうがあとから見やすいと考え、今回はon_init
に含めました。
これでcopilot.luaがなくてもGitHub Copilotによる補完サジェストを使えるようになりました。
筆者の環境では設定当初は補完の表示が若干遅い印象でしたが、サインインをやり直したところ、キビキビ動くようになりました。
textDocument/inlineCompletion
はcopilotに限らず他のソースでも利用できるようになっているみたいなので、別のLLM補完を使いたくなったときにも設定を流用できそうです。インターフェースが決まっていると提供する側もやりやすいと思うので、今後は個別のプラグインを作るよりもこの方法で呼び出すほうが主流になっていくのではないでしょうか。
Vim駅伝はこの記事のように「Vim駅伝の記事を読んで自分もやってみた」系の投稿も大歓迎です。詳しくはこちらのページをご覧ください。
Discussion