つうさにメモブログ

つうさにがメモをブログとして書いていくところ

sky-color-clockと仲良くする

Emacsのモードラインにいい感じの時計をいい感じに置いてみる

sky-color-clockパッケージ

qiita.com

sky-color-clock.elを知っていますか?時刻によって背景の色が変わる、Emacsのモードライン上に置く用の時計を実現するEmacs lispパッケージです。視覚的に時の流れを感じようという目的から、記事を読めば分かる通り、その時刻の空の色を緯度から計算していたり、月齢を計算して月の絵文字を表示したり、OpenWeatherMapAPIを利用して天気を考慮して表示したり...などさまざまな工夫が込められており、めっちゃすごいパッケージです。

ですが、sky-color-clock.elはEmacs lispのライブラリのようなパッケージです。昔この記事を初めて読んだとき私はEmacs lispに慣れていなかったので、面白そうだなと読むだけで終わってしまいました。

さて、時は経って私のEmacs lisp力もまあまあなものになってsky-color-clockを試してみました。そしたら、いろいろといい感じにする方法がわかったので、それを記事にしたいと思います。

ということで、この記事ではsky-color-clockをいい感じに使う方法について書きます。

モードラインのどこにおくか

先の作者の解説記事ではsky-color-clockを試す方法として、sky-color-clock-initializeなどを済ました上で、以下のようにすることが挙げられています。

;; デフォルトの mode-line-format の先頭に sky-color-clock を追加
(push '(:eval (sky-color-clock)) (default-value 'mode-line-format))

こうすると sky-color-clock がモードラインの左端に現れます。

f:id:tsuu_mmj:20191107001733p:plain
左端にsky-color-clock

これはお手軽なのですが、私はデフォルトのモードラインが結構好きなので、急に左端に置かれてしまうのはあまり好ましくありません。sky-color-clockはモードラインの右のほう、というか右寄せして表示させたいです。

モードラインで右寄せする方法については、zk-phiさん(sky-color-clockの作者)のinit.elが非常に参考になります。また、これについて私は以前ブログの記事にしました。これを参考に右寄せします。

右寄せするために以下のような関数を定義します。

(defun sky-color-clock--form ()
  (let* ((sky-color-clock-str
          (propertize (sky-color-clock) 'help-echo (format-time-string "Sky color clock\n%F (%a)")))
         (mode-line-right-margin
          (propertize " " 'display `(space :align-to (- right-fringe ,(length sky-color-clock-str))))))
    (concat mode-line-right-margin sky-color-clock-str)))

sky-color-clock--form関数は「『right-fringeまでからsky-color-clockの長さ分だけ引いたスペース』と『sky-color-clock』をくっつけた(concatした)文字列」を返します。これを「モードラインの一番右に表示する文字列」とすれば、sky-color-clockを右寄せで表示できますね。せっかくなので、sky-color-clockにマウスオーバーするとツールチップで今日の日付を出してくれる機能を付けました(’help-echoの部分)。

定義した関数sky-color-clock--formmode-line-format変数の中に入れます。ところで、Emacsmode-line-formatはデフォルトでリストであり、一番後ろにmode-line-end-spacesという変数がセットされています。これは端末でEmacsを開くときに表示される、右端まで伸びてる------に使われる変数です。今回はこの変数の中身を単純に置き換えることで、右寄せを実現します。

(setq mode-line-end-spaces '(:eval (sky-color-clock--form)))

こうすることで、(scroll-bar-modeがdisableのとき)ちょうど右端にsky-color-clockが現れます。

f:id:tsuu_mmj:20191107003655p:plain
右端にsky-color-clock

f:id:tsuu_mmj:20191107004135p:plain
マウスを乗せて日付表示(マウス写ってないけど)

右端に表示できていい感じですね。scroll-bar-modeがenableでもdisableでもいい感じにしたい場合は、先ほど挙げた私の過去の記事が参考になると思います。

時計らしく時刻だけ表示

sky-color-clockはデフォルトで「月の絵文字」と「その日の日にち」、「時刻」を並べて表示します。「その日の日にち」の表示がいらないなら以下のようにします。

(setq sky-color-clock-format "%H:%M")

f:id:tsuu_mmj:20191107004756p:plain
絵文字と時刻のみ

時計として使うために1分ごとに再描画

ウルトラスーパー素敵なsky-color-clockパッケージですが、私にとって少しアレだと思ったところがありました。それはEmacsをほったらかしにしておくと、時刻の表示が更新されず、時計として不正確になることです。

Emacsのモードラインは必要なときだけ再描画されます。例えば、表示してるバッファ名が変わったとき、カーソルの置かれている行数が変わったときなどです。これはつまり、逆に言えばEmacsをほったらかすと、モードラインは全然再描画されないということです。sky-color-clockはEmacsでの作業中にふと目をやる状況を想定しているので、正確に時刻を表示する必要はないのかもしれません。ですが、私は時計である以上、時刻が変わったらsky-color-clockの表示時刻も変わってほしいと思いました。

いろんなやり方があると思いますが、標準のtime.elのdisplay-time-modeを参考にしたらなんとかいい感じにできました。

(defvar sky-color-clock--timer (run-at-time t 60 #'sky-color-clock-handler))
(defun sky-color-clock-handler ()
  (force-mode-line-update 'all)
  (let* ((current (current-time))
         (timer sky-color-clock--timer))
    (timer-set-time timer (timer-next-integral-multiple-of-time current 60) 60)))

run-at-timeでタイマーを動かします。このタイマーは60秒ごとにsky-color-clock-handler関数を呼び出してforce-mode-line-updateします。

60秒ごとに呼び出されても、それがちょうど0秒のときでないと意味がないので、timer-set-timeを使ってタイマーを調整しsky-color-clock-handler(の中のforce-mode-line-update)がちょうど0秒の時に呼び出されるようにしました。

これでEmacsを放置してても、時刻の分が切り替わるときにsky-color-clockは再描画されて、ちゃんと時計の機能を果たすようになりました。

終わり

この記事を書こうと思ったモチベの9割くらいは、1分ごとに再描画する方法について書きたかったことでした。

あと自分のプルリクがマージされて嬉しかったので書いたのもあります。

やっぱりEmacsはいろいろ拡張できていいですね。

補足

GUIEmacsでカラー絵文字が使えていて不思議に思った人もいたかもしれません。 私はmacOSMac port1版のEmacsを使っています。Mac port版のEmacsではカラー絵文字が表示できます。

また、(今後リリースが予定されている)Emacs 27からはGNU/Linuxでは--with-cairo付きのビルド、macOSではデフォルトでカラー絵文字が使えるようになります。 これについては、--with-cairoしてみた記事を参考にしてみてください。


  1. macOS用パッケージマネージャのMacPortsとは一切関係なく、ただMacへのport(移植)と言う意味です。