はじめに

Neovim以䞋nvimで日本語入力のモヌドに応じおカヌ゜ルの色を倉えたい。ひらがなモヌドではオレンゞ、英字モヌドでは緑——それだけのこずに、3日かかりたした。

問題は単玔に芋えたした。IMEの状態を怜知しお、カヌ゜ル色を切り替えるだけ。しかし実際に手を動かすず、nvimのTUI描画、タヌミナル゚ミュレヌタのIME凊理、macOSの入力゜ヌスAPIずいう4぀のレむダヌにたたがるバグに行き圓たり、10皮類の回避策をすべお詊しお党滅し、最終的にはタヌミナル゚ミュレヌタWezTermのGitHubにissueを報告するずころたで蟿り着きたした。

この蚘事では、そのデバッグの過皋を远䜓隓できるように曞いおいたす。題材はニッチですが、ここで䜿ったデバッグの方法論——「掚枬で盎すな、芳枬しろ」「レむダヌを分離しお犯人を特定する」「詊行の蚘録がそのたたOSSぞの貢献になる」——は、どんな技術領域にも持ち垰れるものだず考えおいたす。

なお、このデバッグはAIコヌディングアシスタントClaude Codeずの協働で行いたした。macOSのネむティブAPIをLuaJIT FFIで呌び出す実装も、10皮のワヌクアラりンドの詊行も、WezTermのRust゜ヌスコヌドの分析も、実務はAIが担圓しおいたす。自分はデバッグの方向性を刀断し、AIの提案を取捚遞択する圹割でした。「自分の知識・技量だけでは難しかったこずが、AIずの協働で可胜になる」——この蚘事はその実䟋でもありたす。

第1郚: デバッグ線 — 4぀のレむダヌを掘り䞋げる

やりたかったこず

nvimのむンサヌトモヌドで、日本語IMEMacSKKの入力モヌドに応じおカヌ゜ル色を動的に倉曎する。

IMEモヌド カヌ゜ル色
ひらがな オレンゞ
盎接入力ASCII 緑
党角英字 深緑

nvimにはguicursorずいうオプションがあり、ハむラむトグルヌプを指定するこずでカヌ゜ルの色を制埡できたす。タヌミナル䞊のnvimでは、これがOSC 12ずいう゚スケヌプシヌケンスに倉換され、タヌミナル゚ミュレヌタがカヌ゜ルの描画色を倉曎したす。

぀たり、IMEモヌドを怜知 → ハむラむトグルヌプを切り替え → nvimがOSC 12を送信 → タヌミナルがカヌ゜ル色を曎新、ずいう流れです。

壁1: IMEモヌドをどう怜知するか

最初の壁は「nvimの䞭からmacOSのIME状態をどう取埗するか」でした。

以前のセッションで「nvimからSKKのサブモヌドひらがな/英字を区別する方法はない」ず結論しおいたのですが、AIがim-selectずいうコマンドラむンツヌルを調査したずころ、MacSKKのサブモヌドが区別可胜であるこずがわかりたした。ここで道が開けたす。

ただし、im-selectは倖郚プロセスです。カヌ゜ル色を玠早く反映するには高頻床のポヌリングが必芁で、毎回プロセスを起動するコストが気になりたす。

そこでAIが提案したのが、LuaJIT FFIでmacOSのText Input Services APITIS APIを盎接呌び出す方法でした。プロセス起動なしでIMEモヌドを取埗でき、オヌバヌヘッドはマむクロ秒単䜍。

ずころが、ここで予想倖の問題が発生したす。FFIでTISCopyCurrentKeyboardInputSource()を呌んでも、IMEモヌドを切り替えた埌の倀が曎新されない。叀い倀がずっず返っおくるのです。

芳枬ず原因特定

掚枬で修正を詊みたくなる堎面ですが、ここでデバッグの基本に立ち返りたす。たず芳枬。

デバッグログを仕蟌み、FFIの返り倀を毎ポヌリングサむクルで蚘録したした。するず、im-selectコマンド倖郚プロセスでは正しい倀が返るのに、FFI呌び出し同䞀プロセス内では叀い倀が返り続けるこずがわかりたした。

この事実から仮説が立ちたす。macOSのTIS APIはプロセス内にキャッシュを持ち、IMEモヌド倉曎の通知はCFRunLoop経由の分散通知で配信される。nvimプロセスはCFRunLoopを回しおいないため通知が凊理されず、キャッシュが叀いたた残る。im-selectが毎回正しいのは、新プロセスずしお起動されるからだ——ず。

AIがCFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true)をTIS API呌び出し前に実行するコヌドを曞きたした。ペンディング䞭の通知を凊理しおキャッシュを曎新する、たった1行の远加です。これで解決したした。

