lazy.nvimを使った起動時間チューニング入門! ~起動速度10倍を目指して~

2023/07/31に公開

1. はじめに

この記事では、lazy.nvimを使ったVim起動の高速化について解説します。

移行した後のdotfilesはこちらです

https://github.com/yuucu/dotfiles/pull/2/files#diff-c4b04158718ed1d36b3150e7629b3de8d77bc2da6116e2e42070d35b17e79ab4

最近、vim-plugからlazy.nvimへ移行し、全てをLuaで書き直すことでVimの起動速度が向上したのと
とても可愛い起動画面を作ることができました。

どのくらい早くなったの

整理していくうちに追加したいプラグインが増えたり、新しいプラグインに移行したので、完全な比較ではありませんが、
控えめにみても300ms程度かかっていたものが20msで起動できるように改善できました。

(比較には下記ツールを用いています。)
https://github.com/rhysd/vim-startuptime

詳細のプラグインなどは最後の結果の方に記載しようと思います。

before

  • vim-plug を用いたnvim (遅延ロードなし)
  • 総プラグイン数50
計測結果
計測コマンド
$ vim-startuptime -vimpath nvim > before.log
Measured: 10 times

Total Average: 356.781100 msec
Total Max:     526.554000 msec
Total Min:     209.534000 msec

after

  • lazy.nvim を用いて遅延ロードをしたnvim
  • 総プラグイン数56
計測結果
計測コマンド
$ vim-startuptime -vimpath nvim > after.log
Measured: 10 times

Total Average: 18.506800 msec
Total Max:     20.024000 msec
Total Min:     18.092000 msec

以前の300msでも気にならなかったものが、15倍も改善されていると、かなり嬉しくなりました。

次の章からは、lazy.nvimの特徴や高速化の実現方法について順を追って説明していきます。

2. lazy.nvim への移行

移行のきっかけ

Vimの設定を全てLuaで統一したく、そのタイミングでパッケージマネージャも一緒に見直そうと考え、lazy.nvimへの移行を決定しました。

vim-plugと比較してみて

vim-plugとlazy.nvimを比較してみると、以下の点が良いなと感じました。

pluginの依存関係が1ファイルで一目超然

参考までに、以下のようなfern (ファイルツリー) を構築するまでの設定例で説明してみます。

以下はfernをインストールしているluaスクリプトです。
最初にメインとなるプラグインを記載し、( 'lambdalisue/fern.vim' ) 2行目からはプラグインの依存関係となるものをを記載しています。

return {
  'lambdalisue/fern.vim',
  keys = {
    { "<C-n>", ":Fern . -reveal=% -drawer -toggle -width=40<CR>", desc = "toggle fern" },
  },
  dependencies = {
    { 'lambdalisue/nerdfont.vim', },
    {
      'lambdalisue/fern-renderer-nerdfont.vim',
      config = function()
        vim.g['fern#renderer'] = "nerdfont"
      end
    },
  },
}

自分のfernはファイルアイコンやディレクトリアイコンを表示するために、nerdfontを利用しており、
そのためのプラグインを2つ導入しています。

依存する/されるプラグイン側の設定も一緒に記載することができるため、
かなり書きやすいなと感じています。

vim起動時の自動インストール

設定ファイルいじると、自動でlazyが立ち上がり、プラグインのインストールが始まります!
とても便利 && UIがカッコ良いです。

パフォーマンスが計測できるUIがついており、
実は自分はここで計測してみて起動時間のチューニングをしてみたくなりました。

遅延ロードの設定が柔軟

私は、vimのhelpを日本語でみるために、vimdoc-jaのプラグインを入れています。

https://github.com/vim-jp/vimdoc-ja

もちろん常時必要なプラグインではないので、特定のキーが押された瞬間に遅延ロードするように設定をしました。

return {
  'vim-jp/vimdoc-ja',
  lazy = true,
  keys = {
    { "h", mode = "c", },
  },
}

上記の設定で、コマンドモードで h を押した瞬間にvimdoc-jaがロードされ、日本語vimdocを閲覧することができます。

ちょっとわかりにくいですが、lazyの画面でnotLoadedだったvimdoc-japanが、:h を押した瞬間にロードされています。

こちらは一例ですが、他の遅延ロードのパターンもいろいろ用意されています。

3. lazy.nvim を使ったVim起動の高速化のいろいろ

おすすめの設定として、defaultでlazy = trueを入れると良いです。

https://github.com/yuucu/dotfiles/blob/79adcdebf7bc36b7cb6e14c2bbcf157ebb12a54f/config/nvim/lua/lazyvim.lua#L18-L20

