[Neovim]Coc.nvim+init.vimからBuiltin LSP+init.luaに移行しました💪
Coc.nvim+init.vimからBuiltin LSP+init.luaへ
この記事の概要
BuiltinLSP+Lspsaga.nvimでドキュメントをホバーさせているところ
2022年10月現在、NeovimにおいてモダンなIDEに近い様々なコーディング支援機能(Ex. 言語ごとの入力補完、定義ジャンプ、コード診断など)を利用するには、
- Vim/NeovimをまるごとIDE化するプラグインである
Coc.nvim
を導入する方法 - Neovimに組み込まれたLSP(Language Server Protocol)クライアント機能を用いて、補完(Completion)、コード診断(Diagnostics)、リンター/フォーマッター(Linter/Formatter)の各要素につき、個別にプラグインを導入してカスタマイズする方法
の2通りがあります。
今回は、Neovim組み込みのBuitin LSPを用いた方法で、Neovimに様々なコーディング支援機能を導入します!
NeovimとLSPのエコシステムについて
初めまして!食パン🍞と申します🌵
普段は主にフロントエンドよりのコーディングをしている者です。
私は普段のコーディングにおいて、メインエディタとしてNeovimを用いています。
Neovim/Vimは、その独特な操作方法から慣れるまで扱いが難しいですが、一度馴染むと非常に効率よくテキスト編集ができるため、日々のコーディングには欠かせません。
ところで、近年のNeovimでは、Microsoftが2016年に発表したLSP(Language Server Protocol)を活用した、コーディング支援機能に関するエコシステムの発展が著しいものとなっています。
NeovimにはデフォルトでLSPクライアント(Language Serverからの情報を受けとり、コーディング支援に活用できる機能)が組み込まれており、この機能を利用した様々なプラグインが登場しています。
数年前までは、おなじくLSPなどを活用しつつも、一つのプラグインでNeovim/Vimを丸ごとVSCodeのようにしてしまうCoc.nvimをイントールするのがメジャーでした。
しかし、上記のようなエコシステムの著しい発展によって、最近のNeovim界隈においては、NeovimのBuiltinLSPを活用した各種プラグインを導入してセッティングすることが大きなトレンドとなっています。
そこで今回は、Coc.nvim
を導入していたNeovimに、(代わりに)BuiltinLSPを活用したプラグインを複数導入するとともに、合わせてVimscirpt
で記述していた設定ファイルをLua
によって書き換えたいと思います(Neovimの設定ファイルはLua言語で記述することができます)。
OS環境など
OS:ZorinOS Core16(UbuntuベースのLinux)
NeovimはHomebrewで、brew install neovim --HEAD
でインストール
Neovim Ver.0.8.0(nightly)
BuiltinLSPを活用したプラグインの多くは、最新版のNeovimを要求することが多いので、Nightlyを導入されることをおすすめします🌃
◯init.luaと設定ファイルの置き場
Neovimにおいて、ほぼ全ての設定ファイル類はLinux/Macの場合~/.config/nvim
以下のディレクトリに配置します。
(省略)nvim/
直下のディレクトリにinit.lua
を設置すると、Neovimは起動時にinit.lua
を最初に読み込み、そこから設定ファイル群を読み込みます。
このあたりの詳細や、Vimscript
による記述をLua
に書き換える際の具体的な記述の仕方については、下記の記事が参考になると思いますのでご参照ください🌵
ディレクトリの構成とファイルの配置について
Neovimは、~/.config/nvim
直下にあるinit.lua
最初に読み込んだあと、次のディレクトリにある.lua
ファイルおよび.vim
ファイルを自動的に読み込みます。
そのため、init.lua
に全ての設定を書くこともできますし、ディレクトリ別に設定ファイルを分割して配置することもできます。
今回は、下記のようなディレクトリ構成を前提に配置しています。
├── README.md
├── after
│ └── plugin
│ ├── autopairs.rc.lua
│ ├── cmp.rc.lua
│ ├── colorizer.rc.lua
│ ├── colorscheme.rc.lua
│ ├── comment.rc.lua
│ ├── fern.rc.lua
│ ├── fidget.rc.lua
│ ├── git.rc.lua
│ ├── gitsigns.rc.lua
│ ├── indentline.rc.lua
│ ├── lualine.rc.lua
│ ├── prettier.rc.lua
│ ├── telescope.rc.lua
│ ├── treesitter.rc.lua
│ └── wintab.rc.lua
├── init.lua
├── lua
│ ├── base.lua
│ └── plugins.lua
└── plugin
├── lspconfig.lua
├── lspsaga.rc.lua
├── null-ls.rc.lua
└── packer_compiled.lua
◯プラグイン管理のためにPacker.nvimを導入
私の最終的な設定ファイル類はこちらです◎
ここから具体的に各種プラグインを導入していきます💪
まず最初に導入するのは、プラグインを管理するためのプラグイン、すなわちプラグインマネージャーです。
メジャーなプラグインマネージャーにはいくつかありますが、今回はLua言語製でinit.luaとの相性が良いPacker.nvimを利用します。
基本的な使い方はリポジトリのドキュメントに丁寧に解説されていますが、日本語で解説されたものとしては下記の記事が大変参考になります。
◯LSPのための基本プラグインを導入
Packerを用いて、次のプラグインをインストールします。
これらのプラグインは、2022年現在においてBuiltinLSPを(便利に)利用する上で事実上必須となります。
-
nvim-lspconfig
- NeovimのLSPクライアントとLanguage Serverがやり取りするための各種設定をまとめたプラグインです。LSPを用いる上では事実上必須となります
-
mason.nvim
-
mason.nvim
は、LanguageServerやLinter/FormatterをNeovim上で簡単にまとめて管理(インストール・アンインストール・導入済みのサーバーなどに関する情報の表示など)できるようにしてくれるプラグインです。nvim-lspconfig
だけでも、個別にインストールしたLanguage Serverなど(例:npm install -g typescript typescript-language-server
)を利用できますが、mason.nvim
を用いることでグラフィカルに管理することができます👍(オヌヌメ)
画像は公式リポジトリより
-
-
mason-lspconfig.nvim
-
mason.nvim
とnvim-lspconfig
を連携させて、よりセットアップを簡単にするものです。コマンドモードで:LspInstall
などを実行する際、意識すること無くmason.nvim
とnvim-lspconfig
を統合してくれます(オヌヌメ)
-
use {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
"neovim/nvim-lspconfig",
}
これら3つのプラグインをインストール(:PackerInstall
)し、mason-lspconfig.nvimのマニュアルに記載されてるDYNAMIC SERVER SETUPの項目などを読みつつlspconfig.lua
にLSPのセットアップのためのコードを記述します。
local on_attach = function(client, bufnr)
-- LSPが持つフォーマット機能を無効化する
-- →例えばtsserverはデフォルトでフォーマット機能を提供しますが、利用したくない場合はコメントアウトを解除してください
--client.server_capabilities.documentFormattingProvider = false
-- 下記ではデフォルトのキーバインドを設定しています
-- ほかのLSPプラグインを使う場合(例:Lspsaga)は必要ないこともあります
local set = vim.keymap.set
set("n", "gd", "<cmd>lua vim.lsp.buf.definition()<CR>")
set("n", "K", "<cmd>lua vim.lsp.buf.hover()<CR>")
set("n", "<C-m>", "<cmd>lua vim.lsp.buf.signature_help()<CR>")
set("n", "gy", "<cmd>lua vim.lsp.buf.type_definition()<CR>")
set("n", "rn", "<cmd>lua vim.lsp.buf.rename()<CR>")
set("n", "ma", "<cmd>lua vim.lsp.buf.code_action()<CR>")
set("n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>")
set("n", "<space>e", "<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>")
set("n", "[d", "<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>")
set("n", "]d", "<cmd>lua vim.lsp.diagnostic.goto_next()<CR>")
end
-- 補完プラグインであるcmp_nvim_lspをLSPと連携させています(後述)
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- (2022/11/1追記):cmp-nvim-lspに破壊的変更が加えられ、
-- local capabilities = require('cmp_nvim_lsp').update_capabilities(
-- vim.lsp.protocol.make_client_capabilities()
-- )
-- ⇑上記のupdate_capabilities(...)の関数は非推奨となり、代わりにdefault_capabilities()関数が採用されました。日本語情報が少ないため、念の為併記してメモしておきます。
-- この一連の記述で、mason.nvimでインストールしたLanguage Serverが自動的に個別にセットアップされ、利用可能になります
require("mason").setup()
require("mason-lspconfig").setup()
require("mason-lspconfig").setup_handlers {
function (server_name) -- default handler (optional)
require("lspconfig")[server_name].setup {
on_attach = on_attach, --keyバインドなどの設定を登録
capabilities = capabilities, --cmpを連携
}
end,
}
問題なくプラグインが設定できていれば、Neovimのコマンドモードで:Mason
と入力するとmason.nvim
が立ち上がり、LSPなどの管理画面が立ち上がります。
画像は公式リポジトリより
欲しいLanguage Server(例えばTypescript
を書く人ならtypescript-language-server
)を探してカーソルを合わせてi
を押すか、コマンドモードで:LspInstall tsserver
とすると簡単にインストールすることがきます。欲しいサーバーを入れちゃいましょう!
(なお、tsserver
を使う場合はglobalにtypescript
がインストールされている必要があるので、ご注意ください)
2022/12/04:上記は誤りでした…(TOT) mason.nvim経由でtypescript-language-server
をインストールすれば、globalにtypescript
本体を入れずとも問題なく動作します。
必要なサーバーをインストールしたあと、それぞれのLSPに対応した各種拡張子のファイルを開くと、自動的にサーバーが起動します。
また、その状態で:LspInfo
と入力すると、現在起動しているactiveなサーバーの一覧や詳細が確認できるので、新しいサーバーを導入したときは正常に動作しているか確認すると確実です。
◯補完プラグインを導入
これでLSPの設定そのものは完了しましたが、この状態でコードを入力しても補完候補の表示は行われません。サーバーから送られてきた補完候補等を取得し、入力に応じた補完機能を動作させるには、補完プラグイン(Completion Plugin)とスニペットプラグイン(Snippet Plugin)が別途必要となります。
補完プラグインやスニペットプラグインには様々な組み合わせがありますが、今回は比較的メジャーな次のプラグイン群をインストールします。
-
nvim-cmp
- Luaで作られたNeovim用の補完エンジンです。補完エンジン本体である
nvim-cmp
と、補完候補を取得するソース(source)を設定するcmp-◯◯
シリーズのプラグインから構成されます。 - cmp-nvim-lsp LSPが提案する補完候補をソースとして取得します。
- cmp-buffer バッファの内容をソースとして補完候補を取得します。
- cmp-path ファイルやディレクトリのPathをソースとして取得し、補完候補として出してくれます。あのファイル/ディレクトリどこだったかなぁ…ってときに補完してくれるやつです。
- Luaで作られたNeovim用の補完エンジンです。補完エンジン本体である
-
vim-vsnip スニペットエンジンです。時折、LSPが補完候補としてスニペットを提案してくるので入れておきます。
-
cmp-vsnip
vim-vsnip
用の補完ソースです。
-
cmp-vsnip
-
lspkind.nvim
nvim-cmp
と連携して補完欄にかわいらしいアイコンを表示することができます(オヌヌメ)
Lspkind.nvimのかわいい補完アイコン
use 'hrsh7th/nvim-cmp' --補完エンジン本体
use 'hrsh7th/cmp-nvim-lsp' --LSPを補完ソースに
use 'hrsh7th/cmp-buffer' --bufferを補完ソースに
use 'hrsh7th/cmp-path' --pathを補完ソースに
use 'hrsh7th/vim-vsnip' --スニペットエンジン
use 'hrsh7th/cmp-vsnip' --スニペットを補完ソースに
use 'onsails/lspkind.nvim' --補完欄にアイコンを表示
これらをインストールした上で、①nvim-cmp
をLSPに登録、②nvim-cmp
の設定でcmp-◯◯
ソース達を登録する必要があります。このうち①は、すでに記述しています。
local on_attach = function(client, bufnr)
-- (中略)
-- 補完プラグインであるcmp_nvim_lspをLSPと連携させています⇐ココ!
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- この一連の記述で、mason.nvimでインストールしたLanguage Serverが自動的に個別にセットアップされ、利用可能になります
require("mason").setup()
require("mason-lspconfig").setup()
require("mason-lspconfig").setup_handlers {
function (server_name) -- default handler (optional)
require("lspconfig")[server_name].setup {
on_attach = on_attach, --keyバインドなどの設定を登録
capabilities = capabilities, --cmpを連携⇐ココ!
}
end,
}
②については、別途nvim/after/plugin
のディレクトリ下にcmp.rc.lua
という設定ファイルを作成し、下記のように記述します。
-- Lspkindのrequire
local lspkind = require 'lspkind'
--補完関係の設定
local cmp = require("cmp")
cmp.setup({
snippet = {
expand = function(args)
vim.fn["vsnip#anonymous"](args.body)
end,
},
sources = {
{ name = "nvim_lsp" },--ソース類を設定
{ name = 'vsnip' }, -- For vsnip users.
{ name = "buffer" },
{ name = "path" },
},
mapping = cmp.mapping.preset.insert({
["<C-p>"] = cmp.mapping.select_prev_item(), --Ctrl+pで補完欄を一つ上に移動
["<C-n>"] = cmp.mapping.select_next_item(), --Ctrl+nで補完欄を一つ下に移動
['<C-l>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
["<C-y>"] = cmp.mapping.confirm({ select = true }),--Ctrl+yで補完を選択確定
}),
experimental = {
ghost_text = false,
},
-- Lspkind(アイコン)を設定
formatting = {
format = lspkind.cmp_format({
mode = 'symbol', -- show only symbol annotations
maxwidth = 50, -- prevent the popup from showing more than provided characters (e.g 50 will not show more than 50 characters)
ellipsis_char = '...', -- when popup menu exceed maxwidth, the truncated part would show ellipsis_char instead (must define maxwidth first)
-- The function below will be called before any actual modifications from lspkind
-- so that you can provide more controls on popup customization. (See [#30](https://github.com/onsails/lspkind-nvim/pull/30))
})
}
})
cmp.setup.cmdline('/', {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = 'buffer' } --ソース類を設定
}
})
cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = "path" }, --ソース類を設定
},
})
これらを記述した上で、プラグインをまとめてインストール(:PackerSync
)し、設定に問題がなければ無事補完が動作するようになります🎉
また、plugin/lspconfig.lua
で設定した各種キーバインドを使うと、LSPが持つ様々なコーディング支援機能を利用することができます。ここらへんの機能はCoc.nvimを使ったときと一緒ですね。
◯おまけ:Lspsaga.nvimとFidget.nvimを導入
ここまででもLSPの恩恵を十分に受けることができるのですが、冒頭で述べたとおり、最近のNeovim BuiltinLSPのエコシステムは凄まじく発展しています。
おすすめのプラグインはいろいろあるのですが、その中でも特に人気が高い、LSPの見た目をめっちゃかっこよくしてくれるプラグインを紹介しておきます(私も使ってます)。
-
Lspsaga.nvim
- LSPが標準で備えてる各種機能をかっこいいUIで扱えるようにするプラグインです。
画像は公式リポジトリより
- LSPが標準で備えてる各種機能をかっこいいUIで扱えるようにするプラグインです。
-
Fidget.nvim Neovimでバッファを開いた際にLSPの起動状況や状態をおしゃれに表示してくれます✨ 意外とLSPの状態が気になる機会があるので、入れておくとなにかと便利です。
画像は公式リポジトリより
この他にも、多くのLSP関連プラグインが存在します。
ぜひいろいろ探してみてください🌵
◯おまけ②:LinterとFormatterについて
ここまでがLSPの導入になりますが、これらとは別に、別途Linter/Formatter等をNeovim上でLSPと連携して用いるには、別途、各言語のパッケージマネージャーやmason.nvimを介してローカルにインストールしたLinter/FormtterとNeovimを結びつけるプラグインと設定が必要になります。
この点、Coc.nvimとはやや異なる点かもしれません。
このnull-ls.nvim
は、比較的簡単な設定でLinter/FormatterとNeovimを橋渡ししてくれるプラグインです。導入の詳細については、下記の記事が参考になると思います。
それではよきNeovimライフを!🌵
Discussion