LazyVimの起動を見直す|DAPは遅延読込、IME切替は非同期に

Neovimを開いて、まず検索やコードリーディングへ入りたい。それなのに、私のLazyVim設定では、その日はデバッグしないのにDAPまで起動時から読み込んでいました。さらに、ESCでWindows IMEを半角へ戻すためのzenhan.exe探索も、WSL2からWindows側のPATHを毎回確認する構成でした。

今回はプラグインを削らず、DAPを使う時だけ読み込み、IME切り替えを非同期にしました。開いた直後の引っかかりは減ったと感じています。起動時間の元ログは未保存で、今回は読み込むタイミングを変えた設定と使用感の記録です。

まず、体感ではなく起動ログを取った

使ったのはNeovim 0.12系の--startuptimeです。Neovimの公式ドキュメントにも、設定やプラグインのどこで時間を使っているか確認するための起動オプションとして記載されています。

startup_log=$(mktemp)
nvim --headless -i NONE --startuptime "$startup_log" +qa!

--startuptimeは指定したファイルへ追記するため、比較のたびに空の一時ファイルを作ります。上の例はWSL2などBash系のシェル向けです。各回の出力は設定と一緒に保存しておきます。

普段の画面起動やファイル読込とは条件が違うため、次に起動時間を比べる時は、変更前後の設定、試行回数、除外条件、各回の原出力をセットで残します。

起動時には使わないDAPを、F5まで待たせた

起動ログを読むと、nvim-dapとnvim-dap-virtual-textは、コードを読むだけの日には起動時に不要な読込でした。

私はC#のターミナルアプリでブレークポイントを貼り、標準入力を確認する時や、APIを開発する時にデバッグを使います。しかし、コードを読むだけの日までDAPを最初から読み込む必要はありません。

そこで、DAP本体とVirtual TextはF5などのデバッグ操作まで、DAP UIは<leader>duまで読み込みを待たせるよう変更しました。次はlua/plugins/debug.luaの既存のreturn { ... }に入っている2つのプラグイン定義から、遅延読込に関係する部分を取り出した説明用の抜粋です。既存の2つの定義へlazy、dependencies、keysを組み込み、言語別アダプターやDAP UIのoptsは残します。F5のconfig.projectは多言語DAP設定で示すlua/config/project.luaが必要です。

return {
  -- 既存のspecから遅延読込に関係する部分だけを抜粋
  {
    "mfussenegger/nvim-dap",
    lazy = true,
    dependencies = {
      "mfussenegger/nvim-dap-python",
      "leoluz/nvim-dap-go",
      { "theHamsta/nvim-dap-virtual-text", opts = {} },
    },
    keys = {
      {
        "<F5>",
        function()
          require("config.project").use_root()
          require("dap").continue()
        end,
        desc = "Debug: Start/Continue",
      },
    },
  },
  {
    "rcarriga/nvim-dap-ui",
    lazy = true,
    dependencies = { "mfussenegger/nvim-dap", "nvim-neotest/nvim-nio" },
    keys = {
      {
        "<leader>du",
        function()
          require("dapui").toggle()
        end,
        desc = "Debug: Toggle UI",
      },
    },
  },
}

lazy.nvimのLazy Loadingでは、keys、cmd、ft、eventなどを読込条件にできます。キー操作へ割り当てたプラグインは、そのキーを初めて実行した時に読み込まれます。

変更後も、F5でDAP、<leader>duでDAP UIが読み込まれることを確認しました。初回F5にかかる時間は、次に起動時間と一緒に測りたいです。

zenhan.exeは残し、IME切り替えの完了を待たせない

もう一つ見直したのがIME設定です。私はInsertモードを抜けた時、Windows IMEも半角へ戻しています。ところが、起動時にexepath("zenhan.exe")でPATH全体を探すと、WSL2からWindows側のパスまで確認します。

WSL2では、まずZENHAN_PATHを見ます。WindowsとWSL2のユーザー名が違う環境では、ここへzenhan.exeの場所を設定します。設定していない場合だけ、Windowsユーザー名またはWSL2側のユーザー名から候補を組み立てます。次のコードは候補を集める部分の抜粋です。

add_candidate(vim.env.ZENHAN_PATH)

local windows_user = vim.env.WINDOWS_USER or vim.env.USER
if windows_user and windows_user ~= "" then
  add_candidate(vim.fs.joinpath(
    "/mnt/c/Users",
    windows_user,
    ".local",
    "bin",
    "zenhan.exe"
  ))
end

さらに、以前はInsertLeaveでvim.fn.system()を呼んでいました。ESCを押すたびに編集が止まる場所へ、外部プロセスの完了待ちを置きたくはありません。zenhanは、候補から実行可能なファイルを選んだ後の変数です。

そこでjobstart()へ変更しました。

vim.api.nvim_create_autocmd("InsertLeave", {
  callback = function()
    vim.fn.jobstart({ zenhan, "0" })
  end,
})

NeovimのJob controlでは、jobstart()で外部プロセスを起動し、Neovim本体をブロックせずに処理できます。処理の完了をESC側で待たない形に変えました。

zenhan.exeなしでWindows IMEを切り替える案は採用しなかった

Neovimにはiminsertがあります。しかし、これはNeovim内部の入力方式に関する設定で、WSL2の外側にあるWindows IMEそのものを確実にオフにする機能ではありません。

そのため、zenhan.exeへの依存は残しました。完全に依存をなくすより、外部コマンドを任意設定にし、存在する時だけ非同期で呼ぶ判断です。

Cドライブのマウントが遅い点も変わりません。リポジトリとNeovim本体は引き続きWSL2のLinux側へ置き、Windows側へ触るのはIME切り替えの小さな実行ファイルだけにしています。

コードを読む、F5などのデバッグ操作、<leader>du、InsertLeave(ESCなど)
DAPはデバッグ操作、DAP UIは表示キー、IME切り替えはInsertモードを抜けた時が、それぞれの実行のきっかけです。

速くするために、使っている機能までは捨てなかった

今回削ったプラグインはありません。デバッグは必要ですし、ブラウザでのMarkdownプレビューも残しています。起動時間だけを優先して、使っている機能を減らすつもりはありませんでした。

変えたのは、必要になる時期です。コードを開いたら検索と移動へすぐ入る。F5を押したらデバッグ環境を読む。ESCを押したら、IME切り替えの完了は待たない。最初に全部抱えていた設定を、この順で動くようにしました。

Neovimへ移してから、コードリーディングはやりやすくなりました。対象ファイルへ飛び、定義や呼び出し先を確認し、その場で軽微な修正まで終えられます。開いた直後の引っかかりが減り、検索から編集まで入りやすくなりました。

プラグイン数だけを見て減らす前に、起動ログを見る。使う機能は残し、使う瞬間まで待たせる。今のLazyVim設定には、この考え方が合っています。

環境全体の役割分担はAIにコードを書かせる時代に、WezTerm・WSL2・Neovimを整えた理由、LSPの実行経路はNeovimのLSPが動かない原因はプラグインではなくPATHだったへ書いています。

確認した公式情報

確認日: 2026年9月15日

コメント

タイトルとURLをコピーしました