VimからNeovimへの移行とプラグインの棚卸し

2019年ごろにVim/Neovimで開発できる環境を整えてから3年経過しました。

Neovim関連のプラグインが充実しているようで、最近はGitHub copilot XのようなGPT4ベースのツールもNeovimに対応しています。AstroNvimとか、もはやVSCodeと見間違いそうなカッコいい設定ツールも出てきました。

VimとNeovim両方で動くように.vimrcを設定していましたが、そろそろNeovimへ寄せようと思い、プラグインなどの棚卸しを行いました。

プラグインマネージャーの変更

packer.nvimへ移行しました。

READMEを参考に設定後、nvimで下記コマンドを実行することで、設定ファイルに記載したプラグインがインストールされます。

1:PackerUpdate

LSP設定用のプラグイン

nvim-lspconfigに移行しました。goplsなどのLanguage Server Protocolを設定することで、コーディング時にVSCodeのような静的解析が可能になります。

vim-lspはLSPの設定を自動化してくれるので、使い勝手はnvim-lspconfigより良かったかもしれません。nvim-lspconfigを採用した理由は、Neovimネイティブの機能であるNvim LSP client用のコンフィグだからです。

関数や変数の定義ジャンプなどの設定はREADMEに書かれていた内容をほぼそのまま利用しました。定義元へジャンプしたいときはgd、参照先へ飛びたいときはgrでジャンプできます。omnifuncによる補完はデフォルトのキーバインド<C-x><C-o>だと使いにくかったので、<C-n>で補完されるようにしました(「補完用のプラグイン」の項を参照)。

 1require'lspconfig'.gopls.setup{}
 2require'lspconfig'.golangci_lint_ls.setup{}
 3
 4-- Global mappings.
 5-- See `:help vim.diagnostic.*` for documentation on any of the below functions
 6vim.keymap.set('n', '<space>e', vim.diagnostic.open_float)
 7vim.keymap.set('n', '[d', vim.diagnostic.goto_prev)
 8vim.keymap.set('n', ']d', vim.diagnostic.goto_next)
 9vim.keymap.set('n', '<space>q', vim.diagnostic.setloclist)
10
11-- Use LspAttach autocommand to only map the following keys
12-- after the language server attaches to the current buffer
13vim.api.nvim_create_autocmd('LspAttach', {
14  group = vim.api.nvim_create_augroup('UserLspConfig', {}),
15  callback = function(ev)
16    -- Enable completion triggered by <c-x><c-o>
17    vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc'
18
19    -- Buffer local mappings.
20    -- See `:help vim.lsp.*` for documentation on any of the below functions
21    local opts = { buffer = ev.buf }
22    vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
23    vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
24    vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
25    vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
26    vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts)
27    vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, opts)
28    vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, opts)
29    vim.keymap.set('n', '<space>wl', function()
30      print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
31    end, opts)
32    vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, opts)
33    vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, opts)
34    vim.keymap.set('n', '<space>ca', vim.lsp.buf.code_action, opts)
35    vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
36    vim.keymap.set('n', '<space>f', function()
37      vim.lsp.buf.format { async = true }
38    end, opts)
39  end,
40})

フォーマッターとリンター用のプラグイン

aleからnull-ls.nvimに移行しました。非常に動作が軽くなった気がします。

nvim-lspconfigでもLSPに対応しているリンターを設定できるみたいですが、LSPに対応していないリンターやフォーマッター用に使います。

 1local null_ls = require("null-ls")
 2
 3-- ------------------------------
 4-- Autoformat settings.
 5-- https://github.com/jose-elias-alvarez/null-ls.nvim/wiki/Formatting-on-save
 6-- https://github.com/jose-elias-alvarez/null-ls.nvim/wiki/Avoiding-LSP-formatting-conflicts
 7-- ------------------------------
 8local augroup = vim.api.nvim_create_augroup("LspFormatting", {})
 9local lsp_formatting = function(bufnr)
10    vim.lsp.buf.format({
11        filter = function(client)
12            return client.name == "null-ls"
13        end,
14        bufnr = bufnr,
15    })
16end
17
18-- ------------------------------
19-- null_ls setup.
20-- ------------------------------
21null_ls.setup({
22    sources = {
23        null_ls.builtins.formatting.shfmt,
24        null_ls.builtins.formatting.stylua,
25        null_ls.builtins.diagnostics.eslint,
26        -- null_ls.builtins.completion.spell,
27        -- null_ls.builtins.diagnostics.golangci_lint,
28        null_ls.builtins.formatting.goimports,
29        null_ls.builtins.diagnostics.markdownlint_cli2,
30        null_ls.builtins.formatting.markdownlint,
31    },
32    on_attach = function(client, bufnr)
33        if client.supports_method("textDocument/formatting") then
34            vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
35            vim.api.nvim_create_autocmd("BufWritePre", {
36                group = augroup,
37                buffer = bufnr,
38                callback = function()
39                  lsp_formatting(bufnr)
40                end,
41            })
42        end
43    end,
44})

補完用のプラグイン

変数や関数、メソッドの入力時に自動で補完するためのプラグインnvim-cmpに移行しました。以前はasyncomplete.vimを利用していました(vim-lspを開発したエンジニアが公開しているプラグインです)。

nvim-cmpの利用に際し下記の依存ツールが必要だったので、合わせて導入しました。

  • vim-vsnip: スニペット用プラグイン
  • cmp-nvim-lsp: nvim-cmpでLSPを利用できるようにするためのプラグイン

以下、luaの設定内容。 omnifunkを<C-n><C-p>で実行できるようにしました。それ以外はREADMEに記載されている内容と同じです。

 1local cmp = require'cmp'
 2
 3cmp.setup({
 4  snippet = {
 5    -- REQUIRED - you must specify a snippet engine
 6    expand = function(args)
 7      vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.
 8    end,
 9  },
10  window = {
11    -- completion = cmp.config.window.bordered(),
12    -- documentation = cmp.config.window.bordered(),
13  },
14  mapping = cmp.mapping.preset.insert({
15    ["<C-p>"] = cmp.mapping.select_prev_item(),
16    ["<C-n>"] = cmp.mapping.select_next_item(),
17    ['<CR>'] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
18  }),
19  sources = cmp.config.sources({
20    { name = 'nvim_lsp' },
21  }, {
22    { name = 'buffer' },
23  })
24})

その他

下記のプラグインは引き続き利用することにしました。

  • lambdalisue/fern.vim: ファイルマネージャー
  • Morhetz/gruvbox: カラースキーマ
  • nathanaelkane/vim-indent-guides: インデントを見やすくする
  • airblade/vim-gitgutter: git diffがわかるようにする
  • vim-airline/vim-airline: 見た目を整える
  • vim-airline/vim-airline-themes: 見た目を整える
  • sebdah/vim-delve: Goのデバッガ支援

まとめ

nvim-dapなど気になるプラグインはまだあるのですが、取り急ぎ業務でも問題なく使える状態にアップデートしました。

今はGoのコーディングとMarkdownに特化した設定ですが、少しずつ他の言語の開発も行えるように整えていきたいです。

Neovimの設定置き場