つーさにブログ

つうさにのメモ用ブログ

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を挟めば右寄せすることができます2

marginはdisplayプロパティを持った文字列です。displayプロパティは画像を表示するのに使われるプロパティですが、スペース(空白)を表示するのにも使えます3:align-toプロパティを使えば指定した横幅まで空白で表示することができます。:align-toに与える値としてPixel Specification4という特別な表現を使い、右にrightstr文字列の幅だけ確保するようにスペースを表示するようにします。そしてその後ろでrightstr文字列を表示すれば、実質右寄せで表示できているというわけです。

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

Pixel Specificationのrightはウィンドウの幅の変化に応じて変わるため、上のコードはC-x 3などでウィンドウの幅を変えてもしっかり右寄せは保たれます。

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

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

(kill-local-variable 'mode-line-format)
;; もしくは M-x 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の相対位置、二つ目の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. 詳しくは Mode Line Data - GNU Emacs Lisp Reference Manual を見てください。

  2. ここでは" "という文字列をプロパタイズしてスペースを表示していますが、長さが1以上の文字列ならなんでもokです。(例えばput-image関数は"x"という文字列にdisplayプロパティを追加して画像を表示している)

  3. 詳しくは Specified Space - GNU Emacs Lisp Reference Manual を見てください。

  4. 詳しくは Pixel Specification - GNU Emacs Lisp Reference Manual を見てください。