こちらの設定を入れると、基本的にプラグインはロードされなくなり、
後に述べる遅延ロード設定でのイベント発火でしかプラグインがロードされなくなります。

遅延ロード設定の紹介と実例

  1. event
  2. keys
  3. cmd
  4. ft

と4つの種類で遅延ロード設定を行うことができるため、
そちらを紹介していきます。 (組み合わせることも可能です)

1. event

このオプションは、特定のNeovimイベントが発生したときにプラグインを読み込むように設定できます。

goolord/alpha-nvim での設定

起動時にalpha-nvimというプラグインで起動画面をカスタマイズしているの、
VimEnterというイベントでロードするようにしています。

{
  'goolord/alpha-nvim',
  event = "VimEnter",
}

hrsh7th/nvim-cmp での設定

lspの補完にnvim-cmpを使っており、
こちらは入力されだしてからの補完という流れでしか使わないため、
InsertEnterというイベントでロードするようにしています。

{
  'hrsh7th/nvim-cmp',
  event = "InsertEnter",
}

2. keys

このオプションは、指定されたキーが押されたときにプラグインを読み込むように設定します。

vim-jp/vimdoc-ja での設定

先ほど紹介したvimdoc-jpですね。
コマンドラインモードでの入力は mode を使うことで記載することが出来ます。

{
  'vim-jp/vimdoc-ja',
  lazy = true,
  keys = {
    { "h", mode = "c", },
  },
}

lambdalisue/fern.vim での設定

fernでも設定しています。(ファイルツリーです)
自分は Control + n で開く設定しているので、
そちらのコマンドが入力されてから遅延ロードするように設定しました。

{
  'lambdalisue/fern.vim',
  keys = {
    { "<C-n>", ":Fern . -reveal=% -drawer -toggle -width=40<CR>", desc = "toggle fern" },
  },
}

3. cmd

このオプションは、指定されたコマンドが実行されたときにプラグインを読み込むように設定します。

williamboman/mason.nvim

お馴染みのmasonですね。
自分としては、Lspやlinter, formatterを管理する際にコマンドラインでの入力をしているので
cmdとして入力されうるものを定義しています。

return {
  'williamboman/mason.nvim',
  cmd = {
    "Mason",
    "MasonInstall",
    "MasonUninstall",
    "MasonUninstallAll",
    "MasonLog",
    "MasonUpdate",
  },
}

folke/which-key.nvim

WhichKeyです。
たまに使いたくなった時に、コマンドいれて見れるようにしてます。

{
    'folke/which-key.nvim',
    lazy = true,
    cmd = {
        "WhichKey",
    },
    opts = {},
}

4. ft

このオプションは、特定のファイルタイプが開かれたときにプラグインを読み込むように設定します。
特定の言語のみ使うプラグインなんかは設定しやすいですね。

kat0h/bufpreview.vim

markdownのpreviewで用いているものをftにてmarkdownを指定しています。

{
    'kat0h/bufpreview.vim',
    build = 'deno task prepare',
    ft = {
        "markdown",
    },
    dependencies = {
        'vim-denops/denops.vim'
    }
}

以上4つの遅延ロードする設定の紹介でした。

もちろん組み合わせての設定もできるため、複数のロードタイミングがある場合は、どちらも設定しておくと良いです。

4. まとめ

どのくらい早くなったの(再掲)

before

  • vim-plug を用いたnvim (遅延ロードなし)
  • 総プラグイン数50
