Neovim0.11用のLSP設定
本記事はvim-jp slackの#neovim-pluginsチャンネルに助けられて書きました。
Neovim0.11でlspconfigを使わなくてもLSPを設定しやすくなった
Neovimの0.11がリリースされ、LSP設定の設定方法が新しくなりました。neovim/nvim-lspconfigがなくてもかなり簡単に設定できるようになっています。
先日公開した本では、neovim/nvim-lspconfigを使わずに設定しています。
このようなファイル構成で、
(config)/
├ init.lua
└ lua/
└ lsp/
├ init.lua # 大元のinit.luaから呼び出すファイル
│ # ここから他のlspの設定を読み込む
├ lua_ls.lua # lua_ls単体の設定
└ foo.lua # その他lspの設定ファイルが続く…
lsp/lua_ls.lua
にlua-language-server用の設定を書き、
return {
cmd = { 'lua-language-server' },
filetypes = { 'lua' },
settings = {
Lua = {
diagnostics = {
unusedLocalExclude = { '_*' }
}
}
}
}
lsp/init.lua
で各lsp用の設定を読み込んでvim.lsp.config()
とvim.lsp.enable()
で有効化し、
-- load lsp/lua_ls.lua
local lua_ls_opts = require('lsp.lua_ls')
vim.lsp.config('lua_ls', lua_ls_opts)
vim.lsp.enable('lua_ls')
それをinit.lua
から呼び出す構造です。
require('lsp')
この方法では使用するlspに応じて設定ファイルおよびその読み込みの記述が増えるので、以下のチャプターで一括読込する仕組みを提案しています。
実はlspの設定はrequireしなくて良い
設定がどのように反映されるかは以下のヘルプに書いてあります。
When an LSP client starts, it resolves its configuration by merging from the following (in increasing priority):
- Configuration defined for the
'*'
name.- Configuration from the result of merging all tables returned by
lsp/<name>.lua
files in 'runtimepath' for a server of namename
.- Configurations defined anywhere else.
(意訳)
LSPクライアント起動時に、以下の順に設定がマージされる:
'*'
(全体)用の設定。'runtimepath'
内のlsp/<サーバー名>.lua
ファイルによって返されるすべてのテーブル。- それ以外の場所で定義された設定。
面白いのは上記の2
で、これを使えばユーザーが手動でvim.lsp.config()
で設定を読み込む必要はありません。
init.lua
のあるパス(基本的には~/.config/nvim
)は'runtimepath'
に入っているはずなので、その直下にlsp/<name>.lua
を作りましょう。
先程の例を使うとこうなります。
(config)/
├ init.lua
├ lsp/ # 移動してきた この中の設定は名前に応じて自動で読み込まれる
│ ├ lua_ls.lua # lua_ls単体の設定
│ └ foo.lua # その他lspの設定ファイルが続く…
└ lua/
└ lsp/
└ init.lua # 大元のinit.luaから呼び出すファイル
# ここから他のlspの設定を読み込む
lua/lsp/init.lua
は名前を指定してvim.lsp.enable()
するだけになります。
- -- load lsp/lua_ls.lua
- local lua_ls_opts = require('lsp.lua_ls')
- vim.lsp.config('lua_ls', lua_ls_opts)
vim.lsp.enable('lua_ls')
とはいえlspconfigを使ったほうが楽
これでlspを動かせるのですが、lspの設定をすべて書いていくは正直めんどうです。オプションなどは調整が必要かもしれませんが、起動コマンド(cmd = { 'lua-language-server' }
)とか対象のファイルタイプ(filetypes = { 'lua' }
)とかは決まりきっていることがほとんどでしょう。
そこでlspconfigです。読み込めばリポジトリのトップが'runtimepath'
に入ります。そしてリポジトリ直下にlsp/
があり、その中に<サーバー名>.lua
のファイルが用意されています。すぐ上で説明した構造と同じです。
lsp/
がある
内部では以下のように各サーバーの設定をしてくれています。
これは自動で読み込まれるので、lspconfigをインストールしさえすれば、setup()
などは必要ありません。
たとえば筆者は(前傾の本の通り)mini.depsを使ってプラグインを読み込んでいるので、init.luaはこうなります。
+ add('neovim/nvim-lspconfig')
require('lsp')
cmd
とfiletypes
はlspconfigに任せられるので、ユーザー側の設定からこれらを除くことができます。
return {
- cmd = { 'lua-language-server' },
- filetypes = { 'lua' },
settings = {
Lua = {
diagnostics = {
unusedLocalExclude = { '_*' }
}
}
}
}
lspconfigの定義している設定で十分な場合は自前の設定ファイルを用意する必要もありません。vim.lsp.enable('サーバー名')
するだけです。
lspconfigの設定を上書きしたいときは注意が必要
ここでちょっと問題があります。以下のように設定してもおそらく反映されません。
return {
+ filetypes = { 'lua.neovim' }, -- あくまで例
settings = {
-- 略
}
}
lspの設定ファイルを読み込んでいるのはどうやらこの部分です。vim.api.nvim_get_runtime_file(('lsp/%s.lua'):format(name), true)
で'runtimepath'
内のファイルを取り出し、vim.tbl_deep_extend('force', rtp_config or {}, config)
でマージしています。
したがって、~/.config/nvim
とlspconfigのディレクトリの両方が'runtimepath'
に入っている場合、どちらが先にくるかで結果が変わります。プラグインマネージャによって変わるかもしれませんが、筆者の手元ではlspconfigのほうが後に来ていました。つまりlspconfigのほうが有効になり、ユーザーの作った設定は上書きされます。
ユーザーの設定を有効にしたい場合は追加の手順が必要になります。
afterを使う
おそらく'runtimepath'
の後ろの方に~/.config/nvim/after/
が入っているので、これを使います。
(config)/
├ init.lua
├ after/
│ └ lsp/ # afterの中に移動
│ ├ lua_ls.lua # lua_ls単体の設定
│ └ foo.lua # その他lspの設定ファイルが続く…
└ lua/
└ lsp/
└ init.lua # 変わっていない
こうするとユーザー設定の優先度をlspconfigよりも高めることができます。
vim.lsp.config()を使う
先程のドキュメントの3
を使います。
(意訳)
LSPクライアント起動時に、以下の順に設定がマージされる:
'*'
(全体)用の設定。'runtimepath'
内のlsp/<サーバー名>.lua
ファイルによって返されるすべてのテーブル。- それ以外の場所で定義された設定。
vim.lsp.config()
で明示的に設定を読み込めば有効になります。
有効化切り替えの設定
tsファイルを開いたとき、nodeプロジェクトなのかdenoプロジェクトなのかによって使用するlspを変えたいという問題があります。
root_dir
の引数の関数の実行を切り替えることで、これに対応できます。
筆者はこんな感じに設定しました。
return {
root_dir = function(bufnr, callback)
local found_dirs = vim.fs.find({
'deno.json',
'deno.jsonc',
'deps.ts',
}, {
upward = true,
path = vim.fs.dirname(vim.fs.normalize(vim.api.nvim_buf_get_name(bufnr))),
})
if #found_dirs > 0 then
return callback(vim.fs.dirname(found_dirs[1]))
end
end,
}
その他tips
vim.lsp.enable()
はサーバー名を配列で受け取れます。
local lsp_names = {
'lua_ls',
'denols',
-- 略
}
vim.lsp.enable(lsp_names)
Discussion