つうさにメモブログ

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

Emacsのモードラインで右寄せする方法メモ

ググっても日本語の記事がなかったので

mode-line-format

Emacsのモードラインを自分の手で組み立てる人はなかなかいないと思いますが、そういう人はmode-line-format変数をいじることになります。

mode-line-formatには文字列や、文字列のリスト、または:evalとともに文字列を返す関数などを与えられます1が、これらは左詰めで表示されます。

よって、右寄せして表示することはできないように思えますが、実はできます。

右寄せで表示する方法

(let* ((leftstr
        (propertize "left" 'face '(:background "#ffa0a0")))
       (rightstr
        (propertize "right" 'face '(:background "#a0a0ff")))
       (margin
        (propertize " "
                    'display `(space :align-to (- right ,(length rightstr))))))
  (setq mode-line-format (concat leftstr margin rightstr)))

(表示した時のわかりやすさのために、leftstrrightstrの背景に色をつけています。)

上のようにして、rightstrがちょうど右寄せになるような幅を持つmarginを挟めば右寄せすることができます。

marginをどうやって決めるかというと、rightstrの幅とdisplay specificationの:align-toを使います。:align-toではウィンドウ横幅のどこまで埋めるのかを指定できます。:align-toの中で唐突に出てくる-right:align-toなどの中で使える特別な表現で、rightはテキストエリアの右端地点を表します。よって、rightからrightstrの幅を引いた地点まで:align-toするものをmarginとしてやれば、右寄せを実現できます。

f:id:tsuu_mmj:20200504003646p:plain
モードラインで右寄せ

:align-toに与えるrightはウィンドウの幅の変化に対応しているため、上のコードならC-x 3などでウィンドウの幅を変えてもしっかり右寄せは保たれます。

powerlineのようなライブラリも同様のことをして右寄せを実現しています。

ちなみにmode-line-formatバッファローカルな変数なので、上の式を評価してモードラインが変わっても同じバッファで以下を評価すれば元に戻ります。

(kill-local-variable 'mode-line-format)

【追記】

上の方法は端末上でのEmacsなら右寄せになりますが、GUIEmacsでは完全には右寄せになりません。

GUIEmacsでのfringeやスクロールバーを考慮するなら以下のようになります。

(let* ((leftstr
        (propertize "left" 'face '(:background "#ffa0a0")))
       (rightstr
        (propertize "right" 'face '(:background "#a0a0ff")))
       (margin
        (propertize " "
                    'display `(space :align-to (- (+ scroll-bar scroll-bar) ,(length rightstr))))))
  (setq mode-line-format (concat leftstr margin rightstr)))

scroll-barが2回登場していますが、一つ目はscroll-barの相対位置、二つ目はscroll-barの幅を表します。

f:id:tsuu_mmj:20200504003839p:plain
モードラインで右寄せその2

これなら、M-x scroll-bar-modeでスクロールバーの表示を切り替えたときもそれに対応して右寄せされます。

ただ、前の例もこの例も、右寄せさせたい文字列が日本語や絵文字などのマルチバイト文字だと普通右寄せはうまくいきません(そのときはlengthstring-widthにするといい感じになるかも?)。

実践的な例

上では"left"や"right"など固定された文字列をmode lineに表示しましたが、普通は変動する文字列を表示したくなると思います。

そんなときは文字列を返す関数を定義して、それを:evalを使ったリストにしてmode-line-formatに与えましょう。

(defun my/mode-line--form ()
  (let* ((leftstr
          (propertize (format-mode-line "%b") 'face '(:background "#ffa0a0")))
         (rightstr
          (propertize (format-mode-line "%l") 'face '(:background "#a0a0ff")))
         (margin
          (propertize " "
                      'display `(space :align-to (- (+ scroll-bar scroll-bar) ,(length rightstr))))))
    (concat leftstr margin rightstr)))
(setq mode-line-format '(:eval (my/mode-line--form)))

f:id:tsuu_mmj:20200504005243p:plain
モードラインで右寄せ 実践例

おわり

右寄せを使って、イケてるモードラインを作りましょう!

参考


  1. 詳しくはEmacs Lisp manualのThe Data Structure of the Mode Lineに書かれています。