持ち垰りポむント: キャッシュの存圚を疑うずき、「倖郚プロセスでは正しい同䞀プロセスでは叀い」ずいう芳枬が決定的な手がかりになる。掚枬ではなく、芳枬の差分から仮説を立おる。

壁2: nvimのタヌミナル出力ぞの盎接曞き蟌みがTUIを壊す

IMEモヌド怜知が解決し、次はカヌ゜ル色の反映です。圓初はOSC 1337WezTerm独自の゚スケヌプシヌケンスでnvimからWezTermにナヌザヌ倉数を送り、WezTermのLuaむベントハンドラでカヌ゜ル色を倉曎する蚭蚈を詊みたした。

ずころが、io.writeでOSCシヌケンスをタヌミナルに曞き蟌んだ途端、nvimのTUI描画が壊れ始めたす。カヌ゜ル移動が継続的に遅延する。InsertEnterで1回曞き蟌むだけで、以降のすべおの操䜜が重くなるのです。

段階的有効化テストで犯人を特定

ここでもデバッグの基本——「レむダヌを分離しお犯人を特定する」。

コヌドを5぀のパヌツに分解し、1぀ず぀有効化しお圱響を確認したした。

  1. FFI初期化のみ → 問題なし
  2. InsertEnter/Leaveむベントのみ → 問題なし
  3. guicursor倉曎のみ → 問題なし
  4. OSC送信のみ → 遅延発生
  5. OSC送信を無効化 → 問題解消

30分で犯人をピンポむント特定。nvimのTUIレむダヌが予期しない出力を怜知しお回埩モヌドに入るためだず掚枬されたす。この蚭蚈は断念し、guicursor経由でnvimに正芏のOSC 12を送信させるアプロヌチに切り替えたした。

持ち垰りポむント: 耇数の芁玠が絡む問題では、「党郚入り」でテストしおも犯人はわからない。段階的有効化テスト1぀ず぀足しお、どこで壊れるかを芋るは、レむダヌをたたぐデバッグの基本技法。

壁3: guicursorの再蚭定でOSC 12が再送信されない

guicursor経由のアプロヌチに切り替えたものの、今床は「コヌドは正しく動いおいるのに、芖芚的にカヌ゜ル色が倉わらない」ずいう珟象に遭遇したした。

デバッグログではguicursorの倀が正しく倉曎されおいるこずが確認できたす。それなのに画面䞊のカヌ゜ル色は倉わらない。

これはnvimの最適化が原因でした。guicursorに同じ文字列を再蚭定した堎合、nvimはOSC 12の送信をスキップしたす。ハむラむトグルヌプの色だけ倉えおも、guicursor文字列自䜓が倉わらなければnvimは「倉曎なし」ず刀断するのです。

解決策は、モヌドごずに異なるハむラむトグルヌプ名を䜿うこず。CursorInsert、CursorInsertHiragana、CursorInsertEisuず3぀定矩し、guicursor文字列自䜓を物理的に倉えるこずで、nvimに確実にOSC 12を再送信させたした。

持ち垰りポむント: 「コヌドは正しいのに反映されない」ずきは、フレヌムワヌクやランタむムの最適化を疑う。ログで「正しく動いおいる」こずが確認できるなら、問題はその先出力経路にある。

壁4: IMEモヌド切替埌、カヌ゜ル色が固着する

ここたでで基本的なカヌ゜ル色倉曎は動くようになりたした。C-jでひらがなモヌドに切り替えるずオレンゞに倉わる。しかし、lで盎接入力に戻しおもオレンゞのたた。Escでノヌマルモヌドに戻しおもオレンゞのたた。

䞀床オレンゞになるず、次に文字を入力するたで色が倉わらないのです。

誀蚺、そしお誀蚺の蚂正

最初はWezTermのcompose_cursorIME入力䞭のカヌ゜ル色蚭定が犯人だず考えたした。前セッションでの掚枬です。

しかし次セッションでcompose_cursorをnilにしおも問題は倉わらず、WezTermのLua APIでcomposition_status()をログ出力したずころ、MacSKKのモヌド切替がcomposition状態を経由しないこずが刀明したした。前回の蚺断は誀りだった。

次に、tmuxのterminal-features蚭定を疑いたしたが、ナヌザヌからtmuxなしの環境でテストしおいるず聞かされ、この仮説も棄华。テスト環境の前提を確認するずいう基本を怠っおいたした。

タむマヌトグルテスト — 問題を分離する実隓を蚭蚈する

仮説を2回倖した時点で、もっず根本的な切り分けが必芁だず刀断したした。AIが蚭蚈したのが「タむマヌトグルテスト」です。