before plugins (50個)
call plug#begin('~/.vim/plugged')                                        
  Plug 'vim-jp/vimdoc-ja'
  Plug 'mattn/vim-goimports'                                                
  Plug 'tyru/open-browser.vim'                                              
  Plug 'mechatroner/rainbow_csv'

  " fern
  Plug 'lambdalisue/fern.vim'
  Plug 'lambdalisue/fern-git-status.vim'

  Plug 'hashivim/vim-terraform'

  " react
  Plug 'pangloss/vim-javascript'
  Plug 'leafgarland/typescript-vim'
  Plug 'peitalin/vim-jsx-typescript'
  Plug 'maxmellon/vim-jsx-pretty'

  " UML
  Plug 'weirongxu/plantuml-previewer.vim'
  Plug 'aklt/plantuml-syntax'

  Plug 'kannokanno/previm'
  " Rust
  Plug 'rust-lang/rust.vim'
  Plug 'mattn/webapi-vim'

  " git
  Plug 'airblade/vim-gitgutter'
  Plug 'tpope/vim-fugitive'
  Plug 'tpope/vim-rhubarb'

  Plug 'hashivim/vim-terraform'

  Plug '/Users/s09104/ghq/github.com/yuucu/vimq.vim'

  Plug 'MTDL9/vim-log-highlighting'
  Plug 'tyru/open-browser-github.vim'
  Plug 'simeji/winresizer'
  Plug 'kdheepak/lazygit.nvim'

  Plug 'udalov/kotlin-vim'

  Plug 'neovim/nvim-lspconfig'
  Plug 'williamboman/mason.nvim'
  Plug 'williamboman/mason-lspconfig.nvim'
  Plug 'hrsh7th/nvim-cmp'
  Plug 'hrsh7th/cmp-nvim-lsp'
  Plug 'hrsh7th/vim-vsnip'
  Plug 'j-hui/fidget.nvim'
  Plug 'jjo/vim-cue'

  Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
  Plug 'github/copilot.vim'


  Plug 'kosayoda/nvim-lightbulb'
  Plug 'antoinemadec/FixCursorHold.nvim'
  Plug 'folke/trouble.nvim'

  Plug 'folke/lsp-colors.nvim'

  Plug 'nvim-lua/plenary.nvim'
  Plug 'nvim-telescope/telescope.nvim', { 'tag': '0.1.1' }
  Plug 'nvim-telescope/telescope-frecency.nvim'
  Plug 'kkharji/sqlite.lua'

  Plug 'sebdah/vim-delve'
  Plug 'nvim-tree/nvim-web-devicons'

  " memo
  Plug 'glidenote/memolist.vim'
  "  go install github.com/mattn/memo@latest
  Plug 'delphinus/telescope-memo.nvim'

  " theme
  Plug 'sainnhe/sonokai'
  Plug 'nordtheme/vim'
  Plug 'cocopon/iceberg.vim'
  Plug 'AlexvZyl/nordic.nvim'
  Plug 'junegunn/seoul256.vim'
  Plug 'raphamorim/lucario'
  Plug 'preservim/vim-colors-pencil'
call plug#end()
計測結果
計測コマンド
$ vim-startuptime -vimpath nvim > before.log
Measured: 10 times

Total Average: 356.781100 msec
Total Max:     526.554000 msec
Total Min:     209.534000 msec

after

  • lazy.nvim を用いて遅延ロードをしたnvim
  • 総プラグイン数56
after plugins (56個)
● LuaSnip
● alpha-nvim
● ayu-vim
● bufferline.nvim
● cmp-buffercmp-nvim-lspcmp-path
● cmp_luasnip
● copilot.lua
● copilot.vim
● friendly-snippets
● gitsigns.nvim
● lazy.nvim
● lsp-format.nvim
● lspkind.nvim
● lspsaga.nvim
● lualine.nvim
● mason-lspconfig.nvim
● mason.nvim
● mini.indentscope
● mini.splitjoin
● modes.nvim
● neodev.nvim
● noice.nvim
● nui.nvim
● null-ls.nvim
● nvim-cmp
● nvim-lspconfig
● nvim-treesitter
● nvim-treesitter-textobjects
● nvim-web-devicons
● plenary.nvim
● sqlite.lua
● telescope-frecency.nvim
● telescope-fzf-native.nvim
● telescope.nvim
● todo-comments.nvim
● trouble.nvim
● vim-fugitive
● vim-rhubarb
● vim-sleuth
○ bufpreview.vim
○ catppuccin 
○ denops.vim
○ fern-renderer-nerdfont.vim 
○ fern.vim
○ glyph-palette.vim
○ kanagawa.nvim 
○ neotest 
○ neotest-go
○ nerdfont.vim
○ nvim-dap 
○ onedark.nvim 
○ vimdoc-ja
○ which-key.nvim
○ copilot-cmp
計測結果
計測コマンド
$ vim-startuptime -vimpath nvim > after.log
Measured: 10 times

Total Average: 18.506800 msec
Total Max:     20.024000 msec
Total Min:     18.092000 msec

おわりに

この記事では、lazy.nvimを使ったVim起動の高速化について解説しました。
適切な遅延ロードの設定を行うことで、20ms という速度でvimを起動することが可能になりました。

ぜひvimの起動時間にお困りの際は、遅延ロードの設定を見直してみてください!
ここまで読んでいただきありがとうございました。

参考

  • 移行後のdotfiles

https://github.com/yuucu/dotfiles/pull/2/files#diff-c4b04158718ed1d36b3150e7629b3de8d77bc2da6116e2e42070d35b17e79ab4

  • lazy.nvim

https://github.com/folke/lazy.nvim

Discussion