emacs のカーソルの色が勝手に変わる

最近,emacs を使っているといつの間にかカーソルのデフォルト色が skk が on になっているときの色(正確には skk-cursor-hiragana--color)になっていた,ということが何度もあったので原因を調べてました。skk を off にしてもカーソルの色が on のときと同じになるのでわかりにくいのです。

結論としてはどうするのが正しいかよくわからないけど,とりあえずこうしたらごまかせてる気がする。

(setq anything-save-configuration-functions
      '(set-window-configuration . current-window-configuration))

あれこれ

変化しているのは frame-cursor-color ですが,どういう理由なのか見当がつかないので,とりあえず色が変わるタイミングがいつなのかを知るために,定期的にカーソル色をチェックして変わっていたら知らせるようにしてみました。

具体的にはこんな感じです。毎秒各フレームのカーソル色を調べて,black でなくなっていたらデバッガを起動する。デバッガを起動するのは確実に気付かせるための方法ですぐ思いついたのがそれだっただけで,深い意味はありません。

(run-with-timer 1 1 (lambda ()
                      (dolist (f (frame-list))
                        (unless (string= (frame-cursor-color f) "black")
                          (debug "frame cursor color has changed" f)))))

それで十日くらい様子を見ていたら,どうやら anything が終了するタイミングのようだとわかったので anything のコードを見てみました。

そうすると anything-current-frame/window-configuration で取得した frame parameter らしきものを後で元に戻しているので,このあたりだろうと推測してさらに追いかけてみると,どうやら(デフォルトでは) anything の開始時に frame parameter を取得して,終了時にそれを使って復元しているということがわかりました。さらに,復元時には modify-frame-parameter が呼ばれており,この関数には skk-cursor から require されている ccc.el で advice がかけられていて,どうもその advice が原因になっているのではないかということがわかりました。(なので,このあたりの処理を変更してやれば解消しそうだ,ということで冒頭のような設定をしたわけです)

ちなみに問題の advice は以下。

(defadvice modify-frame-parameters (after ccc-ad activate)
  (when (and (assq 'cursor-color (ad-get-arg 1))
             (null buffer-local-cursor-color))
    (set-frame-cursor-color (ad-get-arg 0)
                            (cdr (assq 'cursor-color (ad-get-arg 1)))))
;; 以下略

buffer-local-cursor-color が設定されていないならカーソルの色を変える,という処理があって,ここでカーソルの色が変わるようです。たぶんこれがうまく意図した通りに動いていない。

このコードはおそらく current buffer を表示しているウィンドウが所属しているフレームのカーソル色を変更しようとした場合にはうまく動くけれど,そうでない場合には意図しない挙動をするのではないかと思います。

かといって,直そうとするとどう直せばいいのかよくわかりません(そんなに真面目に考えてもいませんが……)。

emacs においてカーソル色はフレームの属性ですが(たぶん),バッファは別にフレームに属するものではなくて,複数のフレームで同じバッファを表示したりできます。だから「バッファのカーソル色」みたいなものを考えてそれをそれらしく動作させようとすると,ちょっと難しいのかもしれません。