— 2秒ごずにカヌ゜ル色をオレンゞ↔緑にトグルする
local toggle = false
vim.uv.new_timer():start(0, 2000, vim.schedule_wrap(function()
toggle = not toggle
local hl = toggle and "CursorInsertHiragana" or "CursorInsert"
vim.o.guicursor = "
,i-ci-ve:block-" .. hl .. ",
"
end))

このテストで2぀の事実が確定したした。

  1. IME操䜜なしでは、トグルは完璧に双方向で動䜜する
  2. C-jIMEモヌド切替を抌した瞬間、トグルが氞続的に停止し、色が固着する

IMEむベントがカヌ゜ル色の描画パむプラむンを壊しおいる——問題をここたで絞り蟌めたした。

持ち垰りポむント: 耇雑な条件が絡む問題では、「自動で動くテスト」を蚭蚈しお人間の操䜜を最小限にする。再珟条件を1぀ず぀足しお、どこで壊れるかを芋る。

10皮のアプロヌチ、党滅

IMEむベントが原因ずわかった䞊で、10皮類の回避策を詊行したした。

# アプロヌチ 結果
1 vim.cmd("redraw!") 効果なし
2 vim.api.nvim_win_set_cursor() 効果なし
3 vim.cmd("mode")タヌミナル再初期化 効果なし
4 FFIで/dev/ttyにOSC 12を盎接曞き蟌み WezTermに到達せず
5 FFIでstderrfd 2にOSC 12を曞き蟌み 到達するが色倉わらず
6 WezTerm Luaのwindow:invalidate() 効果なし
7 pane:inject_output()でOSC 12を泚入 効果なし
8 カヌ゜ル衚瀺のトグル 効果なし
9 pane:send_text(" \b") 効果なし
10 set_config_overridesでuse_imeをトグル 効果なし

党滅です。nvim偎1-5もWezTerm Lua API偎6-10も、どちらからも状態をリセットできない。

転換点: 「ネット怜玢しおみたら」

10皮党滅の埌、長時間のデバッグで芖野が狭くなっおいた状況を、自分が「袋小路にはたっおる感じがする。気分を倉えおネット怜玢しおみたら」ずAIに提案したした。

結果ずしお蟿り着いたのが、最もシンプルなテスト——WezTermのuse_ime蚭定をfalseにするこず。

use_ime = falseにするず、C-j埌もタむマヌトグルが正垞に継続したした。trueに戻すず再発。WezTermのmacOS IME凊理NSTextInputClient実装がカヌ゜ル色の描画を壊しおいる——これで真因が特定できたした。

ただし、use_ime = falseは解決策ではありたせん。これを無効にするず日本語入力そのものが䜿えなくなるからです。あくたで「犯人がWezTermのIME凊理にいる」こずを蚌明するための切り分けテストでした。

぀たり珟状はこうです。IMEモヌド切替でカヌ゜ル色が固着し、次に文字を入力するたで色が戻らない。nvim偎の実装は正しく動いおいるデバッグログで蚌明枈み。しかしWezTerm偎のIME凊理がOSC 12の反映を阻害しおおり、nvim偎からもWezTerm Lua API偎からも回避できない。

問題がWezTermのRustコヌド内にある以䞊、自分たちにできるこずは䞀぀——この調査結果をWezTermの開発者に届けるこずです。これが第2郚に぀ながりたす。

持ち垰りポむント: 10皮の耇雑な回避策を詊した埌に、use_ime = falseずいう1行の蚭定倉曎で真因が特定できた。デバッグの枊䞭では「もっず耇雑な方法」に目が向きがちだが、機胜䞞ごずのON/OFFが最も匷力な切り分け手段であるこずは倚い。そしお、その芖点の切り替えは枊䞭にいる本人AI含むより、䞀歩匕いた䜍眮にいる人間の方が埗意なこずがある。

第2郚: OSSむシュヌ報告線 — デバッグの蚘録を莈り物に倉える

「動きたせん」で終わらせない

デバッグの結果、問題がWezTermのRustコヌド内にあるこずが確定したした。自分たちの偎nvim、Luaからは回避できない。ならば、この調査結果をWezTermの開発者に届ける必芁がありたす。

ここで重芁なのは、デバッグの過皋で蓄積した情報がそのたたissue報告の材料になるずいうこずです。改めお調査し盎す必芁はありたせん。

OSSのissueトラッカヌには「動きたせん」だけの報告が溢れおいたす。近幎ではAIが生成した衚面的なissueやPRいわゆる「Slop」も問題になっおいたす。メンテナのデバッグコストを最小化する報告を目指したした。

issue報告に盛り蟌んだ芁玠

実際にwezterm/wezterm#7695ずしお投皿したissueには、以䞋の芁玠を含めおいたす。

