Neovim v0.11のネイティブ補完APIをlazy.nvim+Mason 2.0で使う
わたすけです。最近 Neovim の設定ファイルを弄っていて、ブログ記事になりそうなネタが生えたので、Vim 駅伝に参加させていただきました。前回の記事はれやかさんによる初めて Neovim をプラグインを作った話でした。
さて、僕は Neovim のプラグイン管理には lazy.nvim を使っており、プラグインとしては coc.nvim や telescope.nvim などを使っていました。
ところで、telescope.nvim には LSP との連携機能があるんですよね。 lua require('telescope.builtin').lsp_references()
とすればカーソル下のオブジェクトなどが参照されている箇所をファジーファインダーで閲覧できます。が、coc.nvim だとこの機能が使えないっぽいんですよね。どうにかすれば使えるようになるのかもしれませんが、ちょうど coc.nvim をなんとなくやめたい気分だったので、Mason を使った設定に移行しました。
今回は lazy.nvim と Mason v2.0.0(mason.nvim と mason-lspconfig.nvim)を使って LSP の環境を構築し、さらに Neovim v0.11 から追加された補完 API(vim.lsp.completion
)を利用できるようにした方法について書いていきます。正直めちゃくちゃメジャーな選択肢だと思っているのですが、インターネットにそれっぽい情報があんまりなくて大変でした。GitHub で dotfiles を見ても、v0.11 に対応した設定はそれほど多くないっぽいんですよね。しかもこの設定をした数日後に mason.nvim が v2.0.0 となり、Breaking Change が発生して大変でした。
環境
Arch Linux で Neovim v0.11.1 を使用します。lazy.nvim の設定は既に済んでいるものとします。
dotfiles は以下に置いてあります。執筆時点のコミットハッシュは 7dd1bf5
です(以下の URL はそのコミット時点でのnvim
ディレクトリ)。今回変更したのは主に lua/plugins/lsp.lua
で、lua/base.lua
もちょっとだけ変更しました。
プラグインの読み込み
前述の通り、lazy.nvim で mason.nvim と mason-lspconfig.nvim を読み込みます。mason.nvim は LSP のインストールをするために入れて、mason-lspconfig.nvim は mason.nvim によってインストールされた LSP を読み込むために入れます。
{
{
"mason-org/mason.nvim",
build = ":MasonUpdate",
cmd = { "Mason", "MasonUpdate", "MasonLog", "MasonInstall", "MasonUninstall", "MasonUninstallAll" },
config = true,
},
{
"mason-org/mason-lspconfig.nvim",
dependencies = {
{ "mason-org/mason.nvim" },
{ "neovim/nvim-lspconfig" },
},
event = { "BufReadPre", "BufNewFile" },
config = true,
keys = {
{ "<C-space>", "<cmd>lua vim.lsp.completion.get() <CR>", mode = "i" },
{ "gh", "<cmd>lua vim.lsp.buf.hover() <CR>" },
{ "gd", "<cmd>lua vim.lsp.buf.definition() <CR>" },
{ "gD", "<cmd>lua vim.lsp.buf.declaration() <CR>" },
},
},
}
以前は require("mason-lspconfig").setup_handlers
を用いて、LSP を自動で読み込むように設定していたのですが、v2.0.0 から設定を行わずとも自動で読み込んでくれるようになりました。あと v2.0.0 からプラグインが Organization に移り、williamboman/mason.nvim
だった部分がmason-org/mason.nvim
になっています。
その他に、keys セクションで Ctrl+Space で補完候補を表示する等の設定をしています。それと、遅延ロードを有効にしています。mason-lspconfig.nvim のほうはファイルを開くときまで読み込みを遅延させて、mason.nvim のほうはそれによって提供されているコマンドを入力するまで遅延させています。
一見 mason-lspconfig.nvim の依存として mason.nvim を入れているから別途 mason.nvim を入れる必要はないのでは?と思うかもしれませんが、これは:Mason
コマンドを利用するために必要です。コマンドを入力するまでは読み込む必要がないので、cmd を指定して遅延ロードを行っているわけです(mason-lspconfig.nvim が読み込まれる時、つまりファイルを開く際には、依存関係として読み込みが行われます)。
補完と自動フォーマットの設定
では、Neovim v0.11 から追加された vim.lsp.completion
を用いて補完できるようにしましょう。ついでに保存時の自動フォーマットも有効化します。"LspAttach" イベントに対して autocmd を設定します。以下はほぼ :h lsp-attach
のパクリです(というか triggerCharacters の設定をアンコメントして timeout_ms
を変えたこと以外はそのままかも)。
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("my.lsp", {}),
callback = function(args)
local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
-- 補完の設定
if client:supports_method('textDocument/completion') then
-- 文字を入力する度に補完を表示(遅くなる可能性あり)
local chars = {}; for i = 32, 126 do table.insert(chars, string.char(i)) end
client.server_capabilities.completionProvider.triggerCharacters = chars
-- 補完を有効化
vim.lsp.completion.enable(true, client.id, args.buf, {autotrigger = true})
end
-- フォーマット
if not client:supports_method('textDocument/willSaveWaitUntil')
and client:supports_method('textDocument/formatting') then
vim.api.nvim_create_autocmd('BufWritePre', {
group = vim.api.nvim_create_augroup('my.lsp', {clear=false}),
buffer = args.buf,
callback = function()
vim.lsp.buf.format({bufnr = args.buf, id = client.id, timeout_ms = 3000})
end,
})
end
end,
})
最終的なファイルの全体像はGitHubをご覧ください。
ここまで設定して、:MasonInstall
で任意の LSP をインストールすると、LSP が起動して、文字を入力すると補完候補が表示されるようになります。
Telescope との連携も動いています。画像はファイル内のシンボルを表示している様子です。
completeopt の設定
必要に応じて completeopt も変更します。僕は以下のように設定しました:
vim.opt.completeopt = {
"fuzzy",
"popup",
"menuone",
"noinsert",
}
おわりに
というわけで、lazy.nvim と Mason で LSP の環境を構築できました。移行のモチベーションだった telescope.nvim との連携もうまく動いていて嬉しいです。
設定の過程では、そもそも LSP が起動しなかったり、補完候補が表示されなかったり、遅延ロードがうまくいかなかったりでちょっと大変でした。Neovim は割と雰囲気で触っている節があったのですが、今回の設定いじりでドキュメントの見方がなんとなく分かった気がするので、今後はちゃんと読んでいこうと思います。
参考資料
- neovim 0.11 からは LSP をほぼネイティブ API だけで扱える
vim.lsp.completion
はこれで知りました
- Lsp - Neovim docs
Comments
Powered by Giscus