📝

VSCode VimからVSCode Neovimに移行した

2023/03/02に公開

VSCode Neovimとの遭遇

Vimmerの方には、VSCodeでVSCode Vimを使っている方も多いかと思います。筆者もその一人でしたが、Undoがどうにも壊れたり、vim-surround相当の機能の挙動にやや不満があったり等の理由で、完全なVimの書き心地を得るには至っていませんでした。

先日、Undoが壊れている件のissueを読んでいたところ、VSCode Neovimというエクステンションを知りました。これに移行して割と快適になったので、導入のために行ったことを紹介します。

VSCode VimとVSCode Neovimの違い

https://marketplace.visualstudio.com/items?itemName=vscodevim.vim

VSCode VimはVSCode上でVimの操作感をエミュレートするエクステンションです。基本的なVimのノーマルモードの操作を懸命にサポートしているエクステンションですが、ネイティブのVimと比べると物足りなさを感じるのは事実です。

https://marketplace.visualstudio.com/items?itemName=asvetliakov.vscode-neovim

VSCode NeovimはVSCodeとNeovimをインテグレーションするエクステンションで、裏でNeovimインスタンスをバックエンドとして起動し、エディタ上の操作をNeovimに委譲します。 これにより、Neovimの init.lua による設定、またVim/Neovimプラグインの使用をサポートします。

VSCode VimからVSCode Neovimへの移行

Neovimのインストールと設定

VSCode NeovimはNeovimそのものをバックエンドとして起動するため、Neovimをインストールしておく必要があります。といってもNeovimはビルド済みバイナリが配布されているので、Windows, macOS, Linuxいずれの環境にもすんなりとインストールできるでしょう。

VSCode Neovimの仕組み上、各種キーバインドの設定などはNeovimの設定で行います。筆者はCLI上でテキストファイルを編集するときにはVimを使用していたので、これを機にVimの設定(.vimrc)を棚卸しして、Neovimの設定(~/config/nvim/init.lua)に移行しました。[1]

例えば筆者は、VSCodeでも部分的に実装されているvim-surroundプラグインを使用していたので、これに相当するNeovimネイティブなプラグインnvim-surroundを導入したりしています。また独自のキーバインドとして、スペースをleaderキーに割り当て、 <leader>j, <leader>k を10行単位の移動に割り当てたりしています。

~/config/nvim/init.luaの設定例の抜粋
require("packer").startup(function(use)
  use "wbthomason/packer.nvim"
  ...
  use { "kylechui/nvim-surround", tag = "*" }
  ...
end)

require("nvim-surround").setup()
...
vim.g.mapleader = " "
vim.keymap.set({"n", "v"}, "<leader>j", "10j", { noremap = true })
vim.keymap.set({"n", "v"}, "<leader>k", "10k", { noremap = true })

ここでは、ひとまずVSCode Neovimを意識する必要はありません。 例えばlualine.nvimbufferline.nvimを使用したり、自動補完・LSPやtelescope.nvimを使用したりといった、CLI上でNeovimを起動したときだけ有効にしたい設定(= VSCode Neovimでは不要な設定)も自由に書いてしまって構いません。

VSCode Neovimエクステンションをインストールする

VSCode NeovimはVSCode Vimと衝突するので、VSCode Vimをアンインストールし、VSCode Neovimをインストールします。こちらも特別な手順は必要なく、Marketplaceから通常のエクステンションと同じようにインストールできます。

nvim への PATH が通っている状態なら、これだけで動作するはずです。筆者は以下の設定だけ好みで変更しています:

  • Mouse Selection Start Visual Mode
    • マウス選択でVim側のVisual Modeを開始するように
  • Use WSL
    • WSL上ではWSL上のNeovimを使用する

Neovimの設定をVSCode向けに調整する

VSCode NeovimのバックエンドとしてのNeovimでは、行数を表示する設定(vim.opt.number = true)などは不要です。また、Neovim上のタブ移動などのコマンドを、VSCodeからの起動時はVSCode側のタブの移動にするといったことが行いたいです。このためにNeovimの設定ファイルを調整します。

NeovimがVSCodeから起動されたかどうかは、 vim.g.vscode を確認することで判定できます。

~/config/nvim/init.luaの設定例の抜粋
if not vim.g.vscode then
  vim.opt.number = true
  vim.opt.shiftwidth = 2
  vim.opt.scrolloff = 6
  vim.opt.list = true
  vim.opt.listchars = { tab = ">-", trail = "-" }
  vim.opt.cursorline = true
  vim.opt.completeopt = "menuone"
  vim.opt.termguicolors = true
  vim.cmd("colorscheme duskfox")
  require("lualine").setup()
  require("bufferline").setup()
end

この例では、lualine, bufferlineといったプラグインのsetupや、表示向けの設定を、VSCodeから起動していないときだけ有効にしています。

VSCodeNotify() 関数によってVSCode側のコマンドを呼び出すことができます。

~/config/nvim/init.luaの設定例の抜粋
if vim.g.vscode then
  vim.keymap.set("n", "<leader>o", "<Cmd>call VSCodeNotify('workbench.action.quickOpen')<CR>")
  vim.keymap.set("n", "<leader>d", "<Cmd>call VSCodeNotify('workbench.action.files.save')<CR>")
  vim.keymap.set("n", "H", "<Cmd>call VSCodeNotify('workbench.action.previousEditor')<CR>")
  vim.keymap.set("n", "L", "<Cmd>call VSCodeNotify('workbench.action.nextEditor')<CR>")
else
  vim.keymap.set("n", "<leader>o", function() require("telescope.builtin").find_files { hidden = true } end)
  vim.keymap.set("n", "<leader>d", "<Cmd>bd<CR>")
  vim.keymap.set("n", "H", "<Cmd>bp<CR>")
  vim.keymap.set("n", "L", "<Cmd>bn<CR>")
end

この例では、 <leader>o をNeovimネイティブではtelescopeの find_files に、VSCode NeovimではVSCodeの workbench.action.quickOpen としてバインドしています。

感想

移行して数週間ほど使っていますが、今のところ特に問題には遭遇せず、Vimでプラグインを含めて実現していた操作感が得られて良い感じがしています。VSCodeとのインテグレーションも問題ありません。

CLI上でグラフィカルに動くプラグインは試していませんが、エクステンションの説明を見る限り使用は想定されていないようです。VSCode上であえて使いたいユースケースが思いつかないので、今のところ困ることはないです。少なくともVSCode Vimからの移行で困ることはまず無いでしょう。

VSCode Vimのsettings.jsonに書き連ねていた、長ったらしいキーバインドの設定が消せたのも嬉しいところです。

脚注
  1. NeovimはVimの設定やプラグインがそのまま動くようですが、これを機に整理することにしました ↩︎

Discussion