Emacs
emacs-lisp
EmacsDay 23

モードラインの文字エンコーディング表示をわかりやすくする


文字エンコーディング表示

ファイルを編集することを主目的とするエディタやIDEには、編集中のファイルの文字エンコーディング(および改行文字)が何であるかを表示する機能がついているものです。例えばIntelliJ IDEAの下部に以下のような表示があり、ファイルの文字エンコーディングを表現しています。

intellij.png

一方のEmacsではモードラインにその情報を表示しています。

utf-8-dos.png

え、どこ??? とみんな思ったでしょう。実は

koko.png

なんです。UUTF-8 を表しており、\ は改行文字が CRLF であることを表しています。自分はEmacsを長く使ってきたので慣れてしまいましたが、初めて見る人は「そんなのわかんねーよ」と叫びEmacsから遠ざかるでしょう。

こういったぱっと見のわかりにくさの他にも、文字エンコーディングが UTF-8UTF-16 の両方ともモードラインでは U と表示されてしまうため、バッファを見ただけでは UTF-8UTF-16 の区別が付かないという問題もあります。また仕事でたまにBOM付きのファイルを触ることがあるのですが、デフォルトのモードラインではBOMの有無がわかりません。

そこで、この記事ではモードラインの文字エンコーディング表現を変更し、文字エンコーディング、BOM、改行文字をわかりやすくする方法を紹介します。


設定方法

まず結論として、以下のコードを init.el に加えれば文字エンコーディングの表示が変わります。以下では設定の中身を解説します。


init.el

(require 'cl-lib)

;; 改行文字の文字列表現
(set 'eol-mnemonic-dos "(CRLF)")
(set 'eol-mnemonic-unix "(LF)")
(set 'eol-mnemonic-mac "(CR)")
(set 'eol-mnemonic-undecided "(?)")

;; 文字エンコーディングの文字列表現
(defun my-coding-system-name-mnemonic (coding-system)
(let* ((base (coding-system-base coding-system))
(name (symbol-name base)))
(cond ((string-prefix-p "utf-8" name) "U8")
((string-prefix-p "utf-16" name) "U16")
((string-prefix-p "utf-7" name) "U7")
((string-prefix-p "japanese-shift-jis" name) "SJIS")
((string-match "cp\\([0-9]+\\)" name) (match-string 1 name))
((string-match "japanese-iso-8bit" name) "EUC")
(t "???")
)))

(defun my-coding-system-bom-mnemonic (coding-system)
(let ((name (symbol-name coding-system)))
(cond ((string-match "be-with-signature" name) "[BE]")
((string-match "le-with-signature" name) "[LE]")
((string-match "-with-signature" name) "[BOM]")
(t ""))))

(defun my-buffer-coding-system-mnemonic ()
"Return a mnemonic for `buffer-file-coding-system'."
(let* ((code buffer-file-coding-system)
(name (my-coding-system-name-mnemonic code))
(bom (my-coding-system-bom-mnemonic code)))
(format "%s%s" name bom)))

;; `mode-line-mule-info' の文字エンコーディングの文字列表現を差し替える
(setq-default mode-line-mule-info
(cl-substitute '(:eval (my-buffer-coding-system-mnemonic))
"%z" mode-line-mule-info :test 'equal))



改行文字の文字列表現

3~7行目は改行文字に対する表示を変更しています。eol-mnemonic-* という変数が改行文字ごとに用意されているので、その設定値を変更しているだけです。


文字エンコーディングの文字列表現

9~34行目は文字エンコーディングに対して、独自の文字列表現を返す関数を定義しています。my-coding-system-name-mnemonic は文字エンコーディング自体の文字列表現、my-coding-system-bom-mnemonic がBOMの有る無しによる文字列表現を返します。

my-buffer-coding-system-mnemonic は上記2つを結合して返すだけの関数です。


mode-line-mule-info の文字エンコーディングの文字列表現を差し替える

36~40行目で文字エンコーディングの表示を、デフォルトのものから独自に定義した関数を使用するように差し替えています。

デフォルトの文字エンコーディングの表示設定は mode-line-mule-info という変数が担っています。mode-line-mule-info は以下のような4要素のリストです。

("" <インプットメソッドの文字列表現> <文字エンコーディングの文字列表現> <改行文字の文字列表現>)

3要素目の <文字エンコーディングの文字列表現> を独自のものに差し替えているのが、36~40行目のコードです。"%z":eval の意味は変数 mode-line-format のドキュメントを読んでください。


比較

以下に変更前後でモードラインがどのように変わるかを示します。

文字エンコーディング
BOM
改行文字
変更前
変更後

UTF-8
なし
CRLF

UTF-8
なし
CR

UTF-8
なし
LF

UTF-8
あり
LF

UTF-16
BE
LF

UTF-16
LE
LF

Shift JIS
-
LF

CP932
-
LF

EUC JP
-
LF


デフォルトとの機能差

デフォルト設定ではモードラインの文字エンコーディング部分をクリックすると、文字エンコーディングの説明を見たり、文字エンコーディングを変更する機能があります。しかしコードが長くなりすぎるのも嫌だったので、上記の設定ではそれを再現していません。


検証環境

WindowsのEmacs25.3でしか動作確認していないので、他の環境だと問題が出るかもしれません。

GNU Emacs 25.3.1 (i686-w64-mingw32)

of 2017-09-16

その場合はコメントなどでご指摘ください。


参考