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つのプラグイン定義から、遅延読込に関係する部分を取り出した説明用の抜粋です。このまま1ファイルへ貼るコードではありません。既存の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切り替えの小さな実行ファイルだけにしています。
速くするために、使っている機能までは捨てなかった
今回削ったプラグインはありません。デバッグは必要ですし、ブラウザでのMarkdownプレビューも残しています。起動時間だけを優先して、使っている機能を減らすつもりはありませんでした。
変えたのは、必要になる時期です。コードを開いたら検索と移動へすぐ入る。F5を押したらデバッグ環境を読む。ESCを押したら、IME切り替えの完了は待たない。最初に全部抱えていた設定を、この順で動くようにしました。
Neovimへ移してから、コードリーディングはやりやすくなりました。対象ファイルへ飛び、定義や呼び出し先を確認し、その場で軽微な修正まで終えられます。今回の起動改善だけでこの流れができたわけではありません。それでも、開いた直後の引っかかりが減り、検索から編集まで入りやすくなりました。
プラグイン数だけを見て減らす前に、起動ログを見る。使う機能は残し、使う瞬間まで待たせる。今のLazyVim設定には、この考え方が合っています。
環境全体の役割分担はAIにコードを書かせる時代に、WezTerm・WSL2・Neovimを整えた理由、LSPの実行経路はNeovimのLSPが動かない原因はプラグインではなくPATHだったへ書いています。
確認した公式情報
確認日: 2026年9月15日
- Neovim Starting:
--startuptimeの用途 - lazy.nvim Lazy Loading:キー操作やコマンドによる遅延読込
- Neovim Job control:
jobstart()による非同期ジョブ - nvim-dap:DAPクライアントの役割と基本操作

コメント