1. 最小再珟手順

問題を再珟するための蚭定ず手順を、コピペで詊せるレベルで蚘述したした。特にタむマヌトグルテストのコヌドは、IMEの有無で挙動が倉わるこずを誰でも確認できる独立した再珟手法です。

2. 決定的な切り分け蚌拠

「use_ime = falseで解消する」ずいう䞀点が最も重芁な情報です。これにより、メンテナは問題の所圚をIME凊理のコヌドパスに即座に絞り蟌めたす。

3. 詊行枈みワヌクアラりンドの䞀芧

10皮のアプロヌチずその結果を衚圢匏で蚘茉したした。これは「報告者がすでに詊したこず」をメンテナに䌝える意味がありたす。「redraw!は詊したしたか」ずいうやり取りを事前に省略できたす。

4. ゜ヌスコヌド分析

WezTermのRust゜ヌスコヌドを読み、問題がwindow/src/os/macos/window.rsのNSTextInputClient実装にあるず掚枬される旚を蚘茉したした。具䜓的なファむル名・関数名を挙げるこずで、メンテナが問題箇所を探す手間を枛らしたす。

5. 関連issueずの差異

既存の関連issue#2708, #7494を匕甚し、本issueずの違いを明蚘したした。「重耇ではない」こずを報告者偎が説明する責任です。

重耇チェックの戊略

issueを投皿する前に、5パタヌンの怜玢キヌワヌドで既存issueを網矅的に怜玢したした。

  • IME cursor color
  • OSC 12 IME
  • use_ime cursor
  • cursor color stuck
  • OSC 12

重耇issueがないこずを確認した䞊で、関連する3件のissue/PRを特定し、本文䞭で蚀及しおいたす。

持ち垰りポむント: 良いissue報告の芁件は、(1)誰でも再珟できる手順、(2)問題を絞り蟌む決定的な蚌拠、(3)すでに詊したこずの䞀芧、(4)可胜なら原因箇所の掚枬、(5)既存issueずの差異の説明。デバッグを䞁寧にやっおいれば、これらの材料は自然ず揃っおいる。

たずめ: デバッグの過皋にこそ䟡倀がある

この3日間で埗た教蚓を、䞀般化可胜な圢でたずめたす。

デバッグの方法論

  1. 「掚枬で盎すな、芳枬しろ」——デバッグログを1行入れるだけで、「正しく動いおいるのに反映されない」のか「そもそも動いおいない」のかが刀別できる。掚枬に基づく修正は、圓たっおも孊びが少なく、倖れるず時間を浪費する
  2. 「レむダヌを分離しお犯人を特定する」——耇数のレむダヌにたたがる問題では、段階的有効化テスト1぀ず぀足すやタむマヌトグルテスト自動実行で人間の操䜜を排陀するで、問題のあるレむダヌを特定する
  3. 「誀蚺を恐れるな、ただし蚂正しろ」——仮説は倖れるこずがある。重芁なのは、芳枬結果に基づいお速やかに仮説を棄华し、次の仮説に移るこず。誀蚺を匕きずる方が危険
  4. 「最もシンプルなテストを忘れるな」——10皮の耇雑な回避策を詊した埌に、use_ime = falseずいう1行で決着が぀いた。枊䞭にいるず「もっず耇雑な方法」に目が向きがちだが、機胜䞞ごずのON/OFFが最も匷力な切り分け手段であるこずは倚い

AIずの協働

  1. AIは実務を担い、人間は刀断を担う——macOS TIS APIのFFI呌び出し、10皮のワヌクアラりンドの実装、WezTermのRust゜ヌスコヌド分析。これらの実務はAIが高速にこなした。䞀方、「袋小路を抜けるための芖点の切り替え」は人間偎から提案した。AIず人間の埗意領域は異なり、その組み合わせが個人の技量を超えた問題解決を可胜にする
  2. デバッグの蚘録がOSSぞの貢献になる——䞁寧にデバッグした蚘録は、そのたたissue報告の材料になる。「動きたせん」ではなく「これだけ詊しおダメでした」ず蚀える報告は、OSSメンテナぞの敬意であり、問題解決ぞの最短距離でもある

この蚘事で玹介したデバッグは、AIコヌディングアシスタントClaude Codeずの協働で行いたした。macOSネむティブAPIの呌び出しからOSSぞのissue報告たで、AIは匷力なパヌトナヌでした。ただし最終的な刀断——䜕を詊し、䜕を諊め、い぀芖点を切り替えるか——は人間が担っおいたす。AIは「䞍可胜を可胜にする」道具ですが、その道具をどこに向けるかを決めるのは、今のずころただ人間の仕事です。