設定ファイルは正しいのに Neovim が固まる、という壊れ方があります。原因がリポジトリの外にあるからです。
- mason に C# の言語サーバーが 3 つ入っている。omnisharp、omnisharp-mono、
csharp-language-server。どれも
.csバッファに attach するので、ソリューションの読み込みが 3 回走り、最初のハイライトが出るまで UI スレッドが止まります。設定が要求しているのは 1 つだけ。余分に入っているだけです。 lsp.logが数 MB ある。どこかのサーバーが WARN を吐き続けていて、その 1 行ずつが編集中に同期で書かれます。- blink.cmp が InsertEnter より前にロードされている。require に約 1 秒かかるので(このマシンでの話です)、ファイルを開くたびにそのぶん待たされます。
どれも lint には出ません。リポジトリの中身は正しくて、外の状態がずれています。
検査本体は 1 か所
nvim/src/lua/my/checks/init.lua が全部持っています。各関数は{ ok, msg } のリストを返すだけで、表示にも終了コードにも触りません。
M.lsp_log_size = function()
local max_bytes = 5 * 1024 * 1024
local path = vim.lsp.get_log_path()
local stat = vim.uv.fs_stat(path)
local size = stat and stat.size or 0
return { result(size < max_bytes,
("lsp.log is %.1f MB (%s)"):format(size / 1024 / 1024, path)) }
end
同じ検査を、対話的にも CI でも走らせます。2 回書くと、片方だけ直したときに差が開きます。
:checkhealth my は今そうなっているかを見る
lua/my/health.lua は結果を vim.health.ok と .error に流すだけです。
LSP の attach 検査だけ事情が違います。バッファが開いていないと判定できないので、開いているバッファを走査して、期待を宣言してある filetype のものだけ見ます。該当が無ければ info を出して終わり。
M.expected_lsp_clients = { cs = { "omnisharp" }, lua = { "lua_ls" } }
M.lsp_client_noise = { copilot = true, ["null-ls"] = true, ["GitHub Copilot"] = true }
noise 側が要るのは、言語に紐づかないクライアントも同じバッファに attach するからです。これを除かないと、copilot が居るだけで期待と一致しなくなります。
CI は状態を作ってから見る
headless 側には開いているバッファがありません。nvim/tests/cs_single_lsp.lua
は fixture の .cs を開き、omnisharp が attach するまで最大 120 秒待ちます。
そのあと、もう 5 秒待ちます。
vim.wait(5000) -- let any unexpected second server show itself
「2 つ目が attach していない」は、その瞬間に見ても確認できません。まだ来ていないだけかもしれないからです。不在を確かめるには待つ以外にありません。
omnisharp が入っていない環境では SKIP して終わります。ワークフローはMasonInstall omnisharp を明示的に走らせるので、CI で黙って飛ぶことはありません。
終了コードと、走らせる側
各プローブは最後に cq! か qa! を呼びます。cq! が非ゼロ終了です。
nvim/tests/run.sh が nvim/tests/*.lua を順に回します。ここに引っかかりどころが 2 つ。
- GNU の
timeoutがあるかを調べてから使う。 Windows のSystem32\timeout.exeは同名の別物です。timeout 1 trueが通るかどうかで判定しています。 - パスを native 形式に直す。 Windows の nvim は MSYS 形式の
/d/...を開けないので、cygpath -mを通してから渡します。
ワークフローは ubuntu と windows で走り、nvim/** が変わったときだけ動きます。